考虑以下:
struct A { A(int, int) { } }; struct B { B(A ) { } // (1) explicit B(int, int ) { } // (2) }; int main() { B paren({1, 2}); // (3) B brace{1, 2}; // (4) }
建设brace
中(4)
清楚明确地调用(2)
.在铛,施工paren
中(3)
明确要求(1)
,其中作为GCC 5.2,它失败编译:
main.cpp: In function 'int main()': main.cpp:11:19: error: call of overloaded 'B()' is ambiguous B paren({1, 2}); ^ main.cpp:6:5: note: candidate: B::B(A) B(A ) { } ^ main.cpp:5:8: note: candidate: constexpr B::B(const B&) struct B { ^ main.cpp:5:8: note: candidate: constexpr B::B(B&&)
哪个编译器是对的?我怀疑clang在这里是正确的,因为gcc中的歧义只能通过涉及隐式构造B{1,2}
并将其传递给复制/移动构造函数的路径产生- 但是该构造函数已被标记explicit
,因此不应允许这种隐式构造.
据我所知,这是一个铿锵的错误.
复制列表初始化具有相当不直观的行为:它将显式构造函数视为可行,直到重载完成完成,但是如果选择了显式构造函数,则可以拒绝重载结果.N4567后草案中的措辞,[over.match.list] p1
在copy-list-initialization中,如果
explicit
选择了构造函数,则初始化是错误的.[ 注意:这与其他情况(13.3.1.3,13.3.1.4)不同,其中只考虑转换构造函数进行复制初始化.此限制仅适用于此初始化是重载解析的最终结果的一部分.- 结束说明 ]
clang HEAD接受以下程序:
#includeusing namespace std; struct String1 { explicit String1(const char*) { cout << "String1\n"; } }; struct String2 { String2(const char*) { cout << "String2\n"; } }; void f1(String1) { cout << "f1(String1)\n"; } void f2(String2) { cout << "f2(String2)\n"; } void f(String1) { cout << "f(String1)\n"; } void f(String2) { cout << "f(String2)\n"; } int main() { //f1( {"asdf"} ); f2( {"asdf"} ); f( {"asdf"} ); }
除了f1
直接来自Bjarne Stroustrup的N2532 - Uniform初始化,第4章之外,这个问题除外.感谢Johannes Schaub向我展示了关于std讨论的这篇论文.
同章包含以下说明:
真正的优点
explicit
是它会f1("asdf")
产生错误.一个问题是重载决策"更喜欢"非explicit
构造函数,以便f("asdf")
调用f(String2)
.我认为解决方案f("asdf")
不够理想,因为作者String2
可能并不意味着解决歧义String2
(至少不是在每种情况下,显式和非显式构造函数都是这样发生的)而作者String1
肯定没有.该规则倾向于不使用的"邋program的程序员"explicit
.
据我所知,N2640 - 初始化列表 - 替代机制和基本原理是最后一篇包含这种超载解决方案的理由; 它的继任者N2672被投票到C++ 11选秀中.
从其"明确的意义"一章:
使示例格式错误的第一种方法是要求所有构造函数(显式和非显式)都被考虑用于隐式转换,但如果显式构造函数最终被选中,则该程序是不正确的.这条规则可能会引入自己的惊喜; 例如:
struct Matrix { explicit Matrix(int n, int n); }; Matrix transpose(Matrix); struct Pixel { Pixel(int row, int col); }; Pixel transpose(Pixel); Pixel p = transpose({x, y}); // Error.第二种方法是在查找隐式转换的可行性时忽略显式构造函数,但在实际选择转换构造函数时包含它们:如果显式构造函数最终被选中,则程序格式错误.这种替代方法允许最后一个(Pixel-vs-Matrix)示例按预期工作(
transpose(Pixel)
被选中),同时使原始示例("X x4 = { 10 };
")格式不正确.
虽然本文提议使用第二种方法,但其措辞似乎存在缺陷 - 在我对措辞的解释中,它不会产生本文理论部分所概述的行为.在N2672中对措辞进行了修订,以使用第一种方法,但我找不到任何有关为何更改的讨论.
当然,在OP中初始化变量时涉及的措辞略多,但考虑到我的答案中第一个示例程序中clang和gcc之间行为的差异是相同的,我认为这涵盖了要点.