【问题标题】:Does copy list initialization invoke copy ctor conceptually?复制列表初始化是否在概念上调用复制ctor?
【发布时间】:2015-01-13 20:45:35
【问题描述】:

在 C++11 之前,我们可以通过编写类似 A a = 1; 的内容来进行复制初始化,这或多或少等同于 A a = A(1);。也就是说,首先创建一个临时对象,然后调用一个复制 ctor。不管复制省略如何,这在概念上必须如此,并且复制 ctor 必须是可访问的。

使用 C++11 中的列表初始化,我们可以通过编写 A a = {1, 2}; 来进行复制列表初始化。在我看来,这应该或多或少等同于A a = A(1, 2);。然而,在 GCC 和 clang 上,A a = {1, 2} 编译即使复制和移动 ctor 不可访问(通过声明为私有)。尽管如此,如果相应的复制/移动 ctor 不可访问,A a = 1; 不会在 GCC 或 clang 上编译。所以,A a = {1, 2}; 似乎或多或少等同于直接列表初始化的A a{1, 2};。这与真正的直接列表初始化之间的区别在于,如果采用两个 int 的 ctor 是显式的,则 A a = {1, 2}; 不会编译。在这方面,A a = {1, 2}; 类似于复制初始化。

所以,我的问题是:在概念上,像 A a = {1, 2}; 这样的表达式的确切语义是什么? 在概念上,复制省略不会成为阻碍。

【问题讨论】:

  • 您已经在问题中自己解释了所有内容。复制列表初始化与直接列表初始化相同,只是前者不考虑显式构造函数。两者都不需要可访问的复制构造函数。
  • @Praetorian 这似乎不完全一样。请参阅我对哥伦布回答的评论。

标签: c++ c++11 copy-constructor list-initialization copy-initialization


【解决方案1】:

标准很好地描述了它; [dcl.init.list]/3:

T 类型的对象或引用的列表初始化定义如下:

  • [...]
  • 否则,如果T 是类类型,则考虑构造函数。这 枚举适用的构造函数并选择最好的构造函数 通过重载决议(13.3、13.3.1.7)。如果收窄 转换(见下文)需要转换任何参数, 程序格式不正确。

[over.match.list](强调我的):

当非聚合类类型T 的对象被列表初始化时 (8.5.4),重载决议分两个阶段选择构造函数:

  • 最初,候选函数是类 T 的初始化列表构造函数 (8.5.4),参数列表包括 初始化器列表作为单个参数。

  • 如果没有找到可行的初始化列表构造函数,重载解析再次执行,其中候选函数都是 T 类的构造函数和参数列表由 初始化列表的元素。

如果初始化列表没有 元素和 T 具有默认构造函数,则省略第一阶段。
在复制列表初始化中,如果选择了 explicit 构造函数,则 初始化格式不正确。

因此,如果没有找到初始化列表构造函数(如您的情况),初始化列表的元素构成构造函数调用的参数。
事实上,直接列表初始化和复制列表初始化的唯一区别被最后一个粗体句子所覆盖。

这是列表初始化的优点之一:它不需要存在无论如何都不会使用的特殊成员函数。

【讨论】:

  • 那么,如何在传值函数调用的上下文中解释复制列表初始化,例如f({1, 2});。在 GCC 和 clang 上,这需要可访问复制/移动 ctor。否则,该程序无法编译。
  • 我在cppreference.com的嵌入式在线编译器上进行了测试。例如,您可以在en.cppreference.com/w/cpp/language/explicit的底部找到它
猜你喜欢
  • 2011-08-05
  • 2011-10-11
  • 1970-01-01
  • 2014-03-16
  • 2012-11-21
  • 2022-11-09
  • 1970-01-01
  • 2012-12-07
  • 2021-09-19
相关资源
最近更新 更多