2015-09-12 4 views
2

Примечание: Я написал «рецепт» на основе уроков, извлеченных из учений и ответы & комментарии на этой странице, см http://www.codeproject.com/Tips/1029941/Python-like-enumeration-in-Cplusplus.шаблон для сопзЬ итератор вместо итератора

Я играю с расширениями, которые C++ 11 приносит на C++ 03. Я хочу, чтобы иметь возможность перебирать контейнер, используя следующий код:

int main() 
{ 
    std::list<int> list = { 1, 2, 3, 4, 5, 6, 7 }; 
    for (auto x : enumerated(list)) 
     cout << x.first << " " << x.second << endl; 
    for (auto x : const_enumerated(list)) 
     cout << x.first << " " << x.second << endl; 
} 

Первая итерация имеет изменяемую х, против для второй, пытаясь изменить х приведет к ошибке компиляции. У меня есть то, что работает для неконстантного случая:

template <typename Container> 
class EnumerationAdaptor 
{ 
public: 
    EnumerationAdaptor(Container& container) : container_(container) {} 
    EnumIter<typename Container::iterator> begin() const { return container_.begin(); } 
    EnumIter<typename Container::iterator> end() const { return container_.end(); } 

private: 
    Container& container_; 
}; 

template <typename Container> 
EnumerationAdaptor<Container> enumerated(Container& container) { return container; } 

template <typename Container> 
EnumerationAdaptor<const Container> const_enumerated(const Container& container) { return container; } 

неконстантная случае использует EnumIter<std::list<...>::iterator>, как хотелось бы, и я пытаюсь сделать сопзЬ случай использовать EnumIter<std::list<...>::const_iterator> в качестве возвращаемого типа begin() и end(). Похоже, мне нужно decltype:

template <typename Container> 
class EnumerationAdaptor 
{ 
public: 
    EnumerationAdaptor(Container& container) : container_(container) {} 
    EnumIter<decltype(Container().begin())> begin() const { return container_.begin(); } 
    EnumIter<decltype(Container().end())> end() const { return container_.end(); } // *** compile error (see below) 

private: 
    Container& container_; 
}; 

Но я получаю ошибку компиляции в Visual Studio 2015 Экспресс:

Error C2440 'return': cannot convert from 
'std::_List_const_iterator<std::_List_val<std::_List_simple_types<int>>>' 
to 
'EnumIter<std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>>' 
[in c:\users\...\enumeratedcpp.cpp line 46, which is line marked ***] 

, который говорит о том, что я делаю что-то неправильно с decltype, как компилятор отыскания non-const begin(). Есть ли способ исправить это?

EDIT: даже с простой EnumIter, проблема в том же:

template <typename Iter> 
class EnumIter 
{ 
public: 
    EnumIter(Iter begin) : iter_(begin) {} 

    EnumIter& operator++() 
    { 
     return *this; 
    } 

    bool operator!=(const EnumIter& rhs) 
    { 
     return iter_ != rhs.iter_; // or self.index_ != rhs.index_; 
    } 

    int operator*() const 
    { 
     return index_; 
    } 

private: 
    Iter iter_; 
    int index_ = 0; 
}; 
+1

это, вероятно, проще решить с помощью отдельного 'ConstEnumerationAdaptor', который вызывает' cbegin' и 'cend' – sp2danny

+1

в противном случае, попробуйте заменить' Container() '(внутри decltype) на' std :: declval () '(from ) – sp2danny

+0

На самом деле, я думаю, что проблема может заключаться в 'EnumIter <>' – sp2danny

ответ

2

Существует одна проблема с этим выражением:

decltype(Container().begin()) 

, которая является то, что Container() работает только тогда, когда Container случается по умолчанию. Это ограничивает удобство использования вашего класса без причины. (Существует меньшая проблема, которая заключается в том, что это не будет работать для необработанных массивов, но это еще одно упражнение).

Кроме того, код отлично подходит для типов классов. Из [expr.type.conv]:

выражения T(), где Т представляет собой простой тип спецификатор или Ьурепате-спецификатор для не-массива полного типа объекта или (возможно, CV-квалифицированный) недействительным типа, создает prvalue указанного типа [...]

Так что если Container является const list<int>, то тип этого все выражение должно быть list<int>::const_iterator. Если MSVC дает вам что-то еще, это ошибка.

Это говорит о том, что мы действительно должны решить проблему конструктивности по умолчанию. Вот где std::declval приходит в:

decltype(std::declval<Container&>().begin()) 

Это не накладывает никаких ограничений на Container, и, возможно, MSVC будет правильно обращаться с этим.

+0

«Во-вторых, тип вызова конструктора никогда не определяется с помощью cv. Таким образом,' decltype (T()) ', где valid, является таким же, как' std :: remove_cv_t '." [expr.type.conv]/p2, по-видимому, предлагает другое. И как GCC, так и Clang [сохраняет] (http://coliru.stacked-crooked.com/a/5bfc2907fd219cc0) cv-квалификацию. –

+0

@ T.C. Да, в первый раз я пропустил неклассовую часть. Гектометр Итак, почему выражение OP не работает? – Barry

+0

Ошибка MSVC, насколько я могу судить. –

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