使用C++在虚拟继承期间调用构造函数

Rya*_*n B 2 c++ constructor virtual-inheritance compiler-bug gcc5

这是我在learncpp.com上阅读本节时遇到的一个问题.我使用了这里列出的代码,然后对测试做了一些改动.

背景

虚拟继承创建对基类的公共引用,它具有两个效果.

首先,它消除了歧义,因为只创建了一个基础成员的副本(例如,向PoweredDevice添加print()函数并在main()中调用它否则会导致编译器错误).

其次,派生程度最高的类负责调用基础构造函数.如果其中一个中间类尝试在初始化列表中调用基本构造函数,则应忽略该调用.

问题

当我编译并运行代码时,它返回:

PoweredDevice: 3
PoweredDevice: 3
Scanner: 1
PoweredDevice: 3
Printer: 2
Run Code Online (Sandbox Code Playgroud)

它应该返回:

PoweredDevice: 3
Scanner: 1
Printer: 2
Run Code Online (Sandbox Code Playgroud)

当我使用GDB(7.11.1)执行执行时,它表明中间函数也通过初始化列表调用PoweredDevice - 但是应该忽略这些函数.PoweredDevice的这种多次初始化不会导致任何成员的歧义,但确实让我感到困扰,因为代码只执行一次时执行多次.对于更复杂的问题,我不习惯使用虚拟继承.

为什么这些中间类仍在初始化基础?这是我的编译器(gcc 5.4.0)的怪癖,还是我误解了虚拟继承的工作原理?

编辑:代码

#include <iostream>
using namespace std;

class PoweredDevice
{
public:
    int m_nPower;
public:
    PoweredDevice(int nPower)
        :m_nPower {nPower}
    {
        cout << "PoweredDevice: "<<nPower<<endl;
    }
    void print() { cout<<"Print m_nPower: "<<m_nPower<<endl; }
};

class Scanner : public virtual PoweredDevice
{
public:
    Scanner(int nScanner, int nPower)
        : PoweredDevice(nPower)
    {
        cout<<"Scanner: "<<nScanner<<endl;
    }
};

class Printer : public virtual PoweredDevice
{
public:
    Printer(int nPrinter, int nPower)
        : PoweredDevice(nPower)
    {
        cout<<"Printer: "<<nPrinter<<endl;
    }
};

class Copier : public Scanner, public Printer
{
public:
    Copier(int nScanner, int nPrinter, int nPower)
        :Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower}
    { }
};

int main()
{
    Copier cCopier {1,2,3};
    cCopier.print();
    cout<<cCopier.m_nPower<<'\n';
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Jus*_*ica 8

这似乎是一个GCC错误,当统一初始化与虚拟继承一起使用时触发.


如果我们改变:

Copier(int nScanner, int nPrinter, int nPower)
    :Scanner {nScanner, nPower}, Printer {nPrinter, nPower}, PoweredDevice {nPower}
{ }
Run Code Online (Sandbox Code Playgroud)

至:

Copier(int nScanner, int nPrinter, int nPower)
    :Scanner (nScanner, nPower), Printer (nPrinter, nPower), PoweredDevice (nPower)
{ }
Run Code Online (Sandbox Code Playgroud)

错误消失,并且行为符合预期:

PoweredDevice: 3
Scanner: 1
Printer: 2
Print m_nPower: 3
3
Run Code Online (Sandbox Code Playgroud)

Clang和Visual Studio都能够正确编译原始代码,并提供预期的输出.

  • @RyanB欢迎你.如果要检查特定编译器中是否存在某些错误或语言问题,通常可以将在线环境用于未安装的编译器.我喜欢[Rextester](http://www.rextester.com/),它有下载菜单中的Clang,GCC和VC++.但是,每个页面都在一个单独的页面上,因此您应该在切换到另一个之前复制代码. (2认同)
  • 我打开了一个错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80849。还有这个旧的预先存在的错误,它可能相关也可能无关:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55922 (2认同)