Почему все значения / тип-различия / указатель / ссылка на back_insert_iterator / front_insert_iterator / insert_iterator являются недействительными?

В моем проекте я хочу разделить поток на значения определенного типа, поэтому я реализую функцию шаблона как

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 функция без явного указания типа элемента при вызове? Благодарю.

18

Решение

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>так что это не особенность вашей реализации.

5

Другие решения

Как отметил Люк в комментариях, то, что вы хотите сделать, может быть легко сделано с помощью стандартной библиотеки:

std::vector<int> v;
std::copy( std::istream_iterator( std::cin )
, std::istream_iterator()
, std::back_inserter( v ) );
3

В случае, если вы просто ищете способ избежать, чтобы указать 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 вы используете, вы не должны использовать этот подход.

3

AFAIK, вы должны быть в состоянии получить container_type от итератора, из которого вы сможете получить value_type, Вы, вероятно, хотите специализироваться на pair в какой-то момент. Это должно ответить на вторую часть, как для первой части; Точно сказать не могу…

2
По вопросам рекламы [email protected]