使用C++11的成员初始化列表中的初始化器列表语法

5

我一直在学习 'C++之旅',Bjarne 在构造函数中使用了 c++11 的初始化列表特性来进行成员初始化,如下所示(使用花括号):

A a;
B b;
Foo(Bar bar):
  a{bar.a}, b{bar.b}
{}

然而,在 C++11 之前,这并不能编译。与旧的成员初始化列表(使用圆括号)有何区别:
Foo(Bar bar):
  a(bar.a), b(bar.b)
{}

那么这两者有什么区别,何时应该优先选择其中之一?

4
"A a{bar.a};" 和 "A a(bar.a);" 的区别为何? - chris
1
Bjarne只是喜欢花括号初始化而已。 - AndyG
1
花括号避免了“最令人烦恼的解析”问题,但引入了答案中指出的与std::vector相关的烦人问题,请参见http://en.wikipedia.org/wiki/C%2B%2B11#Uniform_initialization。 - amdn
3个回答

10

那么它们有什么区别呢?

圆括号只适用于非类类型,或者具有与括号中参数数量相适应的构造函数的类型。

花括号适用于这些情况,也适用于聚合体 - 没有构造函数的简单struct或数组类型。 因此,以下内容是可行的:

struct {
    int a,b;
} aggregate;
int array[2];

Foo() : aggregate{1,2}, array{3,4} {}

最后,花括号将匹配一个取适当类型的initializer_list参数的构造函数,而不是带有参数以匹配参数的构造函数。例如:

std::vector<int> v1;
std::vector<int> v2;

Foo() :
    v1(10,2),   // 10 elements with value 2
    v2{10,2}    // 2 elements with value 10,2
{}
什么情况下应该优先选择其中一个?
如果您想要更清晰地表明初始化使用的是构造函数而不是聚合或initializer_list;或者强制使用特定的构造函数,请使用圆括号。
当您需要一种不受支持的初始化形式时,或者当您只想让初始化“做正确的事情”时,请使用大括号。
在两者都能实现相同功能的情况下,选择很大程度上是审美方面的。

3
请注意,列表初始化也禁止缩窄转换。 - Casey

4

在一些非常烦人的边缘情况下,可能会出现一些差异:

std::vector<int> v{3, 2}; // constructs a vector containing [3, 2]
std::vector<int> u(3, 2); // constructs a vector containing [2, 2, 2]

无论vu是函数中的变量还是在初始化列表中初始化的类成员,这都是正确的。

但是,在没有std::initializer_list<T>构造函数与接受相同数量参数的普通构造函数重叠的情况下,它们没有区别。


1
简述:成员初始化列表中的符号与其他地方初始化的变量匹配。但是,它所做的描述并不容易,因为与构造函数调用使用大括号有两个相互冲突的变化:
  1. 统一初始化语法打算使所有构造都使用大括号,并调用相应的构造函数,即使它是默认参数或该类型根本没有构造函数并使用直接初始化。
  2. 为支持可变数量的参数,可以使用大括号提供std :: initializer_list 而不需要额外的括号/大括号。 如果存在一个采用std :: initializer_list (对于适当的类型T)的构造函数,则在使用大括号时使用此构造函数。
换句话说,如果没有std :: initializer_list 构造函数,但是有其他用户定义的构造函数,则使用括号和大括号是等效的。 否则,将调用std :: initializer_list 构造函数…我想,我会漏掉一些细节,因为整个初始化实际上非常复杂。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接