在不使用 copy-constructor 的情况下初始化成员聚合类型

Initialising member aggregate type without copy-constructor

提问人:Owen Binchy 提问时间:3/10/2020 更新时间:3/10/2020 访问量:312

问:

我需要使用非默认构造函数启动类的成员数组,而不使用复制构造函数。

我有以下两个类:

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} }

但是我无法找到一种在不定义复制构造函数的情况下初始化数组的方法。我已经从这里和这里找到了答案,解释说正在发生复制省略,需要一个复制者来编译,但应该被编译器删除。永远不应该被复制,所以只为这个初始化定义一个复制者似乎非常尴尬,并且容易在其他地方进行更困难的调试。memMemberClass

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

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

有没有一种“正确”的方法来进行这种初始化?

C++ 初始化 复制构造函数 DataMember

评论

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

答:

0赞 M.M 3/10/2020 #1

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

如C++17 [dcl.init.aggr]/3所述(此文本在C++14中基本相同):

当聚合由11.6.4中指定的初始值设定项列表初始化时,初始值设定项列表中的元素将按顺序作为聚合元素的初始值设定项。每个元素都是从相应的初始值设定项子句复制初始化的。[...]如果 initializer-clause 本身是初始值设定项列表,则该成员是 list-initialized,

我将在这里分析三个不同的案例:

  • 案例一:mem { id, id }
  • 案例二:mem { MemberClass{id}, MemberClass{id} }
  • 案例三:mem { {id}, {id} }

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

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

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

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

因此,正确的行为是:

  • C++14 - 案例 3 正确,案例 1 和 2 格式错误。
  • C++17 - 所有大小写都正确。

gcc 发出的错误消息表明,它在 C++14 模式下将情况 3(您的代码)与其他两种情况相同。在C++17模式下,它从未得到过关于保证复制省略的备忘录。