15

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 初始化

[...]

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

[...]

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

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

否则,如果括号初始化列表为空并且T是具有默认构造函数的类类型,则执行值初始化。

[...]

我的结论是Foo({})应该调用默认构造函数。

错误在哪里?

4

1 回答 1

18

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

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

Foo({})相反,只会以空列表作为参数调用构造函数,复制列表初始化选择的任何构造函数的参数。[dcl.init]/16:

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

你有一个论点:空的括号初始化列表。有一个列表初始化序列转换{}为,int因此构造函数Foo(int)由重载决议选择。参数被初始化为零,这{}意味着值初始化,对于标量,意味着零初始化

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

7) 在函数转换表达式或其他直接初始化中,使用花括号初始化列表作为构造函数参数

这显然会导致与上面引用相同的结果:构造函数是用(空的)braced-init-list 调用的。

于 2014-11-06T20:13:11.343 回答