是直接初始化还是复制初始化?

Is it direct-initialization or copy-initialization?

在 C++ 中初始化对象(类 或结构的实例)可以通过多种方式完成。有些语法会引发对象的 直接初始化 ,其他语法会导致 复制初始化 。在编译器中启用 copy-elision 后,两者具有相同的性能。在禁用 copy-elision 的情况下,当您选择后者(复制初始化)时,每个实例化都会有一个额外的 copy/move 构造函数调用。

结论:复制初始化会带来性能损失!

从以下问题:C++11 member initializer list vs in-class initializer? 我可以得出结论,这将是 copy-initialization 语法:

obj s = obj("value");

这将是 直接初始化 语法:

obj s{"value"};


但是这个呢:

obj s = {"value"};

还有这个:

obj s = obj{"value"};

还有这个:

obj s("value");

或者这个:

obj s = "value";

注意
Bjarne Stroustrup 在他的书 "Programming, Principles and Practice Using C++" 第二版第 311 页,§9.4.2:

中比较了一些初始化样式(但不是全部)
struct Date {
    int y,m,d;                     //year, month, day
    Date(int y, int m, int d);     //check for valid date and initialize
    void add_day(int n);           //increase the Date by n days
};

...

Date my_birthday;                   //error: my_birthday not initialized
Date today{12,24,2007};             //oops! run-time error
Date last{2000,12,31};              //OK (colloquial style)
Date next = {2014,2,14};            //also OK (slightly verbose)
Date christmas = Date{1976,12,24};  //also OK (verbose style)

先生Stroustrup 将这些不同的初始化风格同等对待。至少,在我看来是这样。尽管如此,仍然有可能有些是直接初始化,而另一些是复制初始化,因为这些术语在那个时候还没有讨论这本书。


编辑
给定的答案带来了一些有趣的东西。
显然,这是直接初始化:

obj s("value");

这是直接列表初始化:

obj s{"value"};

正如你们中的一些人所指出的,这是有区别的。它们实际上有什么不同?这种差异在非优化编译器的输出中会很明显吗?

总的来说:

copy-initialization中,右侧隐式转换为T类型的临时实例,随后s copy/move-constructed.

Mr. Stroustrup presents these different initialization styles as equal.

在很多情况下,生成(优化)的代码确实是完全一样的。允许编译器 elide 复制构造(即使它有副作用)。现代编译器远远超出了像这个这样的简单优化,所以你可以有效地依赖这个省略(这是 C++17 所必需的)。

复制和直接初始化之间的区别仍然非常重要,因为语义不同;例如,调用声明为 explicit 的构造函数只能在 直接初始化 .

中使用

1 形式 T s = {...};copy-list-initialization 并遵循一些特殊的列表初始化规则。

您可以轻松look up 回答这类问题。也就是说,简单的答案是 = 表示复制初始化。然而,T t={...};copy-list-initialization,它(除非大括号只包含 T 或从它派生的东西)不涉及副本!但是,它确实不允许使用非 explicit 构造函数。

obj s = obj("value");

这是纯右值的直接初始化,然后用于复制初始化变量s。 C++17 的纯右值规则使得 s.

的这种事实上的直接初始化
obj s{"value"};

这是直接-list-初始化。 "list" 部分很重要。任何时候为了初始化对象而应用大括号初始化列表,都在执行列表初始化。

obj s = {"value"};

这是复制列表初始化。

obj s = obj{"value"};

这是纯右值的直接列表初始化,然后用于复制初始化变量s

obj s("value");

即直接初始化

obj s = "value";

那是复制初始化。

Mr. Stroustrup presents these different initialization styles as equal.

他们在做大部分相同的事情的意义上是平等的。但它们在技术上并不平等;复制列表初始化无法调用 explicit 构造函数。因此,如果选择的构造函数是 explicit,代码将无法在复制列表初始化情况下编译。