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

问题描述:

在以下代码中,编译器正在请求基类类X默认构造为。但是,如果我从类节点后,进入该成员m_x变,当然,暧昧的继承删除虚拟关键字,但类X默认构造函数不再需要。虚拟继承是否强制基类成为默认可构造的?

这是什么原因?

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

这几乎是一个*最小*的例子。我可以从这里删除约100行代码! – Barry

+0

@抱歉抱歉,但我认为保持示例有效的唯一多余的事情是基类C.我希望代码清晰,即使它比最小值长一点。 – nyarlathotep108

+1

只要'A','X'和'Node '就足够了(不需要'Apply','Inheritance','B','C'或'Example' ......) – Barry

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

然而,这是不够的显式调用X的建设类一个,或Ç:必须基本上叫每个类在任何级别参与继承!

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

以下是在MinGW 4.9上运行的代码。2与启用C++ 11标志:

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

初始化顺序的一类是这样的[class.base.init]:

在非委托构造,初始化进行以下顺序:
- 首先,与只对最大派生类(1.8)的构造函数,虚拟基类在 中初始化它们出现在基类的有向无环图的深度优先从左到右的遍历中的顺序,其中“left-to -right“是派生类base-specifier-li中基类的出现顺序ST。

你的层次是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正在建设的两倍,因为虚基类仅修建了派生类......这将是A而不是Node<X>

+0

这还是不为我工作。我在A,B,C甚至在Example中添加了显式的X(i)构造函数调用,但是编译器仍然要求我使用默认的可构造X. – nyarlathotep108

+0

@PiotrNycz我纠正了代码,现在虚拟继承被正确实现 – nyarlathotep108