2016-07-19 8 views
0

Ich versuche, einen generischen umgekehrten Wrapper für Container zu schreiben, die bidirektionale Iteratoren mit std::reverse_iterator verwenden.Warum geht die Bereichsauflösung nicht außerhalb der Klasse?

Allerdings scheint es, dass, wenn der Compiler für begin(...) oder end(...) aussieht, es wird sagen, dass es keinen passenden Funktionsaufruf reverse_wrapper<CONTAINER>::begin(container) weil der Kandidat 0 Argumente finden erwartet, aber 1 zur Verfügung gestellt.

Ich hätte gedacht, das ist, weil std::begin(myArray&) und std::end(myArray&) nicht existieren. Sie in den Namespace std zu zwingen, funktionierte nicht (und ist sowieso nicht ratsam). Ich habe auch versucht, die std:: Präfixe von meinem reverse_wrapper zu entfernen, aber das hat nicht funktioniert und würde auch die funktionierende std Containerimplementierung brechen.

Dies scheint ein Problem Bereich Auflösung zu sein, aber ich kann nicht scheinen, um die Lösung zu bekommen. Was mache ich falsch?

Code:

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

Fehler:

 
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

Entfernen Sie die 'std ::' in 'std :: begin' und' std :: end', fügen Sie dann 'using std :: begin; Verwenden Sie std :: end' an den Stellen, an denen Sie 'begin' und' end' verwenden. Es könnte besser sein, automatisch abgeleiteten Typ anstelle von nachgestellten Rückgabetyp in einigen Fällen zu verwenden – KABoissonneault

+0

@KABoissonneault, yeah, ich habe gedacht, mit einem 'using std :: begin/end ', aber es wird nicht im Klassenbereich so funktionieren Der Rückgabetyp kann es nicht finden. Ich dachte auch an einen automatisch abgeleiteten Typ, aber das funktioniert nicht in VS2013. – Adrian

+0

Wie wäre es, sie im Namensraumbereich zu verwenden? – KABoissonneault

Antwort

2

Als Reaktion auf die Kommentare, lassen Sie mich für den Mangel an automatisch abgeleitet Rückgabetyp eine gewisse Abhilfe schreiben.

Zusammenfassend das Problem ist, dass Sie einen Namespace-qualifizierte Aufruf begin verwenden und end, wenn Sie tatsächlich einfach eine, die immer die beste Lösung ist, während die std:: Implementierung als Backup verwenden.

Da die Lösungen, die ich in den Kommentaren vorgeschlagen habe, nicht funktionieren, könnten Sie dies versuchen.

Im Header enthält reverse_wrapper, können Sie diese Methode hinzufügen

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

Es gibt drei mögliche Lösungen, die ich denken kann, von denen bereits in another answer beschrieben worden; Ich würde empfehlen, dieses zu verwenden, wenn man bedenkt, dass Sie VS 2013 verwenden, aber ich dachte, dass ich die anderen zwei als Referenz zur Verfügung stellen würde.

Die erste Lösung, die den Sinn kommt, wenn möglich, Elementfunktionen begin() und end() zu myArray (und alle Typen es fungiert als Platzhalter für) hinzuzufügen; Der einfachste Weg dazu wäre, die Funktionen int* begin(myArray&) und int* end(myArray&) in Elementfunktionen umzuwandeln. Dadurch können std::begin() und die entsprechenden Iteratoren zurückgeben, was wiederum reverse_wrapper::begin() und reverse_wrapper::end() ermöglicht.

Dies ist, weil for any instance c of container class C, std::begin() will take C& c and return exactly c.begin(). Gleichermaßen std::end() will take C& c and return exactly c.end().

Beachten Sie, dass dies die Änderung jedes benutzerdefinierten Containertyps mit den Elementfunktionen begin() und end() erfordern würde, wenn diese nicht bereits vorhanden sind. Dies kann eine zeitaufwendige und möglicherweise fehleranfällige Aufgabe sein.Ich würde diesen Ansatz empfehlen, wenn Sie die Container von Grund auf neu erstellen, aber nicht, wenn Sie mit bereits vorhandenen Containern arbeiten, die begin() und end() als eigenständige Funktionen definieren.


Alternativ kann, wie KaBoissonneault in den Kommentaren darauf hingewiesen, können Sie die Verwendung von Klärungen using std::begin; und using std::end; verwenden können, sofern Ihr Compiler Rückgabetyp Abzug durchführen kann.

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

Dies ist nicht möglich bei Compilern, die Rückführungsabzug nicht unterstützen; Von den größten 3 Compilern erfordert es Clang 3.4 oder höher, GCC 4.9 oder höher oder Visual Studio 2015. Wenn Sie eine frühere Version verwenden, müssen Sie eine andere Lösung verwenden.


Oder, wie KaBoissonneault's answer erklärt, können Sie die Details der Implementierung in einem Namensraum verbergen, so dass Sie die Vorteile der Verwendung Klärungen sogar ernten, wenn der Compiler nicht Rückgabetyp Abzug nicht unterstützt. Dies ist die einfachste Lösung und erfordert die geringste Menge an Änderungen. Es ist auch diejenige, die am ehesten von einem gegebenen Compiler unterstützt wird, da keine C++ 14 Features benötigt werden.

Verwandte Themen