这两种在 C++11 中初始化变量的方式有什么细微的区别吗?
vector<double> v { 0.0, 1.1, 2.2, 3.3 };
vector<double> v = { 0.0, 1.1, 2.2, 3.3 };
后者可以用于与第一个相同的所有情况吗?
Stroustrup 在TCPL4ED中声称第一种方式是唯一可以在任何情况下使用的方式,因此推荐它。后来,他似乎暗示第二个只是第一个的不同写法。
vector<double> v { 0.0, 1.1, 2.2, 3.3 };
是直接列表初始化。这意味着它是使用带有初始化列表的构造函数进行初始化的。
构造函数:
vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );
vector<double> v = { 0.0, 1.1, 2.2, 3.3 };
是一个复制列表初始化。
标准很明确:
8.5.4 列表初始化[dcl.init.list]
列表初始化是从一个花括号初始化列表初始化一个对象或引用。这样的初始化器称为初始化器列表,列表中以逗号分隔的初始化器子句称为初始化器列表的元素。初始化列表可能为空。列表初始化可以发生在直接初始化或复制初始化上下文中;直接初始化上下文中的列表初始化称为直接列表初始化,而复制初始化上下文中的列表初始化称为复制列表初始化。[注意:可以使用列表初始化:
- 作为变量定义中的初始化器
[...]
例子 :
std::complex<double> z{1,2}; [...] std::map<std::string,int> anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} };
对于两者之间的区别,我们应该更进一步:
13.3.1.7 通过列表初始化 [over.match.list] 进行初始化
- 对于直接列表初始化,候选函数是类 T 的所有构造函数。
- 对于copy-list-initialization,候选函数是 T 的所有构造函数。 但是,如果
explicit
选择了构造函数,则初始化格式不正确。[注意:此限制仅适用于此初始化是重载解决的最终结果的一部分-结束注释]
统一初始化的目的是(部分)消除这两种结构之间的差异。以确保它们具有相同的功能。
可悲的是,他们失败了。直接列表初始化(即:)和复制列表初始化(即:)之间只有一个区别。您不需要用于复制列表初始化的复制/移动构造函数(尽管有名称);第 8.5.4 节没有将其列为要求。没有可能被忽略的临时构造的概念。它们具有相同的行为,除了:T t{...}
T t = {...}
如果选择explicit
构造函数,则复制列表初始化将失败。这是唯一的区别。
很难从规范中引用,因为只有 3 次提到了复制列表初始化是什么。一个是在 8.5.4 中,它定义复制列表初始化是列表初始化的一种形式,另一个声明返回一个花括号初始化列表使用复制列表初始化。最后一个在 13.3.1.7 中,它声明了上述异常:
在复制列表初始化中,如果
explicit
选择了构造函数,则初始化格式错误。
所以这是唯一的区别。
想到的一个区别是,即使声明了构造函数,也可以使用第一种形式,explicit
而第二种形式则不能。抱歉,@Timothy Shields 和 @Tomek 错了,这两个语句都是直接初始化,而不是复制初始化。
我猜这类似于普通构造函数的情况(即不使用初始化列表)。第一种选择是使用(在这种情况下)构造函数采用初始化列表构造函数的构造。另一种选择是使用初始化列表构造函数创建的临时变量,然后从该变量中复制构造变量。暂时的然后消失。请注意,后者通常会在 (N)RVO 启动时跳过临时和复制破坏,但后者要求您的类在定义时具有可访问的复制构造函数。至少这是我从 C++03 中记得的,它可能在 C++11 中发生了变化。