13

这两种在 C++11 中初始化变量的方式有什么细微的区别吗?

  1. vector<double> v { 0.0, 1.1, 2.2, 3.3 };

  2. vector<double> v = { 0.0, 1.1, 2.2, 3.3 };

后者可以用于与第一个相同的所有情况吗?

Stroustrup 在TCPL4ED中声称第一种方式是唯一可以在任何情况下使用的方式,因此推荐它。后来,他似乎暗示第二个只是第一个的不同写法。

4

4 回答 4

8

1.

vector<double> v { 0.0, 1.1, 2.2, 3.3 };

直接列表初始化。这意味着它是使用带有初始化列表的构造函数进行初始化的。

构造函数:

vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );

2.

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选择了构造函数,则初始化格式不正确。[注意:此限制仅适用于此初始化是重载解决的最终结果的一部分-结束注释]
于 2013-08-13T17:57:43.030 回答
5

统一初始化的目的是(部分)消除这两种结构之间的差异。以确保它们具有相同的功能。

可悲的是,他们失败了。直接列表初始化(即:)和复制列表初始化(即:)之间只有一个区别。您不需要用于复制列表初始化的复制/移动构造函数(尽管有名称);第 8.5.4 节没有将其列为要求。没有可能被忽略的临时构造的概念。它们具有相同的行为,除了T t{...}T t = {...}

如果选择explicit构造函数,则复制列表初始化将失败。这是唯一的区别。

很难从规范中引用,因为只有 3 次提到了复制列表初始化是什么。一个是在 8.5.4 中,它定义复制列表初始化是列表初始化的一种形式,另一个声明返回一个花括号初始化列表使用复制列表初始化。最后一个在 13.3.1.7 中,它声明了上述异常:

在复制列表初始化中,如果explicit选择了构造函数,则初始化格式错误。

所以这是唯一的区别。

于 2013-08-13T18:10:17.880 回答
1

想到的一个区别是,即使声明了构造函数,也可以使用第一种形式,explicit而第二种形式则不能。抱歉,@Timothy Shields 和 @Tomek 错了,这两个语句都是直接初始化,而不是复制初始化。

于 2013-08-13T18:17:33.917 回答
0

我猜这类似于普通构造函数的情况(即不使用初始化列表)。第一种选择是使用(在这种情况下)构造函数采用初始化列表构造函数的构造。另一种选择是使用初始化列表构造函数创建的临时变量,然后从该变量中复制构造变量。暂时的然后消失。请注意,后者通常会在 (N)RVO 启动时跳过临时和复制破坏,但后者要求您的类在定义时具有可访问的复制构造函数。至少这是我从 C++03 中记得的,它可能在 C++11 中发生了变化。

于 2013-08-13T17:19:45.643 回答