clang 3.5.0和gcc 4.9.1从代码生成的可执行文件
#include <iostream>
struct Foo
{
Foo() { std::cout << "Foo()" << std::endl; }
Foo(int x) { std::cout << "Foo(int = " << x << ")" << std::endl; }
Foo(int x, int y) { std::cout << "Foo(int = " << x << ", int = " << y << ")" << std::endl; }
};
int main() // Output
{ // ---------------------
auto a = Foo(); // Foo()
auto b = Foo(1); // Foo(int = 1)
auto c = Foo(2, 3); // Foo(int = 2, int = 3)
auto d = Foo{}; // Foo()
auto e = Foo{1}; // Foo(int = 1)
auto f = Foo{2, 3}; // Foo(int = 2, int = 3)
auto g = Foo({}); // Foo(int = 0) <<< Why?
auto h = Foo({1}); // Foo(int = 1)
auto i = Foo({2, 3}); // Foo(int = 2, int = 3)
}
按照评论行事。
来自cppreference:cpp/language/list initialization:
[...] T( { arg1, arg2, ... } ) (7) [...]
T类型对象的列表初始化的效果是:
如果
T
是聚合类型,则执行聚合初始化。否则,如果支撑的init列表为空,并且
T
是具有默认构造函数的类类型,则执行值初始化。[...]
我得出的结论是Foo({})
应该调用默认构造函数。
虫子在哪里?
默认构造函数仅适用于使用一对大括号的情况:
auto a = Foo(); // Foo()
auto b = Foo{}; // Foo()
Foo({})
将只调用以空列表为参数的构造函数,复制列表初始化所选构造函数的参数。[dcl.init]/16:
如果目标类型是(可能是cv限定的)类类型:
--如果初始化是直接初始化[…]构造函数。适用的构造函数列举(13.3.1.3),通过过载选择最佳resolution(13.3)。如此选择的构造函数被调用初始化对象,使用初始值设定项表达式或表达式列表作为其参数如果没有构造函数应用,或者重载解析不明确,初始化为不正规。
您有一个参数:空的支撑init列表。有一个将{}
转换为int
的列表初始化序列,因此通过重载解析来选择构造函数Foo(int)
。参数被初始化为零,因为{}
意味着值初始化,对于标量,这意味着零初始化。
cpprreferences文档中也没有错误:对于(7),它指出
7) 在函数强制转换表达式或其他直接初始化中,将支持的init列表用作构造函数参数
这显然导致了与上面引用的结果相同的结果:构造函数是用(空)支撑的init列表调用的。