Joh*_*itb 21 c++ overload-resolution c++11 list-initialization
我有这个代码
struct A { A(); A(A&); };
struct B { B(const A&); };
void f(A);
void f(B);
int main() {
f(A());
}
Run Code Online (Sandbox Code Playgroud)
令我惊讶的是,GCC和Clang失败了.例如,Clang说
Compilation finished with errors:
source.cpp:8:10: error: no matching constructor for initialization of 'A'
f(A());
^~~
source.cpp:1:21: note: candidate constructor not viable: expects an l-value for 1st argument
struct A { A(); A(A&); };
^
source.cpp:1:16: note: candidate constructor not viable: requires 0 arguments, but 1 was provided
struct A { A(); A(A&); };
^
source.cpp:4:13: note: passing argument to parameter here
void f(A);
Run Code Online (Sandbox Code Playgroud)
为什么他们选择第一个f,当第二个f工作正常?如果我删除第一个f,则呼叫成功.对我来说更奇怪的是,如果我使用大括号初始化,它也可以正常工作
int main() {
f({A()});
}
Run Code Online (Sandbox Code Playgroud)
他们都叫第二个f.
CB *_*ley 17
这是一种语言怪癖.第一个f匹配更好,因为您A不需要转换来匹配参数类型(A),但是当编译器尝试进行调用时,没有找到合适的拷贝构造函数会导致调用失败.该语言不允许在执行重载决策步骤时考虑实际调用的可行性.
最接近的标准报价ISO/IEC 14882:2011 13.3.3.1.2用户定义的转换序列[over.ics.user]:
将类类型的表达式转换为相同的类类型给出了精确匹配等级,并且将类类型的表达式转换为该类型的基类转换等级,尽管存在复制/移动的事实为这些情况调用构造函数(即,用户定义的转换函数).
对于列表初始化的情况,您可能需要查看:13.3.3.1.2用户定义的转换序列[over.ics.user]
当非聚合类类型T的对象被列表初始化(8.5.4)时,重载决策分两个阶段选择构造函数:
- 最初,候选函数是类T的初始化列表构造函数(8.5.4),参数列表由初始化列表作为单个参数组成.
- 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,参数列表由初始化列表的元素组成.
由于重载有看可行contructors在每一种情况下f(A)和f(B)它必须拒绝seqence试图绑定A()到A(A&),但B(const A&)仍然是可行的.