是直接初始化还是复制初始化?
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"};
正如你们中的一些人所指出的,这是有区别的。它们实际上有什么不同?这种差异在非优化编译器的输出中会很明显吗?
总的来说:
T s(...);
或 T s{...};
是 direct-initialization
T s = ...;
是 copy-initialization1
在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
,代码将无法在复制列表初始化情况下编译。
在 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"};
正如你们中的一些人所指出的,这是有区别的。它们实际上有什么不同?这种差异在非优化编译器的输出中会很明显吗?
总的来说:
T s(...);
或T s{...};
是 direct-initializationT s = ...;
是 copy-initialization1
在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
,代码将无法在复制列表初始化情况下编译。