【问题标题】:Initialising member aggregate type without copy-constructor初始化没有复制构造函数的成员聚合类型
【发布时间】:2020-03-09 19:12:22
【问题描述】:

我需要使用非默认构造函数并且不使用复制构造函数来初始化类的成员数组。

我有以下两个类:

class MemberClass
{
  public:
    MemberClass(int id) {  /* Do stuff */ }; // Define non-default ctor
    MemberClass(const MemberClass& other) = delete; // Delete copy ctor
    ~MemberClass() { /* Do stuff */ }; // Overide default dtor
};

class ContainerClass
{
  private:
    MemberClass mem[2];

  public:
    ContainerClass(int id)
        : mem { {id} , {id} }
          {}
};

编译时出现以下错误:

error: use of deleted function ‘MemberClass::MemberClass(const MemberClass&)’
         : mem { {id} , {id} }

但是如果不定义复制构造函数,我无法找到初始化mem 数组的方法。我从herehere 中找到了答案,解释了复制省略正在发生并且需要复制ctor 来编译但应该被编译器删除。 MemberClass 永远不应该被复制,所以为这个初始化定义一个 copy-ctor 似乎很尴尬,而且在其他地方容易调试更困难。

如果MemberClass 仅具有默认构造函数,则编译器不会给出任何问题。如果mem 不是一个数组而只是一个MemberClass 对象,也不会出现任何问题。我唯一的问题是使用非默认 ctor 初始化这个数组,而不使用 copy-ctor。

奇怪的是,如果我不定义析构函数,我不会收到任何编译错误,这似乎是一个线索。

是否有一种“正确”的方式来进行这种初始化?

【问题讨论】:

  • 你的班级可以移动吗? (显示的不是但不确定这是否是遗漏)
  • 从您链接的帖子中,您可以声明复制构造函数而不定义它。
  • FWIW,它使用最新版本的 Clang 编译,但不是 GCC。

标签: c++ initialization copy-constructor datamember


【解决方案1】:

我认为这是 gcc 中的一个错误。

由 C++17 [dcl.init.aggr]/3 指定(此文本在 C++14 中基本相同):

当聚合由 11.6.4 中指定的初始化器列表初始化时,初始化器列表的元素按顺序作为聚合元素的初始化器。 每个元素都从相应的初始化子句复制初始化。 [...] 如果一个初始化子句本身就是一个初始化列表,则该成员是列表初始化的,

这里我会分析三种不同的情况:

  • 案例一:mem { id, id }
  • 案例2:mem { MemberClass{id}, MemberClass{id} }
  • 案例3:mem { {id}, {id} }

在情况 1 中,mem[0] 由表达式 id 复制初始化。这与MemberClass x = id; 的初始化类型相同。它被 dcl.init/17.6.2 覆盖(从不同类型的表达式复制初始化)。

行为是初始化器被转换为纯右值(即MemberClass{id}),然后直接初始化目标。在 C++14 中这是不正确的:虽然它是一个复制省略上下文,但仍然必须存在一个有效的复制构造函数。在 C++17 中它是格式良好的:从相同类型的纯右值初始化对象与使用纯右值指定的构造函数初始化对象相同(所谓的“保证复制省略”)。

案例 2 与案例 1 类似:它使用 17.6.1(从相同类型的表达式初始化)并且适用于案例 1 的相同分析。

但是,案例 3 不同。根据 dcl.init.aggr/3 的最后一个粗体引用,mem[0]{id}初始化,即代码的行为应与MemberClass z {id}; 相同。即使在 C++14 中也没有临时或复制操作。

所以正确的行为是:

  • C++14 - 案例 3 正确,案例 1 和 2 格式错误。
  • C++17 - 所有情况均正确。

gcc 发出的错误消息表明它在 C++14 模式下正在处理案例 3,即您的代码,与其他两种情况相同。而且在 C++17 模式下,它从来没有得到关于保证复制省略的备忘录。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多