为什么Foo({})调用Foo(0)而不是Foo()?

pre*_*ous 15 c++ language-lawyer value-initialization list-initialization c++14

由代码中的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)
}
Run Code Online (Sandbox Code Playgroud)

表现为评论.

cppreference:cpp/language/list初始化:

[...]

T( { arg1, arg2, ... } )    (7)

[...]
Run Code Online (Sandbox Code Playgroud)

类型T对象的列表初始化的效果是:

如果T是聚合类型,则执行聚合初始化.

否则,如果braced-init-list为空并且T是具有默认构造函数的类类型,则执行值初始化.

[...]
Run Code Online (Sandbox Code Playgroud)

我总结说Foo({})应该调用默认构造函数.

错误在哪里?

Col*_*mbo 18

默认构造函数仅适用于使用一对大括号的情况:

auto a = Foo();         // Foo()
auto b = Foo{};         // Foo()
Run Code Online (Sandbox Code Playgroud)

Foo({})相反,它只会调用带有空列表作为参数的构造函数,copy-list-initialize选择任何构造函数的参数.[dcl.init]/16:

如果目标类型是(可能是cv限定的)类类型:
- 如果初始化是直接初始化[...],则考虑构造函数.列举了适用的构造函数(13.3.1.3),并通过重载解析(13.3)选择最佳构造函数.调用所选的构造函数来初始化对象,初始化表达式或 表达式列表作为其参数.如果没有构造函数适用,或者重载决策是不明确的,则初始化是错误的.

你有一个参数:空的braced-init-list.有一个列表初始化序列转换{}为,int因此构造函数Foo(int)由重载决策选择.该参数初始化为零,因为它{}意味着一个值初始化,对于标量来说,这意味着零初始化.

cppreferences文档中也没有错误:对于(7),它表示

7)在函数化转换表达式或其他直接初始化中,使用braced-init-list作为构造函数参数

这显然导致与上述引用相同的结果:使用(empty)braced-init-list调用构造函数.

  • +1为了澄清OP,在这种情况下代码相当于`auto g = Foo(int {});`和`int {} == 0`. (6认同)
  • @Hurkyl在这种特殊情况下的预期结果是一个错误,因为`{}`实际上没有一个类型因此根据标准,编译器应该将`auto`视为类型推断的请求,`a`作为label,`=`作为赋值/复制ctor,`{1}`作为没有带文字的类型的东西; 因此,编译器不应该为"a"推导出任何类型,这就是为什么它应该生成错误而不是整数类型的实例.微软在规则中添加了这个例外,我不认为这个例外会更好地使用`{}`. (2认同)