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

时间:2013-01-28 16:17:02

标签: c++

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

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

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

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

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

  •   

我没有看到这些规则如何解释这里的SECOND错误:

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 个答案:

答案 0 :(得分:3)

Foo没有默认构造函数,因为你声明了一个构造函数;来自C ++ 11 12.1 / 5:

  

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

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

  

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

你引用的“规则”确实似乎缺少这一点,只提到第3个项目符号点中符合const的成员的情况。

答案 1 :(得分:1)

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

  

如果出现以下情况,则将类X的隐式声明的默认构造函数定义为已删除:

     
      
  • const-quali fi ed类型(或其数组)的任何非静态数据成员都没有用户提供的默认构造函数,
  •   
  • 任何非静态数据成员都是引用类型,
  •   
  • 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
}

答案 2 :(得分:1)

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

从标准12.1

  在以下情况下,

类X的默认默认构造函数被定义为已删除:...

     

任何直接或虚拟基类,或者没有的非静态数据成员   brace-or-equal-initializer,具有类型M(或其数组)和   要么M没有默认构造函数,要么重载解析(13.3)为   应用于M的默认构造函数会导致模糊或在   从默认默认值中删除或无法访问的功能   构造

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

答案 3 :(得分:0)

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