Ext*_*t3h 10 c++ overload-resolution c++11 list-initialization
以下代码成功编译了大多数现代C++ 11兼容编译器(GCC> = 5.x,Clang,ICC,MSVC).
#include <string>
struct A
{
explicit A(const char *) {}
A(std::string) {}
};
struct B
{
B(A) {}
B(B &) = delete;
};
int main( void )
{
B b1({{{"test"}}});
}
Run Code Online (Sandbox Code Playgroud)
但是为什么它首先编译,以及列出的编译器如何解释该代码?
为什么MSVC能够在没有的情况下编译它B(B &) = delete;,但其他3个编译器都需要它?
为什么在删除复制构造函数的不同签名时,除了MSVC之外的所有编译器都会失败,例如B(const B &) = delete;?
编译器甚至都选择相同的构造函数吗?
为什么Clang会发出以下警告?
17 : <source>:17:16: warning: braces around scalar initializer [-Wbraced-scalar-init]
B b1({{{"test"}}});
Run Code Online (Sandbox Code Playgroud)
我将尝试解释标准所说的内容,而不是解释编译器的行为.
直接初始化b1的{{{"test"}}},重载适用于选择最佳的构造B.因为从没有隐式转换{{{"test"}}}到B&(列表初始化是不是一个左值),构造B(B&)是不可行的.然后我们专注于构造函数B(A),并检查它是否可行.
确定从隐式转换序列{{{"test"}}}来A(我会用符号{{{"test"}}}- > A为简单起见),重载适用于选择最佳的构造函数A,所以我们需要比较{{"test"}}- > const char*和{{"test"}}- > std::string(注意括号的最外层会被省略)根据[over.match.list]/1:
当非聚合类类型T的对象被列表初始化,使得[dcl.init.list]指定根据本子条款中的规则执行重载解析时,重载决策分两个阶段选择构造函数:
最初,候选函数是类T的初始化列表构造函数([dcl.init.list])...
如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数是类T的所有构造函数,参数列表由初始化列表的元素组成.
...在复制列表初始化中,如果选择了显式构造函数,则初始化是错误的.
注意,无论说明符如何,都会考虑所有构造函数explicit.
{{"test"}}- > const char*根据[over.ics.list]/10和[over.ics.list]/11不存在:
否则,如果参数类型不是类:
如果初始化列表有一个元素本身不是初始化列表 ...
如果初始化列表没有元素......
在除上述列举之外的所有情况下,都不可能进行转换.
要确定{{"test"}}- > std::string,将采用相同的过程,并且重载决策选择std::string带有类型参数的构造函数const char*.
结果,{{{"test"}}}- > A通过选择构造函数完成A(std::string).
explicit被删除怎么办?这个过程不会改变.GCC将选择构造函数,A(const char*)而Clang将选择构造函数A(std::string).我认为这是GCC的一个错误.
b1怎么办?注意{{"test"}}- > const char*不存在但是{"test"}- > const char*存在.因此,如果初始化器中只有两层大括号,则选择b1构造函数A(const char*),因为{"test"}- > const char*优于{"test"}- > std::string.其结果,一个显式的构造被选择在复制列表初始化(参数的初始化A在构造B(A)从{"test"}),然后是形成不良的节目.
B(const B&)被声明怎么办?请注意,如果B(B&)删除声明,也会发生这种情况.这次我们需要比较{{{"test"}}}- > A和{{{"test"}}}- > const B&,或{{{"test"}}}- > const B等效.
为了确定{{{"test"}}}- > const B,采用上述过程.我们需要比较{{"test"}}- > A和{{"test"}}- > const B&.注意{{"test"}}- > const B&根据[over.best.ics]/4不存在:
但是,如果目标是
- 构造函数的第一个参数或
- 用户定义的转换函数的隐式对象参数
并且构造函数或用户定义的转换函数是候选者
- [over.match.ctor],当参数是类复制初始化的第二步中的临时参数时,
- [over.match.copy],[over.match.conv]或[over.match.ref](在所有情况下),或者
- [over.match.list]的第二阶段,当初始化列表只有一个元素本身就是一个初始化列表时,目标是类X的构造函数的第一个参数,转换是X或引用cv X,
不考虑用户定义的转换序列.
为了确定{{"test"}}- > A,再次采用上述过程.这与我们在前一小节中谈到的情况几乎相同.结果,A(const char*)选择了构造函数.请注意,此处选择构造函数来确定{{{"test"}}}- > const B,并且实际上不适用.尽管构造函数是显式的,但这是允许的.
结果,{{{"test"}}}- > const B通过选择构造函数B(A),然后选择构造函数来完成A(const char*).现在,{{{"test"}}}- > A和{{{"test"}}}- > const B都是用户定义的转换序列,两者都不比另一个好,所以初始化b1是不明确的.
根据前面小节中引用的[over.best.ics]/4,不考虑用户定义的转换{{{"test"}}}- > const B&.因此,即使B(const B&)声明了构造函数,结果也与主要示例相同.
| 归档时间: |
|
| 查看次数: |
1130 次 |
| 最近记录: |