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

Почему значение value_type/difference_type/указатель/ссылка 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 без явного указания типа элемента при вызове? Спасибо.

4b9b3361

Ответ 1

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

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

Ответ 2

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

N.B.: стандарт явно указывает, что insert_iterator и его братья и сестры должны наследоваться от iterator<output_iterator_tag, void, void, void, void>, поэтому это не является особенностью вашей реализации.

Ответ 3

Если вы просто ищете способ избежать указания параметра шаблона TElem, вы можете использовать этот подход:

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

Ответ 4

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