2009-11-12 2 views
8

Я написал OutputIterator для answer to another question. Вот оно:Почему мой пользовательский итератор не работает с копией STL?

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

Но это не работает для СТЛ copy из algorithms. Вот замечательные ошибки 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. Что случилось с моим итератором? Разве я не могу написать свои простые итераторы?

+0

И как алгоритм должен знать, что такое _kind_ итератора? Например, алгоритм может работать быстрее с Итераторами случайного доступа, но как он знает, является ли ваш итератор произвольным доступом или нет? –

+0

Поскольку все ваши итераторы должны начинаться так: struct queue_inserter: boost :: iterator_facade <...> {... ' – alfC

+0

@alfC: FWIW, я просто попробовал написать простой OutputIterator с' boost :: iterator_facade' и сразу же столкнулся с проблемой. http://stackoverflow.com/questions/43481025 Поскольку OP был * также * пытается создать OutputIterator, «' iterator_facade' решает все ваши проблемы по магии »- это не полезно. Оказывается, 'iterator_facade' * создает * некоторые свои проблемы. (Тем не менее, вероятно, следует использовать его, но это не однострочный ответ.Это многострочный ответ с несколькими оговорками и, надеюсь, пример.) – Quuxplusone

ответ

17

Ваши 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); } 
}; 
+1

Удивительно, как плохо был разработан STL. Я думал, что все итераторы были в том, что я мог бы свернуть сам? Заставляет ли 'char *' наследовать от std :: iterator? Но спасибо за информацию. :-) –

+5

Вы все еще можете обойтись, не вызывая .. (я еще не пробовал), но вам нужно все эти typedefs самостоятельно. – Naveen

+3

@Frank Krueger: Как бы вы разработали алгоритмы стандартной библиотеки для эффективной работы, если они не могут определить свойства типов, в которых они должны работать? Это не тривиальная проблема. std :: iterator_traits специализируется на реализации для типов указателей, так что они могут использоваться с алгоритмами без какой-либо дополнительной работы со стороны пользователя. –

8

Выведите его из станд :: итератор. Если вам интересно, у доктора Добба есть article о пользовательских контейнерах и итераторах.

6

Вашего итератор не удовлетворяет требование в отношении «назначаемый» типа, который является необходимым условием для итератора вывода, поскольку он содержит ссылку и назначаемые типам должны гарантировать, что после того, как t = u что t эквивалентно u.

Вы можете предоставить подходящую специализацию для вашего итератора для iterator_traits либо путем специализации std::iterator, либо путем предоставления одного явно.

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

Спасибо, что дал мне любопытство узнать больше о 'iterator_traits'! Никогда не знал об этом раньше. Хотя явная специализация 'iterator_traits' не нужна. См. Мой ответ :) – legends2k

+0

'iterator_traits',' char_traits', 'pointer_traits;', 'regex_traits' и' allocator_traits' тоже! Также есть '' include, но это совершенно не связано. –

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; вспомогательная структура в <iterator>, которая определяет все типы, которые обычно должен определять итератор. Функции в <algorithm>, обратитесь к этим типам, когда это необходимо, например iterator_traits<it>::iterator_category или скажите iterator_traits<it>::value_type и т. Д. Просто определение их внутри собственного итератора сделало бы трюк. Это современный способ написания итераторов, в отличие от классического способа наследования от std::iterator. Взглянув на <iterator>, вы обнаружите, что даже std::iterator определяет эти типы, то есть iterator_category, difference_type и т. Д. Это причина, когда унаследовано от std::iterator, полученный класс итератора получает их из-за наследственности.

+0

Почему унаследовано от 'std :: iterator' устаревшее, и где я могу найти больше о том, как это сделать правильно? – SasQ

+0

Подробнее см. [Этот ответ] (http://stackoverflow.com/a/8054856/183120). И извините за очень поздний ответ, не знал, что вы спрашивали. – legends2k

Смежные вопросы