【问题标题】:List-initialization priority from object of same type来自相同类型对象的列表初始化优先级
【发布时间】:2016-03-02 10:52:37
【问题描述】:
#include <iostream>
#include <initializer_list>
using namespace std;

struct CL
{
    CL(){}
    CL (std::initializer_list<CL>){cout<<1;}
    CL (const CL&){cout<<2;}
};

int main()
{
    CL cl1;
    CL cl2 {cl1}; //prints 21
}

这是带有复制构造函数和初始化列表构造函数的 CL 结构。我认为这里必须只调用复制构造函数,因为根据 C++ 14 Standard, 8.5.4/3

类型 T 的对象或引用的列表初始化定义为 如下:
— 如果 T 是一个类类型并且初始化列表有一个 cv U 类型的单个元素,其中 U 是 T 或从 T 派生的类, 对象从那个元素初始化(通过复制初始化 用于复制列表初始化,或通过直接初始化 直接列表初始化)。
— 否则,...

换句话说,cl2 的初始化必须从 cl1 元素执行,而不是从初始化列表 {cl1} 执行。 Clang 和 gcc 都打印“21”,只有 Visual Studio 打印“2”,我认为它是正确的。
有两个候选构造函数用于获取 CL 类型的参数 cl1

  1. std::initializer_list&lt;CL&gt; 的构造函数(通过,因为没有从 CL 到 std::initializer_list&lt;CL&gt; 的此类转换)
  2. 使用 const CL& 复制构造函数(与仅限定转换 non-const->const 完全匹配)

谁是对的?谁的行为是正确的?

【问题讨论】:

  • 什么编译器?我无法用clang复制。我按预期收到了2
  • @juanchopanza 哪个版本的clang? ;)
  • 使用在线编译器:clang 3.6 说 21 但 clang 3.7 说 2 ;高达 5.2 的所有 g++ 版本说 21
  • @n.m.无论苹果 llvm 7.0.0 对应什么。
  • 您能否确认您使用的是已发布的 C++14 标准? (如果不是,并且您指的是草稿,请说明是哪个草稿)

标签: c++ constructor language-lawyer overload-resolution list-initialization


【解决方案1】:

tl;dr: 已发布的 C++14 文本指定输出 21。然而,这段代码的行为被CWG Issue 1467改变了,它在2014年11月获得了缺陷状态。

缺陷报告被视为追溯应用。 clang 3.7 和 VS2015 已应用此缺陷报告建议的解决方案,该报告从 N4296 开始出现在 C++17 草案中。


在此缺陷报告之前,该行为已被 N4140 [over.match.list] 中的此文本所涵盖:

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

  • 最初,候选函数是类 T 的初始化列表构造函数 (8.5.4),而参数列表由作为单个参数的初始化列表组成。
  • 如果找不到可行的初始化列表构造函数,则再次执行重载决议,其中候选函数是类 T 的所有构造函数,参数列表由初始化列表的元素组成。

如果初始化列表没有元素并且 T 有默认构造函数,则省略第一阶段。在复制列表初始化中,如果选择了显式构造函数,则初始化格式错误。 [注意:这与其他情况(13.3.1.3、13.3.1.4)不同,其中仅考虑转换构造函数进行复制初始化。仅当此初始化是重载决议的最终结果的一部分时,此限制才适用。 ——尾注]

您的类不是聚合,因为它有一个用户提供的构造函数。

以上文本由 [dcl.init.list]/3 中的以下项目符号指向:

  • 否则,如果 T 是类类型,则考虑构造函数。枚举适用的构造函数,并通过重载决议(13.3、13.3.1.7)选择最佳构造函数。

因此,在已发布的 C++14 中,如果匹配,初始化列表构造函数实际上应该优先于复制构造函数。 C++11 有相同的文本。


在您的问题中,您说 C++14 包含:

如果T 是一个类类型并且初始化列表有一个[...]类型的元素

此文本不是 C++14 中的,但后来通过缺陷报告应用。在应用了缺陷报告的更新标准 (N4296) 中,这显示为 [dcl.init.list]/3 中的项目符号列表中较高的项目符号;所以现在复制构造函数在这个过程中被提前选择了,我们没有达到上面的 [over.match.list] 步骤。

请注意,尽管该缺陷的标题为从同一类型对象聚合的列表初始化,但该解决方案实际上会影响聚合和非聚合的初始化。

【讨论】:

  • 希望这个缺陷解决方案能够解决所有未按预期选择复制构造函数的问题
  • 是的,我使用 N4296 Standard 进行了修正。谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-03
  • 1970-01-01
  • 2017-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多