【问题标题】:Why does using-declared inheriting constructor NOT initialize the virtual base class using a default constructor?为什么使用声明的继承构造函数不使用默认构造函数初始化虚拟基类?
【发布时间】: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])。

[class.base.init]/10

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

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

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

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

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

我期望的是,当BC using-declare 构造函数时,最初由BA 继承的应该首先被视为B 构造函数,然后由BC 继承。但事实并非如此。

【问题讨论】:

  • 请注意,您引用的继承构造函数的规范已经过时,was completely replaced。看起来您正在使用符合新规范的编译器。
  • @j6t 这很好,谢谢!我在帖子末尾添加了一个相关问题。

标签: c++ multiple-inheritance virtual-inheritance using-declaration


【解决方案1】:

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

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

你引用的paragraph 说:

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

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

struct B 使用using A::A 时,它使构造函数A::Astruct B 中查找构造函数时可用。当struct BC 使用using B::B 时会发生这种情况;这是struct B 中名称B 的合格查找;因此,它找到了构造函数A::A,从而使A::Astruct BC 中可用。

【讨论】:

  • 好的,我认为这实际上为我连接了点。 using A::A 有效地将限定名称 B::A(或 B::A::A,如果我们通过注入类名路径)作为继承构造函数放入 B。然后被struct BCusing B::B继承,变成BC::A。它被选为完美匹配。在class.inhctor.init 之后,这保证了虚拟基础A 使用“相同”构造函数进行初始化。如果我没记错的话,这同样适用于子对象 BC
猜你喜欢
  • 1970-01-01
  • 2018-04-18
  • 2021-06-02
  • 2012-04-12
  • 2014-10-23
  • 1970-01-01
  • 2012-07-26
  • 2013-10-24
相关资源
最近更新 更多