使用空大括号初始值设定项的重载解析:指针还是引用?

Overload resolution with an empty brace initializer: pointer or reference?

当我发现下面的代码输出 "pointer".

时,我 运行 陷入了现实生活中的 WTF 时刻
#include <iostream>
#include <utility>

template<typename T>
struct bla
{
    static void f(const T*) { std::cout << "pointer\n"; }
    static void f(const T&) { std::cout << "reference\n"; }
};

int main()
{
    bla<std::pair<int,int>>::f({});
}

std::pair<int,int> 模板参数更改为 int 或任何其他原始类型,会出现(至少对我而言)预期的 "ambiguous overload" 错误。似乎内置类型在这里很特殊,因为任何用户定义的类型(聚合、非平凡、具有默认构造函数等)都会导致调用指针重载。我相信模板不是复制它所必需的,它只是让尝试不同类型变得简单。

就我个人而言,我认为这不合逻辑,我希望在所有情况下都会出现模棱两可的重载错误,无论模板参数如何。 GCC 和 Clang(我相信 MSVC)在 C++11/14/1z 上都不同意我的观点。请注意,我完全清楚 API 这两个重载存在的问题,我永远不会写 something like this, I promise.

所以问题就变成了:这是怎么回事?

哦,这太恶心了。

根据 [over.ics.list]p4 和 p7:

4 Otherwise, if the parameter is a non-aggregate class X and overload resolution per 13.3.1.7 chooses a single best constructor of X to perform the initialization of an object of type X from the argument initializer list, the implicit conversion sequence is a user-defined conversion sequence with the second standard conversion sequence an identity conversion. [...]

[...]

6 Otherwise, if the parameter is a reference, see 13.3.3.1.4. [Note: The rules in this section will apply for initializing the underlying temporary for the reference. -- end note] [...]

[...]

7 Otherwise, if the parameter type is not a class:

[...]

(7.2) -- if the initializer list has no elements, the implicit conversion sequence is the identity conversion. [...]

{} 构建 const std::pair<int,int> 临时文件被认为是用户定义的转换。 const std::pair<int,int> * 纯右值、const int * 纯右值或 const int 临时对象的构造都被视为标准转换。

标准转换优先于用户定义的转换。

您自己发现的 CWG issue 1536 是相关的,但主要针对语言律师。这是措辞上的一个空白,标准并没有真正说明从 {} 初始化参考参数会发生什么,因为 {} 不是表达式。这并不是使一个调用模棱两可而另一个调用模棱两可的原因,并且实现正在设法在这里应用常识。