首页 > 解决方案 > 为什么使用声明的继承构造函数不使用默认构造函数初始化虚拟基类?

问题描述

我昨天偶然发现了一个使用声明的继承构造函数的问题。在仔细阅读答案以及链接的标准草案 N3337 之后using,我发现当直接基类也用于从虚拟基类继承构造函数时,可能存在一些不一致(或者至少是我的误解) 。

这是一个例子:

struct A
{
    A(int, int){}
};

struct B : virtual A
{
    using A::A;
};

struct C : virtual A
{
    using A::A;
};

struct BC : B, C
{
    using B::B;
    using C::C;
}; 

// Now if we define an inline constructor that does the same
// as the constructor B inherited...
struct BB : virtual A
{
    BB(int a, int b):A(a,b){}
};

struct BBC : BB, C
{
    using BB::BB;
    using C::C;
};

int main()
{
    BC(1, 1);  // this compiles
    BBC(1, 1); // this doesn't because it needs to defaultly
               // initialize the virtual base A who doesn't
               // have a default constructor
}

我明白为什么BBC无法编译上述答案提供的确切原因,我将在此处重复 [class.inhctor]/8

隐式定义的继承构造函数执行类的一组初始化,这些初始化将由用户编写的内联构造函数为该类执行,该类具有一个 mem-initializer-list,其唯一的 mem-initializer 具有一个 mem-initializer-id,命名在 using 声明的嵌套名称说明符中表示的基类和下面指定的表达式列表,并且其函数体中的复合语句为空([class.base.init])。

[class.base.init]/10

在非委托构造函数中,初始化按以下顺序进行:首先,并且仅对于最派生类 ([intro.object]) 的构造函数,虚拟基类按照它们出现在深度优先左侧的顺序进行初始化从右到右遍历基类的有向无环图,其中“从左到右”是派生类基类说明符列表中基类的出现顺序。

所以基本上虚拟基类需要默认构造,因为它不在继承构造函数的mem-initializer-listBBC中。但是A没有默认构造函数,所以它失败(添加A()=default;显然可以使它编译,但这不是重点)。

但我还不清楚为什么BC没有这个问题?它与继承构造函数部分中的 cppreference 给出的示例基本相同。所以它必须工作。但它看起来与标准不矛盾吗?当从 继承构造函数时,它也只得到非默认构造函数,它执行与 中定义的构造函数相同的初始化,除了是隐式的。那么当这个构造函数进一步被继承时,不应该同样的规则适用于默认构造的地方,因此不编译吗?BABBBBCA

编辑:@j6t 指出我正在查看一个过时的标准草案。新的确实更符合我之前找到的 cppreference 页面。

对我来说仍然不清楚的一件事是,它确实解释了如果选择了虚拟基础构造函数会发生什么,但它BC首先是如何被孙子类继承的?从同一草案中,似乎using只在派生类中考虑引入的虚拟基构造函数(B在这种情况下)。usingin如何BC继承上面两层的构造函数?

我期望的是,在BC使用-声明构造函数时,最初由Bfrom继承的A应该首先被视为B构造函数,然后由BC. 但事实并非如此。

标签: c++multiple-inheritancevirtual-inheritanceusing-declaration

解决方案


我只是在编辑中回答你的问题。

...但是[虚拟基构造函数]首先是如何被孙子类 BC 继承的呢?

你引用的段落说:

在查找派生类 ( class.qual )的构造函数时,由 using 声明引入的构造函数被视为派生类的构造函数[...]。

即,它说这些构造函数是通过限定名称查找找到的。这使得 using-declaration 有效地成为递归操作:为了在基类中查找事物,它使用限定名称查找,然后使其找到的事物可用于限定名称查找。

使用时,struct Busing A::A使构造A::A函数在 中查找构造函数时可用struct Bstruct BC使用时会发生这种情况using B::B;这是对 中名称的限定B查找struct B;因此,它找到了构造函数A::A,并以这种方式A::Astruct BC.


推荐阅读