【问题标题】:Explicit default constructor显式默认构造函数
【发布时间】:2018-02-08 04:48:12
【问题描述】:

这段代码在 GCC 5.X、MSVC 上编译得很好,但是 GCC 6.X 给出了错误:

"从初始化列表转换为 'a' 将使用显式 constructor 'a::a()'", clang " 选择的构造函数在 复制初始化”。

删除explicit 或更改为a c{} 可以解决问题,但我很好奇为什么会这样。

class a
{
public:
    explicit a () {}
};
struct b
{
    a c;
};

int main() {
    b d{};
}

【问题讨论】:

  • 使用b d; 或b d{a{}} 也可以

标签: c++ c++11 c++14 language-lawyer


【解决方案1】:

b 是一个aggregate。当您使用初始化器列表对其进行初始化时,列表中的元素将初始化聚合的前 n 个成员,其中 n 是列表中的元素数。聚合的剩余元素是copy-list-initialized。

因此,在您的示例中,c 将被 copy-list-initialized,但如果选择的构造函数是 explicit,则格式错误,因此会出现错误。

相关标准报价为

[dcl.init.aggr]/3

当聚合被 [dcl.init.list] 中指定的初始化列表初始化时,初始化列表的元素被视为聚合元素的初始化。 聚合的显式初始化元素确定如下:
...
— 如果初始化列表是 initializer-list,则显式初始化的聚合元素是聚合的前 n 个元素,其中 n是初始值设定项列表中的元素数。
— 否则,初始化列表必须是{},并且没有显式初始化的元素。

[dcl.init.aggr]/5

对于非联合聚合,每个不是显式初始化元素的元素都按如下方式初始化:
...
— 否则,如果元素不是引用,则从空的初始化列表 ([dcl.init.list]) 复制初始化该元素。

从一个空的初始化列表复制初始化c的效果在

中描述

[dcl.init.list]/3

T 类型的对象或引用的列表初始化定义如下:
...
— 否则,如果初始值设定项列表没有元素并且T 是具有默认构造函数的类类型,则该对象是值初始化的。

[dcl.init]/8

值初始化T 类型的对象意味着:
...
— 如果T 是一个(可能是 cv 限定的)类类型,没有默认构造函数 ([class.ctor]) 或用户提供或删除的默认构造函数,则该对象是默认初始化的;

[dcl.init]/7

默认初始化T 类型的对象意味着:
— 如果 T 是(可能是 cv 限定的)类类型,则考虑构造函数。枚举适用的构造函数([over.match.ctor]),并通过重载决议选择初始化器()的最佳构造函数。如此选择的构造函数被调用,并带有一个空参数列表,以初始化对象。

[over.match.ctor]

...对于复制初始化,候选函数都是该类的转换构造函数。

[class.conv.ctor]/1

声明的构造函数没有函数说明符 explicit 指定从其参数类型(如果有)到其类的类型的转换。这样的构造函数称为转换构造函数。

在上面的示例中,a 没有转换构造函数,因此重载解析失败。 [class.conv.ctor]/2 中的(非规范)示例甚至包含一个非常相似的案例

  struct Z {
    explicit Z();
    explicit Z(int);
    explicit Z(int, int);
  };

  Z c = {};                       // error: copy-list-initialization

您可以通过为c 提供默认成员初始化程序来避免该错误

struct b
{
    a c{};  // direct-list-initialization, explicit ctor is OK
};

【讨论】:

  • 这是否意味着这是 GCC 5.x 和 MSVC 中的错误? GCC 4.9.2 也可以编译 OP 的代码。
  • @DanielLangr 当前提到复制初始化的措辞是作为指定初始化程序的change 的一部分引入的。如果我将其与 N4140(C++14 草案)进行比较,前面的措辞是如果没有大括号或等号初始化器,来自一个空的初始化器列表,我想这可以解释为直接列表初始化。这可能是旧编译器接受它的原因。我找不到引入“复制初始化”的具体原因..
  • 还有这个paper 处理explicit 构造函数问题,说明其余成员是通过直接列表初始化进行初始化的,但我找不到关于那篇论文发生了什么的任何信息.
  • 您从 [over.match.list] 中“随机”选择了一条规则,但未说明此规则为何适用。您没有显示您之前引用的 [dcl.init.list] 中的相关措辞链。所以引用基本上是没有用的。我不明白:为什么这条规则有效?
  • over.match.ctor 说“对于不在复制初始化上下文中的直接初始化或默认初始化,候选函数是正在初始化的对象类的所有构造函数。对于复制初始化,候选函数是该类的所有转换构造函数。参数列表是初始化程序的表达式列表或赋值表达式。"。这意味着我们只考虑转换构造函数。
猜你喜欢
  • 2016-02-05
  • 2011-02-19
  • 2016-06-24
  • 2012-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-25
  • 1970-01-01
相关资源
最近更新 更多