2017-10-08 83 views
0

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

结论:复制初始化可能会有性能损失!

从以下问题:C++11 member initializer list vs in-class initializer?我可以断定,这将是副本初始化语法:

obj s = obj("value"); 

,这将是直接初始化语法:

obj s{"value"}; 

 
但是这个怎么样:

obj s = {"value"}; 

这一个:

obj s = obj{"value"}; 

这一个:

obj s("value"); 

或者这一个:

obj s = "value"; 

注意
了Bjarne Stroustrup的在他的著作 “编程,原理与实践使用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) 

斯特劳斯先生介绍了这些不同的初始化风格相等。至少,这就是它对我的看法。尽管如此,仍有可能有一些是直接初始化和其他复制初始化,因为这些条款尚未在书中讨论。


编辑
给出的答案带来了一些有趣的事情。
显然,这是直接初始化

obj s("value"); 

这是直接列表初始化

obj s{"value"}; 

你们有些人指出,是有区别的。他们实际上以何种方式不同?在非优化编译器的输出中,差异是否会显着?

+0

有没有'='?是的:这是复制初始化。否则是直接初始化。 – Rakete1111

+0

@ Rakete1111 [这似乎不是真的](http://coliru.stacked-crooked.com/a/18e4e0f6db8a9bfc)。 – nwp

+0

@nwp当然,如果你使用大括号,它是* -list-initialization,取决于'='。对不起,谢谢! – Rakete1111

回答

3
obj s = obj("value"); 

这是一个prvalue,然后将其用于复制初始化变量s的直接初始化。 C++ 17的prvalue规则使这个事实上的直接初始化为s

obj s{"value"}; 

这是指示按列表 -initialization。 “列表”部分很重要。任何时候,为了初始化对象而应用braced-init-list,您都在执行列表初始化。

obj s = {"value"}; 

这是副本列表初始化。

obj s = obj{"value"}; 

这是一个prvalue,然后将其用于复制初始化变量s的直接清单初始化。

obj s("value"); 

即直接初始化。

obj s = "value"; 

这是拷贝初始化。

Stroustrup先生将这些不同的初始化样式显示为相等。

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

+1

从技术上讲,[direct-initialization](https://wg21.link/dcl.init#16)包括直接列表初始化。这就是为什么L(E)WG发明[“direct-non-list-initialization”](http://eel.is/c++draft/defns.direct-non-list-init)来描述由'可选“和朋友。 –

+0

非常感谢。请在我的问题中查看**编辑**。我想知道更多关于**直接初始化**和**直接列表初始化**之间的区别** :-) –

+0

@ K.Mulier:查看“列表初始化”。这是主要区别。 –

2

一般来说:

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

Mr. Stroustrup先生将这些不同的初始化样式表示为相等。

在许多情况下,生成(优化)的代码确实是完全一样的。编译器允许复制构造elide(即使它有副作用)。现代编译器不仅仅是简单的优化,比如这个,所以你可以有效地指望这个elision(这在C++ 17中是必需的)。

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


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

+0

虽然有区别。对于复制列表初始化,没有临时的。 – Rakete1111

+1

@ Rakete1111 - true,但在* copy-list-initialization *和* direct-list-initialization之间仍然存在[difference](http://eel.is/c++draft/dcl.init#list-3.2) *。 – rustyx

+0

非常感谢@RustyX。请在我的问题中看看编辑。我想知道更多关于直接初始化和直接列表初始化之间的区别: - –

2

您可以很容易地look up这些问题的答案。也就是说,简单的答案是=意味着复制初始化。然而,T t={...};复制列表初始化,其中(除非大括号只包含一个T或从中派生的东西)不涉及副本!但是,它确实不允许使用非构造函数。