为什么在已删除的默认 ctor 旁边定义一个空的复制 ctor 会使空列表的值初始化失败?

Why does defining an empty copy ctor beside a deleted default ctor make a value initialization with empty list fail?

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

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++的人的一句话比我认为

更好

没有 user-provided copy constructor, A is an aggregate. Yes, even though we deleted the default constructor. It's something that was .

所以,在 C++20 之前,A a{};aggregate initialization, and so doesn't use the deleted constructor. When you uncomment the copy constructor, A stops being an aggregate, thus turning the aggregate initialization into value initialization。因此 a 的初始化将尝试调用已删除的构造函数。

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

聚合初始化不是值初始化

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

S s{};

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

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


什么是聚合 class 的定义受制于:

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

(2) 在 C++14 到 C++20 中,什么算作用户提供的函数的定义由 [dcl.fct.def.default]/4 in C++11 and [dcl.fct.def.default]/5 管理; user-provided 的定义在所有这些语言版本中基本相同。

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


这个例子是合计吗?

对于下面的每个例子;如果示例的 class 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):

A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration.


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:无(有用户声明的构造函数)