为什么编译器没有给出模糊的引用错误?

Cur*_*ous 24 c++ gcc templates clang c++11

参考以下代码

#include <iostream>
#include <tuple>
#include <string>
#include <type_traits>

using std::cout;
using std::endl;
using std::string;

template <typename... Args>
void bar(Args&&...) {}

int change(const string&) { return 1; }
double change(int) { return 1.0; }

int main() {
    // bar(1, 2.0, static_cast<int(*)(const string&)>(&change));
    bar(1, 2.0, &change);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我理解上面代码中的错误是对change函数的引用是不明确的(这就是注释行的工作原理),但是为什么编译器会给出这个错误消息呢?

test.cpp:17:5: error: no matching function for call to 'bar'
    bar(1, 2.0, &change);
    ^~~
test.cpp:11:6: note: candidate function not viable: requires 2 arguments, but 3 were
      provided
void bar(Args&&...) {}
     ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

在gcc(> 5)和clang(Apple LLVM version 8.0.0 (clang-800.0.42.1))上都会发生这种情况

我只是好奇为什么两个编译器不只是说引用是模糊的.我觉得它与模板实例化在C++中的工作方式有关,但我不确定其确切原因.

rus*_*tyx 7

我认为编译器是正确的,但它可能是奇怪的.模板参数推导规则与替换规则不同.模板参数包上下文中重载函数解析的模糊性并不一定意味着失败.

[temp.deduct.call]/p6:

当P是函数类型,函数指针类型或指向成员函数类型的指针时:

...

- 如果参数是重载集(不包含函数模板),则尝试使用集合中的每个成员进行试验参数推导.如果仅对其中一个重载集成员进行推导成功,则该成员将用作推导的参数值.如果对重载集的多个成员进行推导成功,则将该参数视为非推导上下文.

因此,对于参数包的最后一个参数,我们处于非推导的上下文中(不是错误).

并且[temp.arg.explicit]/p3:

...未以其他方式推导出的尾随模板参数包将被推导为空的模板参数序列....

所以在我看来(尽管最后一点并没有明确地说明部分推断出的参数包),模糊函数指针在推导阶段被简单地抛弃,随后替换失败,因为它试图将3个参数替换为推导出的2参数函数.当它需要解决歧义时,它永远不会达到某种程度.