带/不带默认参数的继承构造函数?

7

《C++ Primer(第五版)》第629页指出:

  • 如果基类构造函数具有默认参数,则不会继承这些参数。

我亲自测试了一下,对我来说,由编译器生成的派生构造函数似乎也具有与基类构造函数相同的默认参数。

以下是一个小测试:

#include <iostream>

struct Base
{
    Base() = default;

    Base(int x_, int y_ = 88, int z_ = 99)
        : x(x_), y(y_), z(z_) {}

    virtual void debug() const 
    { std::cout << "\nx - " << x << ", y - " << y << ", z - " << z << '\n'; } 

private:
    int x, y, z;
};

struct Derived : Base
{
    using Base::Base;
};

int main() {
    Base B(1);
    B.debug();         // x - 1, y - 88, z - 99
    Derived D(5);
    D.debug();         // x - 5, y - 88, z - 99

    return 0;
}

您可以在此处运行代码 - http://coliru.stacked-crooked.com/a/26cbb85757c1f021 那么我们是否也继承了继承构造函数的默认参数呢? 如果没有,为什么我最后两个成员不是垃圾值,而是与从基类继承的构造函数的默认参数完全相同的值? 同时,在互联网上搜索了很多关于这个问题的清晰回答,但都没有找到。
2个回答

8

你引用的书中内容不完整。

如果基类构造函数有默认参数(§6.5.1,第236页),那么这些参数不会被继承。相反,派生类将获得多个继承构造函数,其中每个带有默认参数的参数都会被依次省略。例如,如果基类有一个具有两个参数的构造函数,其中第二个参数有一个默认值,则派生类将获得两个构造函数:一个带有两个参数(没有默认参数),另一个构造函数带有一个参数,对应于基类中最左边的非默认参数

因此,您的Derived类将拥有3个继承构造函数,其签名如下:

Derived (int x): Base{x} {}
Derived (int x, int y): Base{x, y} {}
Derived (int x, int y, int z): Base{x, y, z} {}

因此,您不会从基类继承任何默认参数。

Derived D(5);

调用上述三个构造函数的第一个构造函数,基础构造函数被调用。
Base(5)

此外,请注意,默认、复制和移动构造函数不会被继承。这些构造函数是使用正常规则合成的。继承的构造函数不被视为用户定义的构造函数。因此,只包含继承构造函数的类将具有合成的默认构造函数。

这与我的问题没有任何关联。 - Mugurel
太好了!没想到还有这种情况。谢谢!但是,如果派生类D(5)调用基类(5),为什么我打印D的成员时会得到5、88、99?最后两个成员不应该保持未初始化状态吗? - Mugurel
2
因为基类的默认参数并没有消失。它们仍然存在。而Base(5)实际上调用了Base(5, 88, 99)。 - Elohim Meth

6

来自[class.inhctor]:

在using声明中命名的类X的继承构造函数候选集由实际构造函数和推导构造函数组成,这些推导构造函数是通过默认参数和省略号参数规范的转换产生的,具体如下:
— [...]
— 对于X的每个非模板构造函数,该构造函数至少有一个带有默认参数的参数,从参数类型列表的末尾省略任何省略号参数规范并逐步省略带有默认参数的参数得到的构造函数集合,以及
— [...]

我们有两个非模板构造函数BaseBase的默认构造函数引入了候选项:

Derived() : Base() { }

Base的另一个构造函数为每个连续省略的参数引入了一个候选项。

Derived(int x, int y, int z) : Base(x, y, z) { }
Derived(int x, int y)        : Base(x, y) { }
Derived(int x)               : Base(x) { }

默认参数不会被继承 - 我们只是为每个参数数量获得一个不同的构造函数。因此,Derived(5) 只是调用 Base(5),而不是调用 Base(5, 88, 99)
最终结果是相同的 - 只是我们到达目的地的方式有点不同。

好的,但是为什么如果“Derived(5)只是调用Base(5),而不是Base(5, 88, 99)”,当我打印D的成员时,我会得到5、88、99呢? 最后两个成员不应该保持未初始化状态吗? - Mugurel
1
@Mugurel 因为 Base(5) 调用了 Base(int x_, int y_ = 88, int z_ = 99)Derived 没有默认参数,但是 Base 仍然有。 - Barry
最终结果是相同的 - 只是我们到达那里的方式有点不同。 - Mugurel
1
值得注意的是,委员会很可能会通过N4429(或该文件的修订版)对继承构造函数进行重大改进,并将其作为针对C++11的DR实施。 - T.C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接