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

2

6

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;
}
Repeal answered 6/11, 2015 at 19:27 Comment(3)
This is hardly a minimal example. I can cut out about 100 lines of code from here!Airing
@Airing sorry for that, but I think the only redundant thing to keep the example valid is the base class C. I hope the code is clear even if it is a bit longer than the minimum.Repeal
Just A, X, and Node<T> is sufficient (don't need Apply, Inheritance, B, C, or Example...)Airing
R
0

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;
}
Repeal answered 10/11, 2015 at 11:20 Comment(0)
A
5

The initialization ordering for a class goes like this [class.base.init]:

In a non-delegating constructor, initialization proceeds in the following order:
— First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

Your hierarchy is A --> Node<X> --> X, so the first thing to get initialized is the X, since it's a virtual base class. It's not specified in your mem-initializer, so the implicit default construction is inserted:

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

}

Since X isn't default constructible, you get that error. You can fix this with just explicitly providing the right thing:

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

You don't have to worry about X being constructed twice, since virtual base classes are only constructed for the most derived class... which would be A and not Node<X>.

Airing answered 6/11, 2015 at 19:37 Comment(5)
This is still not working for me. I added the explicit X(i) constructor call in A, B, C and even in Example, but the compiler is still asking me for a default constructible X.Repeal
@Repeal note that your Inheritance<A,B,C> derives from A and Inheritance<B,C> - but it also derived virtually from X - so it must have on its initialization list construction of X. Simple speaking your Inheritance class template is not designed for dealing with virtual inheritance...Maturation
@Maturation I corrected the code, now the virtual inheritance is correctly implementedRepeal
@Repeal Please don't change the code after you post a question, I reverted it. Also, you didn't fix the problem anyway.Airing
@Airing I was not intending to fix the problem of the question, but to provide a correct example because PiotrNycz in his comment was right. In addition, those changes were not invalidating your answer at all.Repeal
R
0

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;
}
Repeal answered 10/11, 2015 at 11:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.