【问题标题】:C++ Default constructor not inherited with "using" when move and copy constructors present当存在移动和复制构造函数时,C++ 默认构造函数不使用“使用”继承
【发布时间】:2017-11-15 06:44:56
【问题描述】:
class A{

public:
    A(){};

};

class B : public A{

public:
    using A::A;

    B(const B&) =   default;
    B(      B&&) =  default;

};

B b;

编译器 (g++ (5.4.0-6ubuntu1) / c++11) 说“没有用于调用 B::B() 的匹配函数”并将复制和移动构造函数列为候选。如果我将那些默认的注释掉,那么它就会编译。这是什么原因造成的?它们被明确默认有什么区别?如果这两行不存在,它们无论如何都会被默认。

【问题讨论】:

  • using 不会带来所有基本构造函数。在某些情况下不会生成默认构造函数。
  • 不确定,但是一旦你明确声明了一个构造函数(无论它是否是默认的),编译器将不会自动为你生成一个构造函数
  • 请注意,using A::A; 不是问题的一部分:如果您将其注释掉,您将得到同样的错误。
  • @GianPaolo 但是 为什么 using A::A 不会导致默认 ctor 被继承?我相信这实际上是 OP 问题的核心。
  • 这段代码使用 clang > 3.9 编译。

标签: c++ c++11 constructor language-lawyer c++17


【解决方案1】:

在C++17之前,基类的默认构造函数不会是inherited via using:

不是默认构造函数或复制/移动构造函数并且其签名与派生类中用户定义的构造函数不匹配的所有候选继承构造函数都在派生类中隐式声明。 (直到 C++17)

在 C++17 之后,代码可以正常工作。

在此之前,默认构造函数不会从基类继承,也不会是generated 类B,因为提供了复制/移动构造函数。

如果没有为类类型(结构、类或联合)提供任何类型的用户声明构造函数,编译器将始终将默认构造函数声明为其类的内联公共成员。

这就是为什么如果您将复制/移动构造函数注释掉,它就会编译。您可以将定义显式添加为 C++17 之前的解决方法。例如

class B : public A {
public:
    B(const B&) =   default;
    B(      B&&) =  default;

    B() = default;
};

代码用gcc8编译。

【讨论】:

  • 真的需要把using A::A放在那里吗?如果您省略它并留下B() = default,它会有什么不同吗?
【解决方案2】:

如果你声明了任何构造函数,默认构造函数不是implicitly generated,你也可以通过为它添加= default来生成它:

class B : public A {

public:

    B() = default;
    B(const B&) =   default;
    B(      B&&) =  default;
};

C++17 改变了这一点(正如other 回答所指出的那样)。

【讨论】:

    【解决方案3】:

    默认构造函数不能被继承,标准明确规定。引用 C++11 12.9 [class.inhctor]/3(强调我的)(*):

    对于候选继承构造函数集合中的每个非模板构造函数除了构造函数 没有参数或具有单个参数的复制/移动构造函数,构造函数是隐式的 用相同的构造函数声明,除非有一个用户声明的构造函数具有相同的 使用声明出现的类中的签名。 ...

    这意味着对于默认构造函数,正常规则适用,就好像using A::A; 声明不存在一样。因此,任何其他构造函数声明(例如复制和移动构造函数)的存在都会导致默认构造函数不会被隐式声明。请注意,您可以通过显式默认将其轻松添加回来:

    class B : public A{
    
    public:
        using A::A;
    
        B() = default;
    
        B(const B&) =   default;
        B(      B&&) =  default;
    
    };
    

    (*) 相同的措辞出现在 C++14 (n4140) 的相同位置。我似乎在 C++1z 中找不到等效的措辞(通过 n4582 查看)

    【讨论】:

    • 随着新标准的变化,这是否有所改变?
    • @PasserBy 我认为是这样,因为(至少)n4582(如果我没记错的话)是 c++1z 的基础,因此第 12.9 段不存在。
    • @Holt 是的,我正在查看标准,但没有看到与上述引用类似的任何内容。我确实看到了这一点 using T::T; // inherits all constructors from class T
    猜你喜欢
    • 1970-01-01
    • 2016-03-24
    • 2015-07-09
    • 2016-12-16
    • 2020-05-14
    • 1970-01-01
    • 2018-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多