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

时间:2014-11-06 20:02:48

标签: c++ language-lawyer c++14 list-initialization value-initialization

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是聚合类型,则执行聚合初始化。

     

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

[...]

我得出结论,Foo({})应该调用默认构造函数。

错误在哪里?

1 个答案:

答案 0 :(得分:18)

默认构造函数仅在您使用一对大括号时才适用:

auto a = Foo();         // Foo()
auto b = Foo{};         // Foo()

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

  

如果目标类型是(可能是cv限定的)类类型:
- 如果   初始化是直接初始化[...]构造函数被考虑。适用的构造函数   列举(13.3.1.3),并通过过载选择最好的一个   决议(13.3)。 如此选择的构造函数被调用   使用初始化表达式或初始化对象    expression-list 作为其参数。如果没有构造函数适用,或者   重载决策是模糊的,初始化是   不良形成。

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

cppreferences文档中也没有错误:对于(7),声明

  

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

这显然会产生与上述引用相同的结果:使用(empty)braced-init-list调用构造函数。