3

Suppose I have a class that extends a (STL) container and provides a customary begin member function:

#include <vector>

template <typename Cont>
struct Bar {
    Cont c;
    auto my_begin() { return begin(c); }
};

int main() {
    Bar<std::vector<int>> b;
    b.my_begin();
}

By ADL, I do not have to specify std:: before the begin() call. This is nice, as std::begin(v) will always try to call v.begin(), a user may want to use a custom container that has no .begin() interface, so if he defines his own free-function begin(v), Bar will use it. However, it seems ADL will no longer work if I rename my_begin to begin as well, as if it was overshadowed. The compiler will just complain that it cannot find a matching call to begin in the class scope:

prog.cc: In instantiation of 'auto Bar<Cont>::begin() [with Cont = std::vector<int>]':
prog.cc:11:13:   required from here
prog.cc:6:27: error: use of 'auto Bar<Cont>::begin() [with Cont = std::vector<int>]' before deduction of 'auto'
    6 |     auto begin() { return begin(c); }
      |                           ^~~~~
prog.cc:6:32: error: no matching function for call to 'Bar<std::vector<int> >::begin(std::vector<int>&)'
    6 |     auto begin() { return begin(c); }
      |                           ~~~~~^~~
prog.cc:6:10: note: candidate: 'auto Bar<Cont>::begin() [with Cont = std::vector<int>]'
    6 |     auto begin() { return begin(c); }
      |          ^~~~~
prog.cc:6:10: note:   candidate expects 0 arguments, 1 provided

Of course, by writing std::begin, the code will run again, but this would just manifest the exclusion of ADL. What can I do, apart from renaming the member function?

Waqar
  • 8,558
  • 4
  • 35
  • 43
Jodocus
  • 7,493
  • 1
  • 29
  • 45

2 Answers2

6

You can just use the two step method:

    auto begin() { 
        using std::begin;   //1
        return begin(c);    //2
    }

1: bring std::begin into consideration
2: If c has begin() in its namespace, call that, otherwise default to std::begin()

You can read more about it here

Waqar
  • 8,558
  • 4
  • 35
  • 43
4

One solution is to write a standalone helper function that specifically uses ADL on begin:

template <typename C>
decltype(auto) adl_begin(C && c)
{
    using std::begin;
    return begin(std::forward<C>(c));
}

And then call adl_begin from within your class.

The using std::begin; part is there so that the function even works with primitive arrays.

lisyarus
  • 15,025
  • 3
  • 43
  • 68