为什么指定“用户提供的”构造函数会使类成为非聚合类?

mad*_*ada 1 c++ default-constructor c++20

我从cppreference发现了这个例子:

\n
...\n\nstruct T3\n{\n    int mem1;\n    std::string mem2;\n    T3() {} // user-provided default constructor\n}\n\n...\n
Run Code Online (Sandbox Code Playgroud)\n

此示例清楚地表明给定的构造函数是用户提供的构造函数,因为它在第一个声明中没有显式默认或删除;根据 [dcl.fct.def.default]/5:

\n
\n

[..] 如果函数是用户声明的并且在其第一次声明时未\n显式默认或删除,则该函数是用户提供的 [..]

\n
\n

现在,根据 [dcl.init.aggr]/1

\n
\n

聚合是一个数组或一个类(第 11 条)

\n
    \n
  • (1.1) \xe2\x80\x94 没有用户声明或继承的构造函数 (11.4.5),
  • \n
  • (1.2) \xe2\x80\x94 没有私有或受保护的直接非静态数据成员 (11.8),
  • \n
  • (1.3) \xe2\x80\x94 无虚函数 (11.7.3),以及
  • \n
  • (1.4) \xe2\x80\x94 无虚拟、私有或受保护的基类 (11.7.2)。
  • \n
\n
\n

看来我的类满足上述所有要求,成为一个聚合,包括点 (1.1),因为给定的构造函数不是用户声明的。

\n

那么为什么下面的代码格式错误(在带有 c++20 标志的 g++12.2 上测试):

\n
static_assert(std::is_aggregate<T3>::value); // fail\n
Run Code Online (Sandbox Code Playgroud)\n

为什么静态断言失败?

\n

for*_*818 8

因为用户提供的构造函数是用户声明的构造函数。

它就在您引用的部分中:

[..] 如果函数是用户声明的并且在第一次声明时未显式默认或删除,则该函数是用户提供的 [..]

只有用户声明的构造函数才能成为用户提供的构造函数。当用户声明的构造函数在第一次声明时未显式默认或删除时,该构造函数就是用户提供的。

T3() {}         // I   user-provided, user-declared 
T3() = default; // II  user-declared, not user-provided
T3() = delete;  // III user-declared, not user-provided
Run Code Online (Sandbox Code Playgroud)

术语可能有点令人困惑。所有 3 个都声明一个默认构造函数(可以不带参数调用的构造函数)。只是II一个默认的构造函数(定义由编译器生成)。

PS:感谢@John 也澄清了这一点

T3();     // IV user-declared, user-provided
Run Code Online (Sandbox Code Playgroud)

是用户声明和用户提供的,即使稍后它可能通过以下方式定义

T3::T3() = default;
Run Code Online (Sandbox Code Playgroud)

因为根据定义,它是用户提供的(它是用户声明的,并且)它在第一次声明时没有显式默认或删除。

  • _“并且 `struct S{ S(); } S::S() = default` 也是用户提供的。”_ 它是用户提供的显式默认构造函数。因此“S”不是一个聚合。 (3认同)