5

初始化自动管理的简单变量时有几种不同的风格,而不是通过赋值。我想知道是否有任何具体的理由偏爱一个而不是另一个,或者这只是风格问题。

使用括号很有吸引力,因为它感觉类似于实例化一个对象

double answer(42.0);
ComplexNumber i(0,1);

虽然使用大括号很吸引人,因为它感觉类似于初始化容器

double answer{42};
std::vector<double> i{0,1};
double i2[] = {0,1};

有什么特别的理由偏爱一种风格而不是另一种风格吗?

4

3 回答 3

4

看这里:GotW #1:变量初始化。这是对 H. Sutter 回答的详细描述。

H. Sutter 在上面谈到了变量初始化的新旧风格的一般意义。

根据您的起始主题上下文,下面显示了文章的简要概要。


这对

double answer(42.0); // (1)
double answer{42};  // (2)

实际上,类似于下一个初始化示例:

widget w(x);                // (d)
widget w{x};                // (e)

这些都是直接初始化。但是,请注意,该语法{x}会创建一个initializer_list. 如果widget有一个带有 的构造函数,initializer_list则首选该构造函数;否则,如果widget有一个采用任何类型的构造函数x(可能带有转换),则使用该构造函数。

有两个主要区别使得 (2, e) 优于 (1, d):

  • 首先,语法 (2, e) 是明确的并且避免了“令人烦恼的解析”。如果x是类型名称,则 (1, d) 是函数声明,即使x在范围内还有一个变量(见上文),而 (2, e) 绝不是函数声明。
  • 其次,语法 (2, e) 更安全,因为它不允许缩小(也称为“有损”)转换,否则某些内置类型会允许这种转换。考虑:

    int i1( 12.345 ); // ok: toss .345, we didn't like it anyway

    int i2{ 12.345 }; // error: would be lossy implicit narrowing

下一对

ComplexNumber i(0,1); // (3)
std::vector<double> i{0,1}; // (4)

与复杂对象的初始化有关。两者看起来完全一样,但第二个帮助我们避免“恼人的解析”,比如:

ComplexNumber       w( real(), img() );       // oops, vexing parse 

除此之外,这种方式使代码更清晰(如果我们使用initializer_list,则更清楚的是初始化),此外,在某些情况下还可以减轻语法,例如:

draw_rect({ origin, selection });                  // C++11

Sutter 的准则是:更喜欢使用 初始化{ },例如vector<int> v = { 1, 2, 3, 4 };or auto v = vector<int>{ 1, 2, 3, 4 };,因为它更一致、更正确,并且完全避免了了解旧式陷阱。在您希望只看到符号的单参数情况下=,例如int i = 42; 和auto x = anything; 省略括号很好。

我们可以使用 () 初始化来显式调用特殊构造函数。

于 2013-06-14T16:12:00.073 回答
2

除了Boris回答之外,还有另一个陷阱可以避免,Herb 的博客文章中没有提到这一点。

以下代码是非法的:

#include <initializer_list>

int main() {
  auto i{42}; // not the same as 'auto i(42);'
  ++i;
}

gcc 4.7.2 引发的错误消息是:

错误:'++i' 中的 'operator++' 不匹配

原因是auto推断出的类型比许多人想象的{42}要多。没有for ,因此有错误。如果替换为,则代码可以正常编译。std::inilializer_list<int>intoperator ++std::initializer_list<int>{42}(42)

于 2013-06-14T16:41:20.343 回答
1

GotW #93有一个非常有趣的理由来声明一切auto

于 2013-06-14T16:39:13.567 回答