10

A a = 1;在 C++11 之前,我们可以通过编写类似于A a = A(1);. 也就是说,首先创建一个临时对象,然后调用一个复制 ctor。无论复制省略如何,这在概念上都必须如此,并且复制 ctor 必须是可访问的。

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

所以,我的问题是:A a = {1, 2};概念上的表达式的确切语义是什么?从概念上讲,复制省略不会妨碍您。

4

1 回答 1

11

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

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

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

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

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

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

  • 如果没有找到可行的初始化列表构造函数,则再次执行重载决议,其中候选函数是类的所有构造函数,T参数列表由初始化列表的元素组成。

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

因此,如果没有找到初始化列表构造函数(如您的情况),则初始化列表的元素构成构造函数调用的参数。
事实上,direct-list-initialization 和 copy-list-initialization的唯一区别在于最后一个加粗的句子。

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

于 2014-11-17T01:47:12.157 回答