2012-01-03 28 views
9

Tôi đang cố gắng sử dụng mẫu meta-lập trình để xác định lớp cơ sở. Có cách nào để có được các lớp cơ sở tự động mà không rõ ràng chuyên cho từng lớp dẫn xuất?Loại lớp cơ sở có thể lấy được từ một loại mẫu tự động không?

class foo { public: char * Name() { return "foo"; }; }; 
class bar : public foo { public: char * Name() { return "bar"; }; }; 

template< typename T > struct ClassInfo { typedef T Base; }; 
template<> struct ClassInfo<bar> { typedef foo Base; }; 

int main() 
{ 
    ClassInfo<foo>::Base A; 
    ClassInfo<bar>::Base B; 

    std::cout << A.Name(); //foo 
    std::cout << B.Name(); //foo 
} 

hiện tại, bất kỳ phương thức tự động nào cũng sẽ cần chọn cơ sở được khai báo đầu tiên và sẽ không thành công cho các cơ sở tư nhân.

+1

Sử dụng ['std :: is_base_of '] (http://stackoverflow.com/questions/2910979/how-is-base-of-works) – iammilind

+3

@iammilind: Đó chỉ là để thử nghiệm nếu một lớp là cơ sở của người khác, bạn phải biết lớp cơ sở để kiểm tra. – Xeo

+4

Bạn cần nó để làm gì? Tôi không nghĩ rằng nó có thể, nhưng có lẽ có cách tiếp cận khác nhau để giải quyết vấn đề thực tế. –

Trả lời

5

Giải pháp của tôi không thực sự tự động, nhưng tốt nhất tôi có thể nghĩ đến.

xâm nhập C++ giải pháp 03:

class B {}; 

class A : public B 
{ 
public: 
    typedef B Base; 
}; 

Không xâm nhập giải pháp C++ 03:

class B {}; 

class A : public B {}; 

template<class T> 
struct TypeInfo; 

template<> 
struct TypeInfo<A> 
{ 
    typedef B Base; 
}; 
+0

đó là khá nhiều chính xác những gì tôi đã đưa ra quá. Tôi đã hy vọng có một số mẹo mẫu hardcore mà tôi đã không biết rằng có thể giải nén nó .. Tôi có thể sẽ sử dụng hình thức xâm nhập cho các lớp với điều khiển tạo hoàn chỉnh và hình thức không xâm nhập cho các lớp được bán nước ngoài - cảm ơn – VonRiphenburger

5

Tôi không biết về bất kỳ mẫu chọn lớp cơ sở nào và tôi không chắc chắn mẫu nào tồn tại hoặc thậm chí là ý tưởng hay. Có nhiều cách mà trong đó điều này phá vỡ khả năng mở rộng và đi ngược lại tinh thần thừa kế. Khi bar công khai thừa kế foo, barfoo cho tất cả các mục đích thực tế và mã máy khách không cần phải phân biệt lớp cơ sở và lớp dẫn xuất.

Một typedef nào trong lớp cơ sở thường gãi ngứa các bạn có thể cần phải có trầy xước và là rõ ràng hơn:

class foo { public: typedef foo name_making_type; ... }; 

int main() { 
    Foo::name_making_type a; 
    Bar::name_making_type b; 
} 
+0

Tốt nhất. Tôi chuẩn bị viết cái này. – iammilind

+0

Nó dành cho hệ thống phản chiếu C++ rất nhẹ. Mã "client" thực hiện tuần tự hóa và nhắn tin lớp chung với biến dạng và/hoặc bổ sung tối thiểu có thể cho lớp gốc. – VonRiphenburger

15

Có thể với C++ 11 và decltype. Để làm điều đó, chúng ta sẽ khai thác rằng một con trỏ tới thành viên không phải là một con trỏ vào lớp dẫn xuất khi thành viên được thừa hưởng từ một lớp cơ sở.

Ví dụ:

struct base{ 
    void f(){} 
}; 
struct derived : base{}; 

Loại &derived::f sẽ void (base::*)(), không void (derived::*)(). Điều này đã đúng trong C++ 03, nhưng không thể có được loại lớp cơ sở mà không thực sự xác định nó. Với decltype, thật dễ dàng và chỉ cần chức năng này chút:

// unimplemented to make sure it's only used 
// in unevaluated contexts (sizeof, decltype, alignof) 
template<class T, class U> 
T base_of(U T::*); 

Cách sử dụng:

#include <iostream> 

// unimplemented to make sure it's only used 
// in unevaluated contexts (sizeof, decltype, alignof) 
template<class T, class R> 
T base_of(R T::*); 

struct base{ 
    void f(){} 
    void name(){ std::cout << "base::name()\n"; } 
}; 
struct derived : base{ 
    void name(){ std::cout << "derived::name()\n"; } 
}; 

struct not_deducible : base{ 
    void f(){} 
    void name(){ std::cout << "not_deducible::name()\n"; } 
}; 

int main(){ 
    decltype(base_of(&derived::f)) a; 
    decltype(base_of(&base::f)) b; 
    decltype(base_of(&not_deducible::f)) c; 
    a.name(); 
    b.name(); 
    c.name(); 
} 

Output:

base::name() 
base::name() 
not_deducible::name() 

Như ví dụ cuối cùng cho thấy, bạn cần phải sử dụng một thành viên đó thực sự là thành viên được thừa kế của lớp cơ sở mà bạn quan tâm.

Tuy nhiên, nhiều thành viên khác cũng phải xác định rõ ràng một thành viên cấp cơ sở:

struct base2{ void f(){} }; 

struct not_deducible2 : base, base2{}; 

int main(){ 
    decltype(base_of(&not_deducible2::f)) x; // error: 'f' is ambiguous 
} 

Đó là điều tốt nhất bạn có thể nhận được, không cần hỗ trợ trình biên dịch.

+0

Cảm ơn Xeo, đó là một giải pháp một phần rất thú vị và rất giống với những gì tôi đang tìm kiếm. Tôi sẽ xem xét nó thêm – VonRiphenburger

2

Điều gì với lớp cơ sở? Bạn có phải là lập trình viên .NET hoặc Java không?

C++ hỗ trợ đa kế thừa và cũng không có lớp cơ sở chung chung. Vì vậy, một loại C++ có thể có 0, một hoặc nhiều lớp cơ sở. Do đó, việc sử dụng các bài báo xác định được chống chỉ định.

lớp cơ sở không có ý nghĩa gì, không có cách nào để tìm.

+0

Có những lý do hiệu quả mạnh Java chỉ cho phép một lớp cơ sở và tôi có xu hướng tái cơ cấu mã C++ của tôi vì cùng một lý do - do đó, nó có ý nghĩa như một tập hợp con hữu ích – VonRiphenburger

+0

@VonRiphenburger: : "Đối với một lớp với chính xác một lớp cơ sở ngay lập tức, được kế thừa công khai và không phải là hầu như, có thể khám phá loại lớp cơ sở không?" Và nó vẫn không rõ ràng cho dù bạn đang tìm kiếm các lớp cơ sở ngay lập tức, hoặc tổ tiên khác. Và Java cho phép chỉ có một lớp cơ sở không có gì để làm với hiệu quả, và rất nhiều để làm với mong muốn thiết kế ngôn ngữ để buộc các lập trình viên xuống "một con đường đúng để OOP". Ditto vì thiếu chức năng miễn phí. –

+0

Tôi DID nói cho cơ sở được khai báo đầu tiên. Dường như rõ ràng là cơ sở ngay lập tức đã được tìm kiếm, không phải là tổ tiên ngẫu nhiên, nhưng điểm đó có thể đã được tuyên bố rõ ràng hơn.Um, cảm ơn cho đầu vào của bạn, ben. – VonRiphenburger

0

Tôi đang tìm kiếm một giải pháp di động cho vấn đề tương tự trong nhiều tháng. Nhưng tôi chưa tìm thấy nó.

G ++ có __bases__direct_bases. Bạn có thể bọc chúng trong danh sách loại và sau đó truy cập bất kỳ phần tử nào của nó, ví dụ: a std::tuple với std::tuple_element. Xem libstdc++'s <tr2/type_traits> để sử dụng.

Tuy nhiên, đây không phải là di động. Clang++ currently has no such intrinsics.

1

Với C++ 11, bạn có thể tạo ra một phương pháp xâm nhập luôn luôn có một thành viên base_t, khi lớp học của bạn chỉ được thừa hưởng từ cha hoặc mẹ:

template<class base_type> 
struct labeled_base : public base_type 
{ 
    using base_t = base_type; // The original parent type 
    using base::base; // Inherit constructors 

protected: 
    using base = labeled_base; // The real parent type 
}; 

struct A { virtual void f() {} }; 

struct my_class : labeled_base<A> 
{ 
    my_class() : parent_t(required_params) {} 

    void f() override 
    { 
     // do_something_prefix(); 
     base_t::f(); 
     // do_something_postfix(); 
    } 
}; 

Với lớp đó, bạn sẽ luôn có một parent_t bí danh, để gọi các nhà xây dựng cha mẹ như thể đó là các nhà xây dựng base có tên ngắn hơn (có thể) và bí danh base_t, để làm cho lớp của bạn không biết tên loại lớp cơ sở nếu nó dài hoặc nặng nề.

Bí danh parent_t được bảo vệ để không hiển thị quảng cáo đó trước công chúng. Nếu bạn không muốn bí danh base_t là công khai, bạn luôn có thể kế thừa labeled_base làm protected hoặc private, không cần thay đổi định nghĩa lớp labeled_base.

Cơ sở đó phải có 0 thời gian chạy hoặc không gian trên không vì các phương thức của nó là nội dòng, không làm gì cả và không có thuộc tính riêng.

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