2016-07-19 4 views
0

Я пытаюсь написать общую обратную оболочку для контейнеров, которые используют двунаправленные итераторы, используя std::reverse_iterator.Почему разрешение не выходит за пределы класса?

Однако, казалось бы, что, когда компилятор ищет begin(...) или end(...), он скажет, что не может найти вызов функции согласования с reverse_wrapper<CONTAINER>::begin(container) потому что кандидат рассчитывает 0 аргументов, но 1, при условии.

Я бы предположил, что это потому, что std::begin(myArray&) и std::end(myArray&) не существует. Принуждение их к пространству имен std не работало (и в любом случае это не рекомендуется). Я также попытался удалить префиксы std:: из моего reverse_wrapper, но это не сработало и также нарушит рабочую реализацию контейнера std.

Это, кажется, проблема с разрешением области, но, похоже, я не могу исправить это. Что я делаю не так?

Код:

#include <iterator> 
#include <iostream> 
#include <vector> 

#define Fn1 0 // std container WORKS 
#define Fn2 1 // non-std container DOESN'T WORK 

template <typename Container> 
struct reverse_wrapper 
{ 
    Container& container; 

    auto begin() 
     -> std::reverse_iterator< decltype(std::end(container)) > 
    { 
     return std::reverse_iterator< decltype(std::end(container)) >(std::end(container)); 
    } 

    auto end() 
     -> std::reverse_iterator< decltype(std::begin(container)) > 
    { 
     return std::reverse_iterator< decltype(std::begin(container)) >(std::begin(container)); 
    } 
}; 

template <typename Container> 
auto reverse(Container&& container) 
-> reverse_wrapper<Container> 
{ 
    return{ container }; 
} 

struct myArray 
{ 
    int elements[5] = {1,2,3,4,5}; 
}; 

int* begin(myArray& array) { return &array.elements[0]; } 
int* end(myArray& array) { return &array.elements[5]; } 

#if Fn1 
void fn1() 
{ 
    std::vector<int> x = { 1,2,3,4,5 }; 
    for (auto& ix : reverse(x)) 
    { 
     std::cout << ix << std::endl; 
    } 
    std::cout << "-----" << std::endl; 
    for (auto& ix : x) 
    { 
     std::cout << ix << std::endl; 
    } 
} 
#endif 

#if Fn2 
void fn2() 
{ 
    myArray x; 
    for (auto& ix : reverse(x)) 
    { 
     std::cout << ix << std::endl; 
    } 
    std::cout << "-----" << std::endl; 
    for (auto& ix : x) 
    { 
     std::cout << ix << std::endl; 
    } 
} 
#endif 

int main() 
{ 
#if Fn1 
    fn1(); 
#endif 
#if Fn2 
    fn2(); 
#endif 
} 

Ошибки:

 
In instantiation of 'struct reverse_wrapper': 
61:30: required from here 
14:56: error: no matching function for call to 'end(myArray&)' 
14:56: note: candidates are: 
In file included from /usr/include/c++/4.9/string:51:0, 
       from /usr/include/c++/4.9/bits/locale_classes.h:40, 
       from /usr/include/c++/4.9/bits/ios_base.h:41, 
       from /usr/include/c++/4.9/ios:42, 
       from /usr/include/c++/4.9/ostream:38, 
       from /usr/include/c++/4.9/iterator:64, 
       from 1: 
/usr/include/c++/4.9/bits/range_access.h:68:5: note: template decltype (__cont.end()) std::end(_Container&) 
    end(_Container& __cont) -> decltype(__cont.end()) 
    ^
/usr/include/c++/4.9/bits/range_access.h:68:5: note: template argument deduction/substitution failed: 
/usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.end()) std::end(_Container&) [with _Container = myArray]': 
14:56: required from 'struct reverse_wrapper' 
61:30: required from here 
/usr/include/c++/4.9/bits/range_access.h:68:5: error: 'struct myArray' has no member named 'end' 
In instantiation of 'struct reverse_wrapper': 
61:30: required from here 
/usr/include/c++/4.9/bits/range_access.h:78:5: note: template decltype (__cont.end()) std::end(const _Container&) 
    end(const _Container& __cont) -> decltype(__cont.end()) 
    ^
/usr/include/c++/4.9/bits/range_access.h:78:5: note: template argument deduction/substitution failed: 
/usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.end()) std::end(const _Container&) [with _Container = myArray]': 
14:56: required from 'struct reverse_wrapper' 
61:30: required from here 
/usr/include/c++/4.9/bits/range_access.h:78:5: error: 'const struct myArray' has no member named 'end' 
In instantiation of 'struct reverse_wrapper': 
61:30: required from here 
/usr/include/c++/4.9/bits/range_access.h:97:5: note: template _Tp* std::end(_Tp (&)[_Nm]) 
    end(_Tp (&__arr)[_Nm]) 
    ^
