5

Trong đoạn mã sau, trình biên dịch yêu cầu cơ sở lớp Xmặc định có thể xây dựng. Tuy nhiên, nếu tôi loại bỏ các ảo từ khóa từ sản nghiệp của lớp Node, việc tiếp cận với các thành viên m_x trở nên, tất nhiên, không rõ ràng, nhưng các nhà xây dựng mặc định cho lớp X là không còn cần thiết .Thừa kế ảo có buộc một lớp cơ sở là cấu hình mặc định không?

Lý do cho điều đó là gì?

#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

Đây không phải là ví dụ * tối thiểu *. Tôi có thể cắt khoảng 100 dòng mã từ đây! – Barry

+0

@Barry xin lỗi vì điều đó, nhưng tôi nghĩ rằng điều duy nhất dư thừa để giữ cho ví dụ hợp lệ là lớp cơ sở C. Tôi hy vọng mã rõ ràng ngay cả khi nó dài hơn một chút so với mức tối thiểu. – nyarlathotep108

+1

Chỉ cần 'A',' X' và 'Node ' là đủ (không cần 'Áp dụng',' Thừa kế', 'B',' C' hoặc 'Ví dụ' ...) – Barry

Trả lời

0

Bắt đầu từ câu trả lời @Berry, cách duy nhất để sửa mã là mã hóa một lệnh gọi rõ ràng đến phương thức khởi tạo ảo được thừa kế X hàm tạo.

Tuy nhiên, nó không phải là đủ để rõ ràng gọi việc xây dựng X trong lớp Một, B, hoặc C: nó phải được gọi là cơ bản trong mỗi lớp liên quan đến việc thừa kế ở bất kỳ cấp độ!

Điều khó khăn nhất là Thừa kế <> Lớp mẫu biến thể: mỗi bước của bản mở rộng variadic phải cung cấp lệnh gọi rõ ràng cho hàm tạo X.

Đây là mã hoạt động trên MinGW 4.9.2 có bật cờ 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; 
} 
5

Việc khởi đặt hàng cho một lớp đi như thế này [class.base.init]:

Trong một constructor không ủy thác, tiền thu được khởi tạo theo trình tự sau:
- Thứ nhất, và chỉ dành cho hàm tạo của lớp có nguồn gốc cao nhất (1.8), các lớp cơ sở ảo được khởi tạo theo thứ tự thứ tự chúng xuất hiện trên một chiều ngang trái-phải-phải của biểu đồ tuần hoàn hướng của các lớp cơ sở, . -ngắn ”là thứ tự xuất hiện của các lớp cơ sở trong lớp cơ sở-specifier-li st.

Phân cấp của bạn là A --> Node<X> --> X, vì vậy điều đầu tiên được khởi tạo là X, vì đây là lớp cơ sở ảo. Nó không quy định tại mem-initializer của bạn, vì vậy việc xây dựng mặc định ngầm được chèn vào:

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

} 

Kể từ X không được mặc định constructible, bạn nhận được lỗi đó. Bạn có thể sửa lỗi này chỉ với cung cấp một cách rõ ràng là điều đúng đắn:

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

Bạn không cần phải lo lắng về X được xây dựng hai lần, kể từ khi các lớp cơ sở ảo chỉ được xây dựng cho các lớp có nguồn gốc nhất ... mà sẽ là A và không phải là Node<X>.

+0

Đây vẫn là không làm việc cho tôi. Tôi đã thêm lệnh gọi hàm dựng X (i) rõ ràng trong A, B, C và thậm chí trong Ví dụ, nhưng trình biên dịch vẫn yêu cầu tôi cho một cấu hình mặc định X. – nyarlathotep108

+0

@ nyarlathotep108 lưu ý rằng 'Inheritance ' có nguồn gốc từ 'A' và 'Thừa kế ' - nhưng nó cũng xuất phát hầu như từ 'X' - vì vậy nó phải có trong danh sách khởi tạo của nó' X'. Đơn giản nói mẫu 'Inheritance' lớp của bạn không được thiết kế để xử lý thừa kế ảo ... – PiotrNycz

+0

@PiotrNycz Tôi đã sửa mã, bây giờ thừa kế ảo được triển khai chính xác – nyarlathotep108

Các vấn đề liên quan