【问题标题】:Why doesn't my custom iterator work with the STL copy?为什么我的自定义迭代器不适用于 STL 副本?
【发布时间】:2010-12-15 23:47:34
【问题描述】:

我为answer to another question 编写了一个OutputIterator。这里是:

#include <queue>

using namespace std;

template< typename T, typename U >
class queue_inserter {
    queue<T, U> &qu;  
public:
    queue_inserter(queue<T,U> &q) : qu(q) { }
    queue_inserter<T,U> operator ++ (int) { return *this; }
    queue_inserter<T,U> operator * () { return *this; }
    void operator = (const T &val) { qu.push(val); }
};

template< typename T, typename U >
queue_inserter<T,U> make_queue_inserter(queue<T,U> &q) {
    return queue_inserter<T,U>(q);
}    

这非常适合这个小复制功能:

template<typename II, typename OI>
void mycopy(II b, II e, OI oi) {
    while (b != e) { *oi++ = *b++; }
}

但对于来自 algorithms 的 STL copy,它根本不起作用。以下是我得到的美妙的 C++ 错误:

i.cpp:33: error: specialization of ‘template<class _Iterator> struct std::iterator_traits’ in different namespace
/usr/include/c++/4.0.0/bits/stl_iterator_base_types.h:127: error:   from definition of ‘template<class _Iterator> struct std::iterator_traits’
/usr/include/c++/4.0.0/bits/stl_algobase.h: In function ‘_OI std::__copy_aux(_II, _II, _OI) [with _II = int*, _OI = queue_inserter<int, std::deque<int, std::allocator<int> > >]’:
/usr/include/c++/4.0.0/bits/stl_algobase.h:335:   instantiated from ‘static _OI std::__copy_normal<true, false>::copy_n(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _OI = queue_inserter<int, std::deque<int, std::allocator<int> > >]’
/usr/include/c++/4.0.0/bits/stl_algobase.h:387:   instantiated from ‘_OutputIterator std::copy(_InputIterator, _InputIterator, _OutputIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _OutputIterator = queue_inserter<int, std::deque<int, std::allocator<int> > >]’
i.cpp:53:   instantiated from here
/usr/include/c++/4.0.0/bits/stl_algobase.h:310: error: no type named ‘value_type’ in ‘struct std::iterator_traits<queue_inserter<int, std::deque<int, std::allocator<int> > > >’
/usr/include/c++/4.0.0/bits/stl_algobase.h:315: error: no type named ‘value_type’ in ‘struct std::iterator_traits<queue_inserter<int, std::deque<int, std::allocator<int> > > >’
/usr/include/c++/4.0.0/bits/stl_algobase.h:315: error: ‘__value’ is not a member of ‘<declaration error>’
/usr/include/c++/4.0.0/bits/stl_algobase.h:335:   instantiated from ‘static _OI std::__copy_normal<true, false>::copy_n(_II, _II, _OI) [with _II = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _OI = queue_inserter<int, std::deque<int, std::allocator<int> > >]’
/usr/include/c++/4.0.0/bits/stl_algobase.h:387:   instantiated from ‘_OutputIterator std::copy(_InputIterator, _InputIterator, _OutputIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >, _OutputIterator = queue_inserter<int, std::deque<int, std::allocator<int> > >]’
i.cpp:53:   instantiated from here
/usr/include/c++/4.0.0/bits/stl_algobase.h:317: error: ‘__simple’ is not a valid template argument for type ‘bool’ because it is a non-constant expression
/usr/include/c++/4.0.0/bits/stl_algobase.h:317: error: ‘copy’ is not a member of ‘<declaration error>’

这是驱动程序:

int main() {
    vector<int> v;
    v.push_back( 1 );
    v.push_back( 2 );
    queue<int> q;
    copy( v.begin(), v.end(), make_queue_inserter(q) );
    while (q.size() > 0) {
        cout << q.front() << endl;
        q.pop();
    }
}

为什么它专门针对iterator_traits。我的迭代器有什么问题?我不能只编写自己的简单迭代器吗?

