删除复制构造函数会破坏继承的构造函数

Posted

技术标签:

【中文标题】删除复制构造函数会破坏继承的构造函数【英文标题】:Deleting copy constructor breaks inherited constructors 【发布时间】:2015-10-05 17:46:55 【问题描述】:

我正在尝试使用 C++11 的构造函数继承特性。以下 sn-p (从某处复制,我不记得从哪里复制的)完全可以正常工作:

#include <iostream>

struct Base 
  Base() : Base(0) 
  Base(int a) : Base(a, 0) 
  Base(int a, double b)  std::cout << "Base(" << a << "," << b << ")" << std::endl; 
;

struct Derived : Base 
  using Base::Base;
  Derived(const Derived& that) = delete;  // This line is the culprit
;

int main(int argc, char* argv[]) 
  Derived d1;
  Derived d2(42);
  Derived d3(42, 3.14);

即直到注释标记的行被添加;因为那时所有的地狱都会崩溃:

> g++ -std=c++11 -o test test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:18:11: error: no matching function for call to ‘Derived::Derived()’
   Derived d1;
           ^
test.cpp:18:11: note: candidates are:
test.cpp:13:16: note: Derived::Derived(int)
    using Base::Base;
                ^
test.cpp:13:16: note:   candidate expects 1 argument, 0 provided
test.cpp:13:16: note: Derived::Derived(int, double)
test.cpp:13:16: note:   candidate expects 2 arguments, 0 provided

似乎删除复制构造函数也以某种方式使Base 中的默认构造函数无法访问。谷歌搜索这个问题并没有带来任何有用的东西。 SO建议this issue,但据我了解,我在这个sn-p中没有使用复制初始化。有人能解释一下这里发生了什么吗?

(生成上述消息的编译器是 GCC 4.8.2;但是,clang 会返回类似的错误消息。)

【问题讨论】:

不继承默认构造函数。 T.C.怎么会这样?在Derived d1; 行中,我清楚地看到Base() 被调用。 @T.C.词语的选择具有误导性。当然,构造函数是继承的——否则,您将无法从派生类调用它们。它只是用于不同的类。 【参考方案1】:

问题是用delete 标记复制构造函数使其用户声明,这实际上删除了该类的默认构造函数(在您的情况下为Derived)。行为可以在这个简单的代码中看到:

struct X

    X(const X&) = delete; // now the default constructor is not defined anymore
;

int main() 

    X x; // cannot construct X, default constructor is inaccessible 

附带说明:即使Base::Base() 会被继承,编译器也会看到它像 Derived(): Base()。但是Derived被删除了,所以它不能真正调用Base::Base()。一般来说,using Base::Base 语句只是相应编译器生成的Derived(params): Base(params) 的语法糖。

【讨论】:

那是我丢失的链接。从没想过删除某些东西会算作“定制”它。谢谢! @DavidNemeskey 是的,这确实很令人困惑,但这就是它的工作原理。 标准术语是user-declared【参考方案2】:

无论何时定义自定义构造函数,都需要显式提供默认构造函数。即

Derived::Derived() = default;

【讨论】:

【参考方案3】:

继承构造函数不会得到特殊的构造函数——空、复制、移动。这是因为你从字面上要求的几乎总是一个坏主意。


检查:

struct base 
  std::vector<int> data;
  base(base const&)=default;
  base(base&&)=default;
  base(size_t n):data(n) 
  base()=default;
;

struct derived:base 
  using base::base;
  std::vector<char> more_data;
;

真的希望derived(base const&amp;) 存在吗?还是base(base&amp;&amp;)?两者都会绝望地切开derived

这些操作“意外”发生的危险意味着如果你想要它们,你必须明确地将它们引入。


默认情况下,复制/移动/默认 ctor 恰好只是调用父版本以及成员变量的 ctor。没有必要(通常​​)涉及从父母那里继承它们。

但是,一旦您 =delete=default 或定义了这些特殊 ctor 之一,编译器就会停止生成其他的。所以你必须=default其他人,如果你仍然希望他们留下来。

【讨论】:

感谢您的解释。甚至=default 也算用户声明? @DavidNemeskey 不。首先,因为我很草率。其次,因为 user-declared 是标准中定义的短语(我相信),我避免使用它们,因为它们具有谨慎的技术含义。但是=default 可以产生一些影响——如果你=default 复制ctor,则移动ctor 被抑制,反之亦然。 Live example。 =default 无效的 ctor 并非如此。 Live example

以上是关于删除复制构造函数会破坏继承的构造函数的主要内容,如果未能解决你的问题,请参考以下文章

C++在单继承多继承虚继承时,构造函数复制构造函数赋值操作符析构函数的执行顺序和执行内容

复制构造函数和赋值运算符

复制构造函数无法识别继承的成员

虚拟继承、默认构造函数和额外复制

将对象的指针提供给构造函数会导致对象被破坏[重复]

复制构造函数不被继承