1

Given this code:

#include <algorithm>
#include <vector>

using std::vector;

int main()
{
    vector<int> intVec(100, 1);

    // no problem
    random_shuffle(intVec.begin(), intVec.end());

    // no problem
    std::random_shuffle<vector<int>::iterator>(intVec.begin(), intVec.end());

    // random_shuffle undefined! 
    random_shuffle<vector<int>::iterator>(intVec.begin(), intVec.end());
}

I realize name management in this code is bad. A less minimal codebase inspired my question however. random_shuffle is resolvable with no std qualification. If qualified and explicitly templated, no issue once more. However, if there is no std qualification, AND explicit template arguments(which are implicitly correct in the least), random_shuffle is suddenly undefined. Why?

Visual Studio 2017(I know...if this is MS shenanigans, just say so)

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
schulmaster
  • 413
  • 5
  • 16

1 Answers1

2

Unqualified name lookup for a function-call expression may also consider associated namespaces in order to find the referred function. This feature is called Argument-dependent lookup. The set of associated namespaces that are being searched through include the namespaces of the types of the function call arguments themselves, as well as the namespaces of template arguments of the class template types of the function call arguments. In other words, for a function call:

foo(A::B<C::D>{});

the set of associated namespaces include namespace A and namespace C, and both are examined to find the declaration of function foo.

In the first case:

random_shuffle(intVec.begin(), intVec.end());

std::random_shuffle is found via ADL which was triggered only because in the VC++ Standard Library implementation std::vector<int>::iterator is a class type defined within the std namespace (std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>), hence std was an associated namespace to perform a name lookup. This, however, is not guaranteed to work, since a vector iterator could also be a pointer, or live outside of namespace std.

In the second case:

std::random_shuffle<vector<int>::iterator>(intVec.begin(), intVec.end());

the compiler performs a qualified name lookup.

In the third case:

random_shuffle<vector<int>::iterator>(intVec.begin(), intVec.end());

the following bullet applies:

[temp.arg.explicit]/p8:

8 (...) when a function template with explicit template arguments is used, the call does not have the correct syntactic form unless there is a function template with that name visible at the point of the call. If no such name is visible, the call is not syntactically well-formed and argument-dependent lookup does not apply.

In other words, f(a) can trigger ADL, but f<B>(a) -- providing template arguments explicitly to a function call, which is precisely your case -- can not.

This bullet seems to be valid only until C++20.

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160