【问题讨论】:

  • 算法应该如何知道它是什么种类的迭代器?例如,使用随机访问迭代器,该算法可能运行得更快,但它如何知道您的迭代器是否是随机访问?
  • 因为你所有的迭代器都应该像这样开始struct queue_inserter : boost::iterator_facade&lt;...&gt; {...
  • @alfC: FWIW,我刚刚尝试用boost::iterator_facade 编写一个简单的OutputIterator,然后立即遇到了麻烦。 stackoverflow.com/questions/43481025 由于 OP 试图制作一个 OutputIterator,“iterator_facade 通过魔法解决你所有的问题”并不是有用的建议。事实证明,iterator_facade 创造了它自己的一些问题。 (仍然应该使用它,但它不是单行答案。这是一个多行答案,有几个警告,希望是一个例子。)
  • @Quuxplusone。是的,评论是提示,而不是答案。

标签: c++ stl iterator


【解决方案1】:

您的queue_inserter 需要从std::iterator 派生,以便正确定义所有类型定义,例如value_type,因为它们在STL 算法中使用此定义有效:

template< typename T, typename U >
class queue_inserter : public std::iterator<std::output_iterator_tag, T>{
    queue<T, U> &qu;  
public:
    queue_inserter(queue<T,U> &q) : qu(q) { }
    queue_inserter<T,U> operator ++ (int) { return *this; }
    queue_inserter<T,U> operator ++ () { return *this; }
    queue_inserter<T,U> operator * () { return *this; }
    void operator = (const T &val) { qu.push(val); }
};

【讨论】:

  • 令人惊讶的是,STL 的设计如此糟糕。我认为迭代器的全部意义在于我可以自己滚动? char* 是否继承自 std::iterator?但感谢您的信息。 :-)
  • 你仍然可以通过不派生来解决......(虽然我还没有尝试过),但是你需要自己的所有这些类型定义。
  • @Frank Krueger:如果标准库的算法无法确定需要操作的类型的属性,您将如何设计它们以高效工作?这不是一个小问题。 std::iterator_traits 由指针类型的实现专门化,因此这些可以与算法一起使用,而无需用户进行任何进一步的工作。
  • @Frank:这是一个糟糕的评论。迭代器的设计方式是 C++ 中使用模板时用来传递类型信息的一种非常常见的技术。只需对特征进行 quicj 搜索,我相信您会找到合适的文章。注意:算法使用特征。对于指针类型(如 char*),它们是为类对象显式定义的,它们指向所需信息的类型。所有非常基本的东西。
  • 但是 typedefs 是这样继承的吗?我对这个答案和 SO (link) 的另一个答案感到困惑。
【解决方案2】:

从 std::iterator 派生它。如果您有兴趣,Dobb 博士有一个关于自定义容器和迭代器的 article

【讨论】:

    【解决方案3】:

    您的迭代器不满足“可分配”类型的要求,这是输出迭代器的要求,因为它包含引用,并且可分配类型需要确保在 t = u 之后 t 等于 @987654323 @。

    您可以通过从 std::iterator 的特化派生或显式提供一个为您的迭代器提供合适的 iterator_traits 特化。

    namespace std
    {
        template<> struct iterator_traits<MyIterator>
        {
            typedef std::output_iterator_tag iterator_category;
            typedef void value_type;
            typedef void difference_type;
        };
    }
    

    【讨论】:

    • 感谢您给予我更多了解iterator_traits 的好奇心!以前从来不知道。尽管不需要 iterator_traits 的显式特化。看我的回答:)
    • iterator_traitschar_traitspointer_traits;regex_traitsallocator_traits 也是!还有一个 &lt;type_traits&gt; 包含,但这完全不相关。
    【解决方案4】:
    #include <queue>
    #include <algorithm>
    #include <iterator>
    #include <iostream>
    
    using namespace std;
    
    template< typename T, typename U >
    class queue_inserter
    {
        queue<T, U> &qu;
    
    public:
        // for iterator_traits to refer
        typedef output_iterator_tag iterator_category;
        typedef T value_type;
        typedef ptrdiff_t difference_type;
        typedef T* pointer;
        typedef T& reference;
    
        queue_inserter(queue<T,U> &q) : qu(q) { }
        queue_inserter<T,U>& operator ++ () { return *this; }
        queue_inserter<T,U> operator * () { return *this; }
        void operator = (const T &val) { qu.push(val); }
    };
    
    template< typename T, typename U >
    queue_inserter<T,U> make_queue_inserter(queue<T,U> &q)
    {
        return queue_inserter<T,U>(q);
    }
    
    int main()
    {
        // uses initalizer list (C++0x), pass -std=c++0x to g++
        vector<int> v({1, 2, 3});
        queue<int, deque<int>> q;
        copy(v.cbegin(), v.cend(), make_queue_inserter(q));
        while (!q.empty())
        {
            cout << q.front() << endl;
            q.pop();
        }
    }
    

    这应该用iterator_traits来做; &lt;iterator&gt; 中的辅助结构,它定义了迭代器通常应该定义的所有类型。 &lt;algorithm&gt; 中的函数,在需要时引用这些类型,例如 iterator_traits&lt;it&gt;::iterator_category 或说 iterator_traits&lt;it&gt;::value_type 等。只需在自定义迭代器中定义它们就可以了。这是编写迭代器的现代方式,而不是从std::iterator 继承的经典方式。查看&lt;iterator&gt; 会发现,即使std::iterator 也定义了这些类型,即iterator_category、difference_type 等。这就是从std::iterator 继承时,派生的迭代器类由于遗传而获得这些类型的原因。

    【讨论】:

    • 为什么从 std::iterator 继承已经过时了,我可以在哪里找到更多关于如何以正确的方式做到这一点的信息?
    • 详情请见this answer。很抱歉回复得太晚了,不知道你问了。
    猜你喜欢
    • 2019-10-31
    • 1970-01-01
    • 2011-01-22
    • 1970-01-01
    • 2019-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    相关资源
    最近更新 更多