虚拟继承是否强制基类默认为可构造的

Does virtual inheritance force a base class to be default constructible?

本文关键字:默认 基类 继承 是否 虚拟      更新时间:2023-10-16

在以下代码中,编译器请求基类X默认可构造。但是,如果我从类节点的继承中删除virtual关键字,则对成员m_x进行的访问当然会变得不明确,但不再需要classX默认构造函数。

这是什么原因?

#include <iostream>
struct Apply
{
    template< typename T >
    struct Node : virtual T    // this line contains the virtual inheritance
    {
        template< typename ...Args>
        Node( Args... args )
            : T( args... )
        {}
    };
    template < typename ...BaseClasses>
    struct Inheritance;
    template < typename FirstBaseClass, typename ...OtherBaseClasses>
    struct Inheritance< FirstBaseClass, OtherBaseClasses... >   : FirstBaseClass
            , Inheritance< OtherBaseClasses... >
    {
        template< typename ...Args>
        Inheritance( Args... args )
            : FirstBaseClass( args... )
            , Inheritance< OtherBaseClasses... >( args... )
        {
        }
    };
};
template < >
struct Apply::Inheritance< >
{
    template< typename ...Args>
    Inheritance( Args... args ){}
};
struct X
{
    X(int i){}
    int m_x;
};
struct A : Apply::Node< X >
{
    A( int i )
        : Apply::Node< X >( i )
        , m_a( i )
    {
    }
    int m_a;
};

struct B : Apply::Node< X >
{
    B( int i )
        : Apply::Node< X >( i )
        , m_b( i )
    { }
    int m_b;
};
struct C : Apply::Node< X >
{
    C( int i )
        : Apply::Node< X >( i )
        , m_c( i )
    { }
    int m_c;
};
struct Example : Apply::Inheritance< A, B, C >
{
    Example( int i )
        : Apply::Inheritance< A, B, C >( i )
    { }
    void print( ) const
    {
        // this line needs the virtual inheritance
        std::cout << m_x << std::endl;
        std::cout << m_a << std::endl;
        std::cout << m_b << std::endl;
        std::cout << m_c << std::endl;
    }
};
int main()
{
    Example ex( 10 );
    ex.print( );
    return 0;
}

类的初始化顺序如下[class.base.init]:

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

您的层次结构是A --> Node<X> --> X,因此首先要初始化的是X,因为它是一个虚拟基类。它没有在mem初始值设定项中指定,因此插入了隐式默认构造:

A( int i )
    : X() // <== implicit 
    , Node< X >( i )
    , m_a( i )
{
}

由于X不是默认可构造的,因此会出现该错误。你可以通过明确地提供正确的东西来解决这个问题:

A( int i )
    : X(i)
    , Node< X >( i )
    , m_a( i )
{

您不必担心X被构造两次,因为虚拟基类只为大多数派生类构造。。。其将是CCD_ 5而不是CCD_。

从@Berry答案开始,修复代码的唯一方法是对虚拟继承的X构造函数进行显式调用。

然而,在类ABC中显式调用X的构造是不够的:它必须基本上在任何级别的继承中涉及的每个类中调用!

棘手的是继承<>可变模板类:可变展开的每一步都必须提供对X构造函数的显式调用。

以下是在启用C++11标志的MinGW 4.9.2上运行的代码:

#include <iostream>
template< typename T, typename V >
struct Node : virtual V
{
    using Virtual = V;    // Added this line
    template< typename ...Args >
    Node( Args... args )
        : V( args... )
    { }
};
template < typename ...BaseClasses>
struct Inheritance;
template < typename FirstBaseClass, typename ...OtherBaseClasses>
struct Inheritance< FirstBaseClass, OtherBaseClasses... >
        : FirstBaseClass
        , Inheritance< OtherBaseClasses... >
{
    template< typename ...Args>
    Inheritance( Args... args )
        : FirstBaseClass::Virtual( args... )    // added this line
        , FirstBaseClass( args... )
        , Inheritance< OtherBaseClasses... >( args... )
    { }
};
template < >
struct Inheritance< >
{
    template< typename ...Args >
    Inheritance( Args... args )
    { }
};
struct X
{
    X(int i)
        : m_x( i )
    { }
    int m_x;
};
struct A : Node< A, X >
{
    A( int i )
        : X( i )    // added this line
        , Node< A, X >( i )
        , m_a( i )
    { }
    int m_a;
};

struct B : Node< B, X >
{
    B( int i )
        : X ( i )    // added this line
        , Node< B, X >( i )
        , m_b( i )
    { }
    int m_b;
};
struct C : Node< C, X >
{
    C( int i )
        : X ( i )    // added this line
        , Node< C, X >( i )
        , m_c( i )
    { }
    int m_c;
};
struct Example : Inheritance< A, B, C >
{
    Example( int i )
        : X ( i )    // added this line
        , Inheritance< A, B, C >( i )
    { }
    void print( ) const
    {
        // this line needs the virtual inheritance
        std::cout << m_x << std::endl;
        std::cout << m_a << std::endl;
        std::cout << m_b << std::endl;
        std::cout << m_c << std::endl;
    }
};
int main()
{
    Example ex( 10 );
    ex.print( );
    return 0;
}