Search code examples
c++variadic-templatestemplate-meta-programmingdefault-constructorvirtual-inheritance

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


In the following code, the compiler is requesting the base class X to be default constructible. However, if I remove the virtual keyword from the inheritance of the class Node, the access to the member m_x becomes, of course, ambiguous, but the default constructor for class X is no longer required.

What is the reason for that?

#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;
}

Solution

  • Starting from @Berry answer, the only way to fix the code was to code an explicit call to the virtual inherited X constructor.

    However, it is not enough to explicit call the construction of X in classes A, B, or C: it must be called basically in every class involved in the inheritance at any level!

    The tricky one was the Inheritance<> variadic template class: every step of the variadic expansion must provide the explicit call to the X constructor.

    Here is the code that works on MinGW 4.9.2 with enabled C++11 flag:

    #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;
    }