The following code does not compile in C++17 but from C++20 on:
#include <tuple>
int main() {
auto t = std::make_tuple(1, 2);
get<0>(t); // no std:: required!
return 0;
}
Since this works with both g++ and clang++ and both -stdlib=libc++
and -stdlib=libstdc++
and even in msvc with /std:c++20
, I wonder whether this is a "feature". What is the rationale behind this? Is there more pollution to the global namespace besides this?
std::get
is found by argument-dependent lookup (ADL), because t
has a type that is a specialization of the class template std::tuple
which is located in the same namespace scope as the std::get
overload you want to use. This is behavior that has always existed in C++ and is fundamental to making operator overloading work, as well as other customizable function calls like swap
.
The reason it fails in C++17 has nothing to do with whether std::get
is found, but rather with the rules that determine whether or not the <
following get
is the less-than operator or the start of an template argument list. Before C++20, if the unqualified name before the <
wasn't found at all by usual unqualified (non-ADL) lookup, the program was ill-formed. Since C++20 it is assumed to introduce a template argument list in this situation.
For example, if you add a function template named get
, regardless of signature, e.g.
template<typename T>
void get();
into your global namespace scope, then <
will also be assumed to introduce a template argument list because this function template is found for get
by usual unqualified lookup, even in C++17. Then ADL applies to the call as usual (this has always worked this way in every C++ edition) and std::get
will be found as candidate as well.