5

简而言之,为什么下面的代码表现得像评论中描述的那样?

struct A
{
    A() = delete;
    //A(const A&) {} // uncommenting this...
};

int main()
{
    A a{}; // ... breaks this
    //A(); // this fails in either case because of `A() = delete;` only
}

为了理解这一点,我应该查看标准的哪一部分(或至少是 cppreference 上的一页)?

但是,写A(const A&) = default;而不是//A(const A&) {} 不会中断A a{};。那这个呢?我认为根本原因是一样的,但是真正了解 C++ 的人的话比我的要好。

4

2 回答 2

10

没有用户提供的复制构造函数,A是一个聚合。是的,即使我们删除了默认构造函数。这是C++20 中解决的问题

因此,在 C++20 之前,A a{};聚合初始化,因此不使用已删除的构造函数。当您取消注释复制构造函数时,A不再是聚合,从而将聚合初始化转换为值初始化。所以初始化a会尝试调用被删除的构造函数。

为了从标准中判断初始化器的含义,通常从[dcl.init]/16开始。通过项目符号,可以发现初始化程序的属性(当与相关类型的属性匹配时)将如何影响初始化发生的方式。

于 2021-01-13T07:41:28.507 回答
3

聚合初始化不是值初始化

如果给定的类,比如说S,是一个聚合,那么

S s{};

是聚合初始化,而不是值初始化,并且将绕过 的任何构造函数S,即使它们被删除或私有。聚合初始化的结果通常是聚合的 (public (1) ) 数据成员的值初始化。

(1) 如下所示,具有任何私有数据成员的类绝不是聚合类,不通过任何 C++11 到 C++20 标准。


什么是聚合类的定义受以下约束:

其中与聚合的典型混淆是 C++11 到 C++17 对没有任何用户提供的(2)构造函数的较弱要求,这在 C++20 中变得更加严格,要求没有任何用户-声明的构造函数。

(2) 用户提供函数的定义由C++11 中的 [dcl.fct.def.default ]/4和 C++14 中的[dcl.fct.def.default]/5到C++20;在所有这些语言版本中,用户提供的定义基本相同。

C++11、C++14 和 C++17 之间的聚合定义也有其他变化,这些变化并不为人所知,但同样增加了 C++20 之前的聚合混乱;我们将通过几个示例来突出它们。


示例是聚合吗?

对于下面的每个示例;如果S示例的类是聚合(对于特定语言版本),则以下格式正确:

S s{};

上述所有示例中的聚合规则同样适用于任何类型的构造函数(假设它们在其他方面适用,例如允许显式默认或删除),而不仅仅是S().


struct S {};
  • C++11:是的
  • C++14:是的
  • C++17:是的
  • C++20:是的

struct S {
    int x;
};
  • C++11:是的
  • C++14:是的
  • C++17:是的
  • C++20:是的

struct S {
    int x{42};
};
  • C++11:否(具有默认成员初始化程序)
  • C++14:是的
  • C++17:是的
  • C++20:是的

class S {
    int x;
};
  • C++11:没有
  • C++14:没有
  • C++17:没有
  • C++20:没有

有一个私有数据成员。


struct S {
    S() = default;
};
  • C++11:是的
  • C++14:是的
  • C++17:是的
  • C++20:否(具有用户声明的构造函数)

struct S {
 private:
    S() = default;
};
  • C++11:是的
  • C++14:是的
  • C++17:是的
  • C++20:否(具有用户声明的构造函数)

struct S {
    S() = delete;
};
  • C++11:是的
  • C++14:是的
  • C++17:是的
  • C++20:否(具有用户声明的构造函数)

struct S {
    S();
};
S::S() = default;
  • C++11:否(具有用户提供的构造函数)
  • C++14:没有(...)
  • C++17:没有(...)
  • C++20:否(具有用户声明的构造函数)

显式默认定义不符合规定,视为用户提供;回顾 [dcl.fct.def.default]/4 (C++11) / [dcl.fct.def.default]/5 (C++14, C++17):

如果函数是用户声明的,并且在其第一次声明时没有显式默认或删除,则该函数是用户提供的。


struct S {
    explicit S() = default;
};
  • C++11:是的
  • C++14:是的
  • C++17:否(有explicit构造函数)
  • C++20:否(具有用户声明的构造函数)

C++17 增加了聚合不能有explicit构造函数的要求。


struct S {
    int x{42};
    S() = delete;
};
  • C++11:否(具有默认成员初始化程序)
  • C++14:是的
  • C++17:是的
  • C++20:否(具有用户声明的构造函数)

C++14 删除了 C++11 对聚合没有默认成员初始值设定项的要求。


struct S {
    int x{42};
    explicit S() = delete;
};
  • C++11:否(具有默认成员初始化程序)
  • C++14:是的
  • C++17:否(有explicit构造函数)
  • C++20:否(具有用户声明的构造函数)
于 2021-01-13T10:26:33.130 回答