Reputation: 21540
I am trying to code a template function that uses an ADL resolved get
to fetch members of a struct/range (tuple
-esque).
#include <iostream>
#include <utility>
#include <tuple>
int main() {
auto tup = std::make_tuple(1, 2);
std::cout << get<0>(tup) << std::endl;
}
I am doing this because of what the structured bindings proposal (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf §11.5.3) says about how get
is used to fetch elements from the struct. It says that a non member get
is used to fetch elements from within the struct.
I assumed that the code above would compile, because ADL would cause the get
function to be looked for in the std
namespace (because it's argument is of type std::tuple<int, int>
, which is in std
), where it would be found. But, I get an error. Can someone explain the right approach here and also why the code above does not work? How can one force ADL to happen in this case?
Upvotes: 17
Views: 869
Reputation: 13810
p0846r0 that was accepted into C++20 now allows ADL for a call to template function with explicit template arguments.
So the OP's code now compiles as is with C++20 without an error!
Upvotes: 2
Reputation: 25347
Argument Dependent Lookup doesn't work the same way for function templates where an explicit template argument is given.
Although a function call can be resolved through ADL even if ordinary lookup finds nothing, a function call to a function template with explicitly-specified template arguments requires that there is a declaration of the template found by ordinary lookup (otherwise, it is a syntax error to encounter an unknown name followed by a less-than character)
Basically, there needs to be some way for the unqualified lookup to find a template function. Then, the ADL can kick in (because the name get
is then known to be a template). Cppreference gives an example:
namespace N1 { struct S {}; template<int X> void f(S); } namespace N2 { template<class T> void f(T t); } void g(N1::S s) { f<3>(s); // Syntax error (unqualified lookup finds no f) N1::f<3>(s); // OK, qualified lookup finds the template 'f' N2::f<3>(s); // Error: N2::f does not take a non-type parameter // N1::f is not looked up because ADL only works // with unqualified names using N2::f; f<3>(s); // OK: Unqualified lookup now finds N2::f // then ADL kicks in because this name is unqualified // and finds N1::f }
Structured bindings are a special case, with ADL enabled.
In the following contexts ADL-only lookup (that is, lookup in associated namespaces only) takes place:
- the lookup of non-member functions begin and end performed by the range-for loop if member lookup fails
- the dependent name lookup from the point of template instantiation.
- the lookup of non-member function get performed by structured binding declaration for tuple-like types
Emphasis added
Upvotes: 12
Reputation: 303457
The problem ultimately is templates:
std::cout << get<0>(tup) << std::endl;
// ~~~~
At that point, the compiler doesn't know that this is a function that needs to be looked up using ADL yet - get
is just a name. And since that name by itself doesn't find anything, this is going to be interpreted as an unknown name followed by less-than. To get this to work, you need some other function template get
visible:
using std::get;
std::cout << get<0>(tup) << std::endl; // now, OK
Even if it does nothing:
template <class T> void get();
int main() {
auto tup = std::make_tuple(1, 2);
std::cout << get<0>(tup) << std::endl;
}
The structured binding wording explicitly looks up get
using argument-dependent lookup, so it avoids the need to have an already-visible function template named get
, from [dcl.struct.bind]:
The unqualified-id
get
is looked up in the scope ofE
by class member access lookup, and if that finds at least one declaration, the initializer ise.get<i>()
. Otherwise, the initializer isget<i>(e)
, whereget
is looked up in the associated namespaces. In either case,get<i>
is interpreted as a template-id. [ Note: Ordinary unqualified lookup is not performed. — end note ]
The note is the key. If we had performed unqualified lookup, we'd just fail.
Upvotes: 17