В моем проекте я хочу разделить поток на значения определенного типа, поэтому я реализую функцию шаблона как
template <typename TElem, typename TOutputIter>
TOutputIter SplitSpace(std::istream& IS, TOutputIter result)
{
TElem elem;
while (IS >> elem)
{
*result = elem;
++result;
}
return result;
}
Я думаю, что это неудобно, так как я должен явно указать тип TElem
при звонке Например, я должен написать:
std::vector<int> v;
SplitSpace<int>(std::cin, back_inserter(v));
// I want to it to be SplitSpace(std::cin, back_inserter(v));
Я пытался получить тип значения из (шаблона) итератора и использовал std::iterator_traits
следующее:
template <typename TOutputIter>
TOutputIter SplitSpace(std::istream& IS, TOutputIter result)
{
typename std::iterator_traits<TOutputIter>::value_type elem;
while (IS >> elem)
{
*result = elem;
++result;
}
return result;
}
Однако приведенные выше коды не работают для back_insert_iterator
, Я проверил исходники back_insert_iterator/front_insert_iterator/insert_iterator
в std
пространство имен и нашел value_type/difference_type/pointer/reference
являются все void
,
Я хотел бы знать, почему все эти типы void
Есть ли какое-либо соображение по этому поводу? Другой вопрос: возможно ли реализовать SplitSpace
функция без явного указания типа элемента при вызове? Благодарю.
value_type
не имеет большого смысла в случае OutputIterators, потому что выходной итератор не дает доступа ни к каким значениям, и что более важно, он может принимать широкий диапазон типов значений.
Единственное требование для OutputIterator it
является то, что оно должно поддерживать выражение *it = o
где o
это значение некоторого типа, которое находится в наборе типов, доступных для записи в конкретный тип итератора i (§24.2.1). Это означает, что выходной итератор может потенциально принимать широкий диапазон типов: например, его operator*
может вернуть объект прокси, который перегружает operator=
для различных типов; что должно быть value_type
в этом случае?
Например, рассмотрим следующий выходной итератор:
struct SwallowOutputIterator :
public std::iterator<output_iterator_tag, void, void, void, void>
{
struct proxy // swallows anything
{
template <typename T>
void operator=(const T &) {}
};
proxy operator*()
{
return proxy();
}
// increment operators
};
Там нет разумного выбора для value_type
Вот.
То же самое относится и к pointer
а также reference_type
, difference_type
не определен, потому что вы не можете вычислить расстояние между двумя выходными итераторами, так как они однопроходные.
Н.Б .: в стандарте прямо говорится, что insert_iterator
и его братья и сестры должны наследовать от iterator<output_iterator_tag, void, void, void, void>
так что это не особенность вашей реализации.
Как отметил Люк в комментариях, то, что вы хотите сделать, может быть легко сделано с помощью стандартной библиотеки:
std::vector<int> v;
std::copy( std::istream_iterator( std::cin )
, std::istream_iterator()
, std::back_inserter( v ) );
В случае, если вы просто ищете способ избежать, чтобы указать TElem
Шаблон param, вы можете использовать этот подход:
template <typename TOutputIter>
TOutputIter SplitSpace(std::istream& IS, TOutputIter result)
{
typedef typename TOutputIter::container_type::value_type TElem;
TElem elem;
while (IS >> elem)
{
*result = elem;
++result;
}
return result;
}
…конечно, в зависимости от того, какие типы TOutputIter вы используете, вы не должны использовать этот подход.
AFAIK, вы должны быть в состоянии получить container_type
от итератора, из которого вы сможете получить value_type
, Вы, вероятно, хотите специализироваться на pair
в какой-то момент. Это должно ответить на вторую часть, как для первой части; Точно сказать не могу…