tags:

views:

361

answers:

4

I wrote an OutputIterator for an answer to another question. Here it is:

#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);
}

This works great for this little copy function:

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

But it doesn't work at all for the STL copy from algorithms. Here are the wonderful C++ errors I get:

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>’

Here is the driver:

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();
    }
}

Why in the world is it specializing iterator_traits. What's wrong with my iterator? Can't I just write my own simple iterators?

+5  A: 

Your queue_inserter needs to be derived from std::iterator so that all the typedefs such as value_type are properly defined since these are used inside STL algorithms This definition works:

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); }
};
Naveen
It's amazing how poorly the STL was designed. I thought the whole point of iterators was that I could roll my own? Does `char*` inherit from std::iterator? But thanks for the info. :-)
Frank Krueger
You can still get around by not deriving..(I haven't tried it though), but you need to all those typedefs yourself.
Naveen
@Frank Krueger: How would you design the algorithms of the standard library to work efficiently if they can't determine the properties of the types that they need to operate on? It's not a trivial problem. std::iterator_traits is specialized by the implementation for pointer types so that these can be used with algorithms without any further work on the part of the user.
Charles Bailey
Actually, the iterator type should be`std::output_iterator_tag`, bidirectional implies that `operator--()` is defined.
rcollyer
@Frank: Thats a poorly though out comment. The way iterators are designed is a very common technique used in C++ to pass type information around when using templates. Just do a quicj search for traits and I am sure you find appropriate articles on it. Note: The algorithms use traits. For the pointer types (like char*) they are explicitly defined for class objects they point back at the type for the required information. All very basic stuff.
Martin York
+3  A: 

Derive it from std::iterator. If you are interested the Dr. Dobb's has an article about custom containers and iterators.

grigy
+2  A: 

Your iterator doesn't meet the requirement for an 'assignable' type which is a requirement for an output iterator because it contains a reference and assignable types need to ensure that after t = u that t is equivalent to u.

You can provide a suitable specialization for iterator_traits for your iterator either by deriving from a specialization of std::iterator or by providing one explicitly.

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

There is a reason Boost.Iterators exists.

You should particularly check out the iterator_adaptor class, a really convenient way of writing an iterator class very quickly.

Matthieu M.