花括号初始化是C++11引入的一种初始化方法。
T object { arg1, arg2, ... };
T { arg1, arg2, ... };
new T { arg1, arg2, ... }
class Class { T member { arg1, arg2, ... }; };
Class::Class() : member{arg1, arg2, ...} {... }
T object = {arg1, arg2, ...};
function( { arg1, arg2, ... } ) ;
return { arg1, arg2, ... } ;
object[ { arg1, arg2, ... } ] ;
object = { arg1, arg2, ... } ;
U( { arg1, arg2, ... } )
class Class { T member = { arg1, arg2, ... }; };
使用花括号初始化时,编译必须加上--std=c++11选项。
C++的复杂之处在这里体现的淋漓尽致,当编译器遇到花括号时,规则见
http://en.cppreference.com/w/cpp/language/list_initialization
规则很长。除去那些简单的情况,约有三种情况,依次优先:
当类型是一个聚合结构( aggragates )时,花括号将进行聚合初始化。
聚合结构是指一个数组( array )或者一个满足下面条件的结果:
聚合结构可以简单理解为 c 语言里的普通结构。聚合初始化时,结构用花括号里的数据依次填充成员变量。当然,具体的规则要复杂很多,详情可见:
http://en.cppreference.com/w/cpp/language/aggregate_initialization
例子:
struct S {
int x;
struct Foo {
int i;
int j;
int a[3];
} b;
};
int main()
{
S s1 = { 1, { 2, 3, {4, 5, 6} } };
S s2 = { 1, 2, 3, 4, 5, 6}; // same, but with brace elision
S s3{1, {2, 3, {4, 5, 6} } }; // same, using direct-list-initialization syntax
}
这里指 C++编译器遇到花括号列表时,将其作为一个std::initializer_list的对象,当初始化对象存在接受std::initializer_list为参数的构造函数时,编译器将调用该构造函数直接进行初始化。
std::initializer_list的文档:
很多std容器如vector、map、set等都有接受std::initializer_list的构造函数,因此可以像普通数组一样使用花括号列表初始化。
当上面两种情况不存在时,编译器将依次匹配初始化对象的所有初始化函数,调取其中参数匹配的。如下例:
std::vector<std::string> strs1{100, "abc"}; // it's the same with below
std::vector<std::string> strs2(100, "abc");
还有个特殊的情况,是匹配构造函数参数和上面的初始化列表初始化的的结合。比如下面这种写法:
std::vector<int> x{{1, 2}};
注意它的结果和下面一样,但没有歧义了。因为下面这个容易被误解为调用std::vector<int>(1, 2)的构造函数:
std::vector<int> x{1, 2};
编译器遇到最外层的花括号时,开始匹配构造函数参数,内层的花括号表示参数为初始化列表。
上面谈到的三种情况都是直接初始化( direct initialized ),对应的是上面第一部分的直接初始化的语法。而对于第一部分中的复制初始化的语法,将采取复制初始化。
字面上而言,复制初始化将先使用上面三种直接初始化方法之一初始化一个临时变量,然后调用copy或move函数将临时对象复制到需初始化的对象上。
但实际操作中,编译器会使用一种叫做copy elision的技术。简而言之就是已知临时变量必然没有用处,那我也不需要建立临时变量,直接用直接初始化方法好了。
因此,在类似std::vector<int> x = {1, 2, 3}这样的语法中,最后生成的二进制代码将和std::vector<int> x{1, 2, 3}一致,它们的效率一模一样。
但复制初始化和直接初始化在编译期还是有区别,区别在于编译器会检查复制构造函数的存在性和可访问性,虽然编译器最后并没有用到它。
比如std::atomic禁用了复制构造函数(一般设置为=delete或者把复制构造函数设置为 private ),下面这种初始化就无法编译:
std::atomic<int> x = 1; // not valid because copy constructor not exist
你必须使用括号或者花括号:
std::atomic<int> x1(1); // valid
std::atomic<int> x2{1}; // valid, the same as x2(1);
我个人坚持的习惯有两个: