C++ 中的对象初始化语法( T obj = {...} 与 T obj{...} )

Object Initialization Syntax in C++ ( T obj = {...} vs T obj{...} )

T obj = {…}T obj{…} 这两种初始化形式有什么区别?
我最初认为 T obj = {…} 对于 T obj = T{…} 是 shorthand,其中临时对象被复制到我们的新对象中。这虽然不执行复制构造函数 (copy elision),但需要它的存在和访问权限。但是当我通过将构造函数设为私有来阻止此特定 class 中的复制构造函数访问时,没有错误。
这意味着不涉及复制机制。那么'='符号的作用是什么?
我提到了以下问题,但由于没有解释而感到不满意:
Is C++11 Uniform Initialization a replacement for the old style syntax?

编辑: 同样,int arr[]{…}int arr[] = {…} 之间有区别吗?我问这个是为了看看我是否可以区分统一初始化和列表初始化。

这些具有几乎完全相同的效果:

  • T x = { 1, 2, 3 };
  • T x { 1, 2, 3 };

从技术上讲,带有 = 的版本称为 copy-list-initialization,另一个版本是 direct-list-initialization 但是这两种形式的行为都由 list-initialization 行为指定。

区别是:

  • 如果 copy-list-initialization 选择了一个 explicit 构造函数,那么代码格式不正确。
  • 如果 Tauto,那么:
    • copy-list-initialization 推导 std::initializer_list<Type_of_element>
    • direct-list-initialization 只允许列表中的单个元素,并推导Type_of_element

更多信息:Why does the standard differentiate between direct-list-initialization and copy-list-initialization?


如果T是数组类型那么上面的仍然适用;由于数组列表初始化始终是聚合初始化,因此永远不会选择构造函数,因此两个版本在所有情况下都是相同的。


T obj = T{...}(不包括 auto)与 T obj{...} 完全相同,因为 C++17,即 direct-list-initializationobj。在 C++17 之前,存在 直接列表初始化 临时文件,然后从临时文件进行 obj 的复制初始化。

我认为比较这两种语法不是你真正的问题。

在我看来,您希望 C++17 省略的行为与标准允许并由许多实现执行的 pre-C++17“优化”一样。

在该“优化”中,虽然可以省略复制构造函数调用,但它必须有效且可访问。

C++17 省略不是这种情况。

这是一个真正的省略,只写 T{} 并不会真正创建一个 T,而是说“我想要一个 T”,而实际的临时是“具体化”只是 if/when 它需要。

这个事实的冗余话语被有效地折叠成一个,所以尽管尖叫着“我想要一个 T!我想要一个 T!我想要一个 T!我想要一个T!" child 最后还是只得到一个 T

所以,在 C++17 中,T obj = T{...} 在字面上等同于 T obj{...}

这解释了您看到的结果以及您的困惑。


您可以阅读有关此功能的更多信息on cppreference.com;这是页面顶部的一个片段:

Mandatory elision of copy/move operations

Under the following circumstances, the compilers are required to omit the copy and move construction of class objects, even if the copy/move constructor and the destructor have observable side-effects. The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:

  • [..]
  • In the initialization of an object, when the initializer expression is a prvalue of the same class type (ignoring cv-qualification) as the variable type [..]