首先,您实际上有两个变量:
T x = { 1, 2, 3 };
T x{1, 2, 3};
这两种初始化方式本质相同,
唯一的区别在于如果选择了explicit
构造函数,则第一种方法无效。否则,它们是完全相同的。第一种称为“复制列表初始化”,第二种称为“直接列表初始化”。
概念是带有
=
的形式赋予一个“复合值”——由3个整数组成的值,并用该值初始化
x
。对于这样的初始化,只允许使用非
explicit
构造函数。对于
x{1, 2, 3}
(没有等号),概念是使用
3个值来初始化变量——概念上不是一个复合值,而是你同时提供的3个单独的值。你可以说这是“构造函数调用”的最通用的意义。
你展示的另一种初始化实际上与上述两种完全不同:
T x({1, 2, 3});
它只调用
T
的构造函数,并使用
{1,2,3}
作为参数。它不会执行任何花哨的操作,例如:如果T是数组,则初始化数组;如果T是聚合结构/类,则初始化结构体成员。如果T不是类,则该声明无效。但如果T恰好具有复制或移动构造函数,则它可以使用该构造函数通过复制列表初始化来构建临时的
T
,并将复制/移动构造函数的引用参数绑定到该临时变量。我相信您在实际编码中不需要经常使用这种形式。
所有这些都记录在初始化程序列表的委员会提案文件中。在本例中,您需要查看
Initializer Lists — Alternative Mechanism and Rationale,第"A programmer's view of initialization kinds"部分:
我们观察到,了解复制初始化和直接初始化之间差异的专业程序员通常会错误地认为前者比后者效率低。(实际上,在两种初始化都有意义时,它们同样有效。)
我们相反地发现,按照不同的术语思考这些问题更加有用:
- 通过调用构造函数来构造(“构造函数调用”)
- 通过传递值来构造(“转换”)
(恰好,前者对应于"直接初始化",后者对应于"复制初始化",但标准术语并不帮助程序员。)
后来,他们发现
Note that since we treat the { ... }
in
X x = { ... };
as a single value, it is not equivalent to
X x{ ... };
where the { ... }
is an argument list for the constructor call (we emphasize it because it is unlike N2531).
在C++0x FDIS中所规定的规则与那篇论文中所述有些许不同,但是该论文所提出的理念被保留并在C++0x FDIS中得到了实现。
T x()
和更令人烦恼的T x(T())
),括号不能用作统一初始化语法的标点符号。花括号没有这个问题。 - James McNellis{}
表示“初始化”,这就是它的设计思想。 - Nicol Bolas