/usr/include/c++/4.9/bits/range_access.h:97:5: note: template argument deduction/substitution failed: 
14:56: note: mismatched types '_Tp [_Nm]' and 'myArray' 
In file included from /usr/include/c++/4.9/bits/basic_string.h:42:0, 
       from /usr/include/c++/4.9/string:52, 
       from /usr/include/c++/4.9/bits/locale_classes.h:40, 
       from /usr/include/c++/4.9/bits/ios_base.h:41, 
       from /usr/include/c++/4.9/ios:42, 
       from /usr/include/c++/4.9/ostream:38, 
       from /usr/include/c++/4.9/iterator:64, 
       from 1: 
/usr/include/c++/4.9/initializer_list:99:5: note: template constexpr const _Tp* std::end(std::initializer_list) 
    end(initializer_list __ils) noexcept 
    ^
/usr/include/c++/4.9/initializer_list:99:5: note: template argument deduction/substitution failed: 
14:56: note: 'myArray' is not derived from 'std::initializer_list' 
20:58: error: no matching function for call to 'begin(myArray&)' 
20:58: note: candidates are: 
In file included from /usr/include/c++/4.9/string:51:0, 
       from /usr/include/c++/4.9/bits/locale_classes.h:40, 
       from /usr/include/c++/4.9/bits/ios_base.h:41, 
       from /usr/include/c++/4.9/ios:42, 
       from /usr/include/c++/4.9/ostream:38, 
       from /usr/include/c++/4.9/iterator:64, 
       from 1: 
/usr/include/c++/4.9/bits/range_access.h:48:5: note: template decltype (__cont.begin()) std::begin(_Container&) 
    begin(_Container& __cont) -> decltype(__cont.begin()) 
    ^
/usr/include/c++/4.9/bits/range_access.h:48:5: note: template argument deduction/substitution failed: 
/usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.begin()) std::begin(_Container&) [with _Container = myArray]': 
20:58: required from 'struct reverse_wrapper' 
61:30: required from here 
/usr/include/c++/4.9/bits/range_access.h:48:5: error: 'struct myArray' has no member named 'begin' 
In instantiation of 'struct reverse_wrapper': 
61:30: required from here 
/usr/include/c++/4.9/bits/range_access.h:58:5: note: template decltype (__cont.begin()) std::begin(const _Container&) 
    begin(const _Container& __cont) -> decltype(__cont.begin()) 
    ^
/usr/include/c++/4.9/bits/range_access.h:58:5: note: template argument deduction/substitution failed: 
/usr/include/c++/4.9/bits/range_access.h: In substitution of 'template decltype (__cont.begin()) std::begin(const _Container&) [with _Container = myArray]': 
20:58: required from 'struct reverse_wrapper' 
61:30: required from here 
/usr/include/c++/4.9/bits/range_access.h:58:5: error: 'const struct myArray' has no member named 'begin' 
In instantiation of 'struct reverse_wrapper': 
61:30: required from here 
/usr/include/c++/4.9/bits/range_access.h:87:5: note: template _Tp* std::begin(_Tp (&)[_Nm]) 
    begin(_Tp (&__arr)[_Nm]) 
    ^
/usr/include/c++/4.9/bits/range_access.h:87:5: note: template argument deduction/substitution failed: 
20:58: note: mismatched types '_Tp [_Nm]' and 'myArray' 
In file included from /usr/include/c++/4.9/bits/basic_string.h:42:0, 
       from /usr/include/c++/4.9/string:52, 
       from /usr/include/c++/4.9/bits/locale_classes.h:40, 
       from /usr/include/c++/4.9/bits/ios_base.h:41, 
       from /usr/include/c++/4.9/ios:42, 
       from /usr/include/c++/4.9/ostream:38, 
       from /usr/include/c++/4.9/iterator:64, 
       from 1: 
/usr/include/c++/4.9/initializer_list:89:5: note: template constexpr const _Tp* std::begin(std::initializer_list) 
    begin(initializer_list __ils) noexcept 
    ^
/usr/include/c++/4.9/initializer_list:89:5: note: template argument deduction/substitution failed: 
20:58: note: 'myArray' is not derived from 'std::initializer_list' 
In function 'void fn2()': 
61:30: error: invalid initialization of reference of type 'myArray&' from expression of type 'reverse_wrapper' 
38:6: note: in passing argument 1 of 'int* begin(myArray&)' 
61:30: error: invalid initialization of reference of type 'myArray&' from expression of type 'reverse_wrapper' 
39:8: note: in passing argument 1 of 'int* end(myArray&)' 

