已删除的默认构造函数(和复制控件成员)缺少规则

Missing rules for deleted default constructor (and copy control members)?

本文关键字:成员 控件 规则 复制 删除 默认 构造函数      更新时间:2023-10-16

我的 c++ 书(lippman,c++ primer,第五版,第 508 页)提供了以下 4 条规则,用于确定编译器何时将复制控件和默认构造函数合成为已删除成员:

  • 如果类具有自己的析构函数被删除或无法访问(例如私有)的成员,则合成析构函数定义为已删除。

  • 如果类具有自己的复制构造函数已删除或无法访问的成员,则合成复制构造函数定义为已删除。如果类的成员具有已删除或无法访问的析构函数,则也会删除该类。

  • 如果成员具有已删除或无法访问的复制赋值运算符,或者类具有 const 或引用成员,则合成复制赋值运算符定义为已删除。

  • 如果类具有具有已删除或无法访问析构函数的成员;或者具有没有类内初始值设定项的
  • 引用成员;或者具有其类型未显式定义默认构造函数且该成员没有类内初始值设定项的 const 成员,则合成的默认构造函数定义为已删除。

我看不出这些规则如何解释这里的第二个错误:

class Foo {
public:
  Foo(int i) { }
};
class Bar {
private:
  Foo foo;
};
int main() {
  Foo foo; //error: no matching constructor in Foo
  Bar bar; //error: implicitly deleted constructor in Bar
  return 0;
}

第一个错误是可以理解的,与这个问题没有直接关系。第二个错误令人惊讶,因为上面的规则没有解释为什么 Bar 应该将其默认构造函数合成为已删除。

我的书缺少哪些规则,还是我没有掌握规则?

Foo没有默认构造函数,因为您声明了一个构造函数;从 C++11 12.1/5 开始:

如果类 X 没有用户声明的构造函数,则隐式声明没有参数的构造函数 默认为

Bar有一个已删除的默认构造函数,因为它没有默认构造函数Foo;从 C++11 12.1/5(第 5 个项目符号点):

类 X 的默认构造函数定义为删除,如果 [...] 任何 [...] 非静态数据成员 [...] 没有默认构造函数

你引用的"规则"似乎确实没有这一点,只在第三个要点中提到了符合常量资格的成员的情况。

错误消息取决于编译器,但是,问题是Foo没有提供默认构造函数。 那个,你的规则缺少一个:

从标准 12.1

在以下情况下,类 X 的默认构造函数定义为已删除:...

任何直接或虚拟基类,或没有 大括号或等于初始值设定项,具有类类型 M(或其数组)和 M 没有默认构造函数或重载分辨率 (13.3) 为 应用于 M 的默认构造函数会导致歧义或 从默认默认值中删除或无法访问的函数 构造 函数。

由于 Foo 没有默认构造函数,因此 Bar 的构造函数被定义为已删除。

© ISO/IEC §12.1 [构造函数]:

在以下情况下,类 X 的隐式声明的默认构造函数定义为已删除:

  • 任何 const 限定类型(或其数组)的非静态数据成员都没有用户提供的默认构造函数,
  • 任何非静态数据成员都是引用类型,
  • X 是一个类似联合的类,它有一个带有非平凡默认构造函数的变体成员

Bar不能实例化,因为Foo没有默认构造函数(你定义了自己的构造函数,所以编译器省略了默认构造函数);默认构造Bar会导致使用Foo的已删除的默认构造函数,而这无法完成;因此编译器因此隐式删除Bar的构造函数。

唯一的方法是创建一个公共Bar构造函数并在成员初始值设定项列表中初始化Foo对象;以便Bar的默认构造调用正确的foo构造函数。例如:

class Bar {
    Foo foo;
    public:
        Bar() : foo(0) {} // calls Foo::Foo(int) constructor
};
int main()
{
    Bar bar; // okay
}

Bar的默认构造函数可能会做什么?它必须构造一个Foo,但不能默认构造它。编译器如何知道给Foo的构造函数什么值?不能。因此,如果任何成员或基不是默认构造的,则编译器无法为该类创建默认构造函数。因此,标准正确地删除了Bar的默认构造函数。