1
votes

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

2
Remove the std:: in std::begin and std::end, then add using std::begin; using std::end in the places where you use begin and end. It might be better to use automatically deduced returned type instead of trailing return type in some casesKABoissonneault
@KABoissonneault, yeah, I have thought of using a using std::begin/end, but it won't work at class scope so the return type can't find it. I also thought of using an automatically deduced type, but that doesn't work in VS2013.Adrian
What about using them at namespace scope?KABoissonneault
@KABoissonneault, as these classes are in a headers, it isn't optimal. The myArray is just a placeholder for other array implementations scattered around, some at the base scope, some in std.Adrian
Ok, but in that regard, I still think myArray should provide its own iterators, and then reverse_wrapper can simply delegate to the container without using reverse_iterator directly: template <typename Container> struct reverse_wrapper { Container& container; auto begin() { return std::rbegin(container); } auto end() { return std::rend(container); } };Remy Lebeau

2 Answers

3
votes

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

There are three possible solutions I can think of, one of which has already been described in another answer; I would recommend using that one, considering that you're using VS 2013, but figured I'd provide the other two for reference.

The first solution that comes to mind is, if possible, to add member functions begin() and end() to myArray (and all the types it acts as a placeholder for); the easiest way to do this would be to turn the functions int* begin(myArray&) and int* end(myArray&) into member functions. This allows std::begin() and std::end() to return the appropriate iterators, which in turn allows reverse_wrapper::begin() and reverse_wrapper::end() to work.

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

This is because for any instance c of container class C, std::begin() will take C& c and return exactly c.begin(). Likewise, std::end() will take C& c and return exactly c.end().

Note that this would require modifying every user-defined container type to have member functions begin() and end() if it doesn't already have them, which can be a time-consuming and potentially error-prone task. I would recommend this approach if making the containers from scratch, but not if working with pre-existing containers that define begin() and end() as standalone functions.


Alternatively, as KaBoissonneault pointed out in the comments, you can use the using-declarations using std::begin; and using std::end;, provided your compiler can perform return type deduction.

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

This won't be possible on compilers that don't support return type deduction; out of the biggest 3 compilers, it requires Clang 3.4 or later, GCC 4.9 or later, or Visual Studio 2015. If using a version prior to these, you would need to use a different solution.


Or, as KaBoissonneault's answer explained, you can hide the implementation details in a namespace, allowing you to reap the benefits of using-declarations even when the compiler doesn't support return type deduction. This is the simplest solution, and requires the least amount of changes. It is also the one most likely to be supported by any given compiler, due to not requiring any C++14 features.