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

时间:2017-06-15 17:37:45

标签: c++ c++11 initializer-list overload-resolution

当我发现下面的代码输出“指针”时,我遇到了真实的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

所以问题变成了:发生了什么?

1 个答案:

答案 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初始化参考参数会发生什么,因为{}不是表达式。它并不是什么使得这个调用模糊不清而另一个不是,并且实现在这里设法运用常识。