【发布时间】:2020-08-05 02:16:43
【问题描述】:
我昨天偶然发现了 question 的 using-declared 继承构造函数。在仔细阅读了答案以及linked standard draft 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])。
在非委托构造函数中,初始化按以下顺序进行: 首先,并且仅对于最派生类([intro.object])的构造函数,虚拟基类按照它们在基类的有向无环图的深度优先从左到右遍历中出现的顺序进行初始化,其中“从左到右”是派生类基说明符列表中基类的出现顺序。
所以基本上虚拟基类需要默认构造,因为它不在BBC的继承构造函数的mem-initializer-list中。但是A 没有默认构造函数,所以它会失败(添加A()=default; 显然可以使它编译,但这不是重点)。
但我还不清楚为什么BC 没有这个问题?它本质上是 继承构造函数 部分中的the same example given by cppreference。所以它必须工作。但它看起来与标准不矛盾吗?当B 从A 继承构造函数时,它只得到非默认构造函数,它执行与BB 中定义的构造函数相同的初始化,除了是隐式的。那么当B 的这个构造函数被BC 进一步继承时,不应该同样的规则适用于A 将被默认构造因此不编译的地方吗?
编辑:@j6t 指出我正在查看一个过时的标准草案。 new one 确实更符合我之前找到的 cppreference 页面。
我还不清楚的一点是,它确实解释了如果选择了虚拟基础构造函数会发生什么,但它首先是如何被孙类BC 继承的?从同一个draft 看来,using 引入的虚拟基构造函数似乎只会在派生类中考虑(在这种情况下为B)。 BC 中的using 如何继承上两层的构造函数?
我期望的是,当BC using-declare 构造函数时,最初由B 从A 继承的应该首先被视为B 构造函数,然后由BC 继承。但事实并非如此。
【问题讨论】:
-
请注意,您引用的继承构造函数的规范已经过时,was completely replaced。看起来您正在使用符合新规范的编译器。
-
@j6t 这很好,谢谢!我在帖子末尾添加了一个相关问题。
标签: c++ multiple-inheritance virtual-inheritance using-declaration