当我发现下面的代码输出“指针”时,我遇到了真实的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
或任何其他基本类型,会给(至少对我来说)预期的“模糊过载”错误。似乎内置类型在这里是特殊的,因为任何用户定义的类型(聚合,非平凡,具有默认构造函数等)都会导致调用指针超载。我相信模板不是重现它的必要条件,只是简单地尝试不同的类型。
就我个人而言,我不认为这是合乎逻辑的,无论模板参数如何,我都希望在所有情况下出现模糊的重载错误。 GCC和Clang(我相信MSVC)在C ++ 11/14 / 1z中都不同意我的观点。注意我完全知道这两个重载存在的错误的API,我永远不会写something like this, I promise。
所以问题变成了:发生了什么?
答案 0 :(得分:5)
哦,这很讨厌。
Per [over.ics.list] p4和p7:
4否则,如果参数是非聚合类
X
,并且每个13.3.1.7的重载决策选择X
的单个最佳构造函数来执行类型{{1}的对象的初始化从参数初始化列表中,隐式转换序列是用户定义的转换序列,第二个标准转换序列是标识转换。 [...][...]
6否则,如果参数是参考,请参见13.3.3.1.4。 [注意:本节中的规则适用于初始化参考的基础临时值。 - 结束说明] [...]
[...]
7否则,如果参数类型不是类:
[...]
(7.2) - 如果初始化列表没有元素,则隐式转换序列是标识转换。 [...]
从X
构建const std::pair<int,int>
临时文件被视为用户定义的转化。构造{}
prvalue,const std::pair<int,int> *
prvalue或const int *
临时对象都被视为标准转化。
标准转化优先于用户定义的转化。
您自己找到的CWG issue 1536是相关的,但主要针对语言律师。它是措辞上的一个空白,标准并没有真正说明从const int
初始化参考参数会发生什么,因为{}
不是表达式。它并不是什么使得这个调用模糊不清而另一个不是,并且实现在这里设法运用常识。