基于模板参数推导利用的 STL 算法名称可解析或未定义

STL algorithm name resolvable or undefined based on template argument deduction utilization

鉴于此代码:

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

我意识到这段代码中的名称管理很糟糕。然而,一个不太小的代码库激发了我的问题。 random_shuffle 是可解析的,没有 std 资格。如果合格且明确模板化,则不再有问题。但是,如果没有 std 限定和显式模板参数(至少隐式正确),random_shuffle 突然未定义。为什么?

Visual Studio 2017(我知道...如果这是MS恶作剧,就这么说)

函数调用表达式的非限定名称查找也可能会考虑关联的命名空间,以便找到引用的函数。此功能称为 Argument-dependent lookup。正在搜索的关联命名空间集包括函数调用参数本身类型的命名空间,以及函数调用参数的 class 模板类型的模板参数的命名空间。换句话说,对于函数调用:

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

关联的命名空间集包括 namespace Anamespace C,检查两者以查找函数声明 foo

第一种情况:

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

std::random_shuffle 是通过 ADL 找到的,它被触发只是因为在 VC++ 标准库实现中 std::vector<int>::iterator 是 [=19] 中定义的 class 类型=] 命名空间 (std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>),因此 std 是执行名称查找的关联命名空间。但是,这不能保证有效,因为向量迭代器也可以是指针,或者位于 namespace std.

之外

第二种情况:

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

编译器执行限定名称查找。

第三种情况:

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

以下要点适用:

[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.

换句话说,f(a) 可以触发 ADL,但是 f<B>(a) - 显式向函数调用提供模板参数,这正是你的情况 - 不能。

这个项目符号似乎只在 C++20 之前有效。