Demo

+0

Удалите 'std ::' в 'std :: begin' и' std :: end', затем добавьте 'using std :: begin; используя std :: end' в тех местах, где вы используете 'begin' и' end'. Возможно, лучше использовать автоматически выводимый возвращаемый тип вместо возвращаемого типа возврата в некоторых случаях – KABoissonneault

+0

@KABoissonneault, да, я думал об использовании 'using std :: begin/end', но он не будет работать в области класса, поэтому тип возврата не может найти его. Я также думал об использовании автоматически выводимого типа, но это не работает в VS2013. – Adrian

+0

Как их использовать в пространстве имен? – KABoissonneault

ответ

2

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

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

Поскольку решения, которые я предложил в комментариях, не работают, вы можете попробовать это.

В заголовке, содержащем reverse_wrapper, вы можете добавить этот метод

namespace detail { 
    using std::begin; 
    using std::end; 
    template< typename Container > 
    auto impl_begin(Container & c) -> decltype(begin(c)) { 
     return begin(c); 
    } 
    // Same for end 
} 

template< typename Container > 
class reverse_wrapper { 
    Container& container; 
public: 
    auto end() -> decltype(detail::impl_begin(container)) { 
     return std::reverse_iterator<decltype(detail::impl_begin(container))>(detail::impl_begin(container)); 
    } 
    // ... and the rest 
}; 
1

Есть три возможных решения я могу думать о том, один из которых уже были описаны в another answer; Я бы рекомендовал использовать этот вариант, учитывая, что вы используете VS 2013, но решил, что я предоставил два других для справки.

Первое решение, которое приходит на ум, если это возможно, чтобы добавить функции-членов begin() и end() к myArray (и всем типам, он действует как заполнитель для); самый простой способ сделать это - включить функции int* begin(myArray&) и int* end(myArray&) в функции-члены. Это позволяет std::begin() и std::end() возвращать соответствующие итераторы, что в свою очередь позволяет работать reverse_wrapper::begin() и reverse_wrapper::end().

struct myArray 
{ 
    int elements[5] = {1,2,3,4,5}; 

    int* begin() { return &elements[0]; } 
    int* end() { return &elements[5]; } 
}; 

// int* begin(myArray& array) { return &array.elements[0]; } 
// int* end(myArray& array) { return &array.elements[5]; } 

Это потому, что for any instance c of container class C, std::begin() will take C& c and return exactly c.begin(). Аналогично, std::end() will take C& c and return exactly c.end().

Обратите внимание, что это потребует модификации каждого пользовательского типа контейнера, чтобы иметь функции-члены begin() и end(), если они еще не имеют их, что может быть трудоемкой и потенциально подверженной ошибкам задачей.Я бы рекомендовал этот подход при создании контейнеров с нуля, но не при работе с уже существующими контейнерами, которые определяют begin() и end() как автономные функции.


Альтернативно, как KaBoissonneault отметил в комментариях, вы можете использовать с помощью деклараций using std::begin;using std::end; и, если ваш компилятор может выполнить обратный тип вычет.

template <typename Container> 
struct reverse_wrapper 
{ 
    Container& container; 

    auto begin() 
//  -> std::reverse_iterator< decltype(std::end(container)) > 
    { 
     using std::end; 
     return std::reverse_iterator< decltype(/*std::*/end(container)) >(/*std::*/end(container)); 
    } 

    auto end() 
//  -> std::reverse_iterator< decltype(std::begin(container)) > 
    { 
     using std::begin; 
     return std::reverse_iterator< decltype(/*std::*/begin(container)) >(/*std::*/begin(container)); 
    } 
}; 

Это не будет возможно для компиляторов, которые не поддерживают вывод типа возврата; из 3 крупнейших компиляторов требуется Clang 3.4 или более поздняя версия, GCC 4.9 или новее, или Visual Studio 2015. Если вы используете версию до этого, вам нужно будет использовать другое решение.


Или, как KaBoissonneault's answer объяснил, вы можете скрыть детали реализации в пространстве имен, что позволяет воспользоваться преимуществами с помощью деклараций, даже если компилятор не поддерживает тип возвращаемого значения дедукции. Это самое простое решение и требует наименьшего количества изменений. Это также наиболее вероятно поддерживается любым компилятором из-за отсутствия каких-либо функций C++ 14.

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