Search code examples
c++11visual-studio-2013iteratorreverse-iterator

Why doesn't the scope resolution not go outside of the class?


I am trying to write a generic reverse wrapper for containers that use bidirectional iterators using std::reverse_iterator.

However, it would seem that when the compiler looks for begin(...) or end(...), it'll say it can't find a matching function call to reverse_wrapper<CONTAINER>::begin(container) because the candidate expects 0 arguments, but 1 provided.

I would have guessed this is because std::begin(myArray&) and std::end(myArray&) don't exist. Forcing them into the std namespace didn't work (and is not advisable anyway). I've also tried to remove the std:: prefixes from my reverse_wrapper but that didn't work and would also break the working std container implementation.

This seems to be a scope resolution problem, but I can't seem to get the fix. What am I doing wrong?

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
}

Errors:

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


Solution

  • In response to the comments, let me write some workaround for the lack of automatically deduced return type.

    In summary, the problem is that you are using a namespace-qualified call to begin and end when you actually simply one which ever is the best fit, while using the std:: implementation as a backup.

    Since the solutions I've proposed in the comments don't work, you could try this.

    In the header containing reverse_wrapper, you can add this method

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