为什么back_insert_iterator / front_insert_iterator / insert_iterator的value_type / difference_type / pointer / reference都是无效的?

时间:2013-04-23 09:23:15

标签: c++ types iterator iterator-traits

在我的项目中,我想将流分割为某些给定类型的值,因此我将模板函数实现为

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函数而不显式给出元素类型?感谢。

4 个答案:

答案 0 :(得分:6)

对于OutputIterators,

value_type没有多大意义,因为输出迭代器不允许访问任何值,更重要的是它可以接受多种值类型。

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没有明智的选择。

同样的推理适用于pointerreference_typedifference_type未定义,因为您无法计算两个输出迭代器之间的距离,因为它们是单遍的。

N.B。:该标准明确规定insert_iterator及其兄弟姐妹必须继承自iterator<output_iterator_tag, void, void, void, void>,因此这不是您实施的特点。

答案 1 :(得分:4)

如果您只想找到避免指定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类型,你不应该使用这种方法。

答案 2 :(得分:3)

正如Luc在评论中所提到的,您可以使用标准库轻松完成您想要做的事情:

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

答案 3 :(得分:2)

AFAIK,您应该能够从迭代器中获取container_type,从中您应该能够获得value_type。您可能希望在某个时候专注于pair。这应该回答第二部分,第一部分;不确定......