为什么除非最派生的基类显式调用它们,否则不调用虚拟基类非默认构造函数?

JVD*_*JVD 5 c++ constructor virtual-inheritance ctor-initializer c++17

我想了解为什么 C++ 标准要求虚拟基非默认构造函数不能由中间非最派生类调用,如这段代码中所示,当使用 '-D_WITH_BUG_' 编译时:

/*  A virtual base's non-default constructor is NOT called UNLESS 
 *  the MOST DERIVED class explicitly invokes it
 */

#include <type_traits>
#include <string>
#include <iostream>

class A
{
public:
    int _a;
    A():  _a(1)
    {
        std::cerr << "A() - me: " << ((void*)this) << std::endl;
    }
    A(int a): _a(a)
    {
        std::cerr << "A(a) - me:" << ((void*)this) << std::endl;
    }
    virtual ~A()
    {
        std::cerr << "~A" << ((void*)this) << std::endl;
    }
};

class B: public virtual A
{
public:
    int _b;
    B(): A(), _b(2)
    {
        std::cerr << "B() - me: " << ((void*)this) << std::endl;
    }
    B(int b) : A(), _b(b)
    {
        std::cerr << "B(b) - me: " << ((void*)this) << std::endl;
    }
    B(int a, int b): A(a), _b(b)
    {
        std::cerr << "B(a,b) - me: " << ((void*)this) << std::endl;
    }
    virtual ~B()
    {
        std::cerr << "~B" << ((void*)this) << std::endl;
    }
};

class C: public virtual B
{
public:
    int _c;
    C(): B(), _c(3)
    {
        std::cerr  << "C()" << std::endl;
    }
    C(int a, int b, int c)
    :
#ifdef _WITH_BUG_    
    B(a,b)
#else
    A(a), B(b)
#endif    
    , _c(c)
    {
        std::cerr  << "C(a,b) - me: " << ((void*)this) << std::endl;    
    }
    virtual ~C()
    {
        std::cerr << "~C" << ((void*)this) << std::endl;
    }  
};
extern "C"
int main(int argc, const char *const* argv, const char *const* envp)
{
    C c(4,5,6);
    std::cerr << " a: " << c._a  << " b: " << c._b << " c: " << c._c 
              <<  std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

因此,当不使用 -D_WITH_BUG_ 进行编译时,代码将打印:

$ g++ -I. -std=gnu++17 -mtune=native -g3 -fPIC -pipe -Wall -Wextra \
  -Wno-unused -fno-pretty-templates -Wno-register  \
  tCXX_VB.C -o tCXX_VB 
$ ./tCXX_VB
A(a) - me:0x7ffc410b8c10
B(b) - me: 0x7ffc410b8c00
C(a,b) - me: 0x7ffc410b8bf0
a: 4 b: 5 c: 6
~C0x7ffc410b8bf0
~B0x7ffc410b8c00
~A0x7ffc410b8c10
Run Code Online (Sandbox Code Playgroud)

但是当使用 -D_WITH_BUG_ 编译时:

$ g++ -I. -std=gnu++17 -mtune=native -g3 -fPIC -pipe -Wall -Wextra \ 
  -Wno-unused -fno-pretty-templates -Wno-register \
  -D_WITH_BUG_ tCXX_VB.C -o tCXX_VB
$ ./tCXX_VB
A() - me: 0x7ffd7153cb60
B(a,b) - me: 0x7ffd7153cb50
C(a,b) - me: 0x7ffd7153cb40
a: 1 b: 5 c: 6
~C0x7ffd7153cb40
~B0x7ffd7153cb50
~A0x7ffd7153cb60
Run Code Online (Sandbox Code Playgroud)

为什么这里必须忽略 B(int a, int b) 对 A(a) 的调用?我理解 C++ 标准强制要求这样做,但为什么呢?什么是理性?

如果我只实例化一个 B 对象: B b(4,5) ;这确实得到了正确的 b._a 值 4;但如果 B 是 C 的子类: C c(4,5,6) C::a 最终为 1,则 IFF c 不会直接调用 A(a) 。因此,如果 B(a,b) 是子类对象,则它的值与它是最派生对象时是不同的。这对我来说非常令人困惑和错误。是否有希望让足够多的人同意就此更改 C++ 标准?

Lea*_*ngC 1

这种行为是因为virtual base class. 由于 A 是虚拟基类,因此它是由最派生的类构造的。
您可以查看有关菱形继承问题类似问题的讨论,以了解为什么必须采用这种方式。
首先了解diamod形状问题是如何通过虚拟基类解决的。
class A { ...}
class B: virtual public A {...}
class C: virtual public A {...}
class D: public B, public C {...}
当您将基类设置为虚拟时,将会有一个基类对象。中间派生类对象将全部引用同一个基类对象。D即,如果创建了一个对象,则 B::A 和 C::A 都将引用同一个对象。该单个对象是B和C的基类。因此,如果允许中间类构造基类对象,则有两个派生类来构造该单个对象。通过让最底层的派生类负责构造虚拟基类,可以解决这种歧义。