【问题标题】:Is it direct-initialization or copy-initialization?是直接初始化还是复制初始化?
【发布时间】:2017-10-08 18:54:18
【问题描述】:

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

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

从以下问题: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"};

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

【问题讨论】:

  • =吗?是的:这是复制初始化。否则直接初始化。
  • @nwp 当然,如果你使用大括号,它是*-list-initialization,取决于=。对不起,谢谢!
  • @nwp copy-list-initialization 在技术上是根据 [dcl.init] 中的措辞的一种复制初始化类型,即使它们具有不同的语义(尤其是复制列表初始化类类型不一定依赖于移动/复制构造函数)。
  • 我们还没有一个规范的“初始化东西的所有不同方法以及何时有副本”的问题吗?也许我们也应该制作一个涵盖 C++17 的内容。

标签: c++ c++11 initialization c++14 initializer-list


【解决方案1】:
obj s = obj("value");

这是一个prvalue的直接初始化,然后用于复制初始化变量s。 C++17 的纯右值规则使得 s 的这种事实上的直接初始化。

obj s{"value"};

这是直接-list-初始化。 “列表”部分很重要。任何时候你为了初始化一个对象而应用一个花括号初始化列表,你就是在执行列表初始化。

obj s = {"value"};

这是复制列表初始化。

obj s = obj{"value"};

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

obj s("value");

即直接初始化。

obj s = "value";

那是复制初始化。

先生。 Stroustrup 将这些不同的初始化风格一视同仁。

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

【讨论】:

  • 从技术上讲,direct-initialization 包括直接列表初始化。这就是为什么 L(E)WG 发明了"direct-non-list-initialization" 来描述optional 和朋友们执行的初始化。
  • 非常感谢。请查看我的问题中的 EDIT。我想了解更多关于 direct-initializationdirect-list-initialization 之间的区别 :-)
  • @K.Mulier:去查找“列表初始化”。这就是原则上的区别。
  • 如果我可能再次特别迂腐关于C++17,我会说“这是从“值”直接初始化's',并从prvalue obj(value)"复制's'的初始化。因为据我所知,表达式没有被初始化(它总是对象或引用),但它们可以用作初始化器。如果您能帮我解决这个问题,我会很高兴:'s' 似乎由多个表达式初始化 - 传递。我认为这很合理,但也许仍然存在形式上的问题?
【解决方案2】:

一般:

复制初始化中,右侧被隐式转换为T类型的临时实例,s随后被复制/移动构造。

先生。 Stroustrup 将这些不同的初始化风格一视同仁。

在许多情况下,生成的(优化的)代码确实是完全相同的。允许编译器elide 复制构造(即使它有副作用)。现代编译器远远超出了诸如此类的简单优化,因此您可以有效地依靠这种省略(C++17 中需要)。

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


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

【讨论】:

  • 虽然有区别。对于copy-list-initialization,没有临时的。
  • @Rakete1111 - true 但在 copy-list-initializationdirect-list-initialization 之间仍有一个 difference
  • 非常感谢@RustyX。请看一下我的问题中的编辑。我想更多地了解直接初始化和直接列表初始化之间的区别:-
  • @K.Mulier - 您的 EDIT 更适合作为新问题。这些非常相似,除了列表初始化隐式构造std::initializer_list,如果你有一个接受std::initializer_list,它可以匹配不同的构造函数。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-29
    • 2021-12-19
    • 2010-11-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多