1

我的 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 应该将其默认构造函数合成为已删除。

我的书缺少什么规则,或者我没有掌握规则?

4

4 回答 4

3

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

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

Bar有一个已删除的默认构造函数,因为Foo没有默认构造函数;来自 C++11 12.1/5(第 5 个要点):

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

您引用的“规则”似乎确实缺少这一点,仅在第三个要点中提到了 const 合格成员的情况。

于 2013-01-28T16:42:54.837 回答
1

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

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

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

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

唯一可行的方法是创建一个公共Bar构造函数并Foo在成员 initializer-list 中初始化对象;以便默认构造Bar调用正确的构造函数foo。例如:

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

int main()
{
    Bar bar; // okay
}
于 2013-01-28T16:41:42.903 回答
1

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

从标准 12.1

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

任何直接或虚拟基类,或没有大括号或相等初始化器的非静态数据成员,具有类类型 M(或其数组),并且 M 没有应用于 M 的默认构造函数或重载决议 (13.3)构造函数会导致歧义或导致从默认的默认构造函数中删除或无法访问的函数。

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

于 2013-01-28T16:42:17.323 回答
0

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

于 2013-01-28T16:50:20.407 回答