2012-01-03 72 views
9

我正在嘗試使用模板元編程來確定基類。有沒有一種方法可以自動獲取基類,而無需爲每個派生類明確專門化?可以自動從模板類型獲取基類的類型嗎?

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 
} 

現在任何自動方法將需要選擇第一個聲明的基地,並將失敗的私人基地。

+1

使用['std :: is_base_of '](http://stackoverflow.com/questions/2910979/how-is-base-of-works) – iammilind 2012-01-03 07:57:41

+3

@iammilind:這僅用於測試如果一個類是基礎另一個,你必須知道基類已經測試過。 – Xeo 2012-01-03 07:58:09

+4

你需要什麼?我不認爲這是可能的,但也許有解決實際問題的不同方法。 – 2012-01-03 08:06:49

回答

5

我的解決方案並不是真正的自動化,但我能想到的最好。

侵入C++ 03的解決方案:

class B {}; 

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

非侵入式的C++ 03的解決方案:

class B {}; 

class A : public B {}; 

template<class T> 
struct TypeInfo; 

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

這幾乎是我想出來的。我希望有一些鐵桿模板技巧,我沒有意識到可以提取它..我可能會使用具有完全創建控件的類的入侵式窗體和半外部類的非入侵式窗體 - 謝謝 – VonRiphenburger 2012-01-04 05:18:09

5

我不知道任何基類選擇模板,我不確定是否存在或甚至是一個好主意。有很多方式破壞可擴展性並違背繼承的精神。當bar公開繼承foo時,barfoo出於所有實際目的,客戶端代碼不應區分基類和派生類。

在基類公共的typedef經常刮花你可能需要有劃傷癢和更清晰:

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

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

好的。我正要寫這個。 – iammilind 2012-01-03 08:22:49

+0

這是一個非常輕的C++反射系統。 「客戶」代碼可以對原始類進行最小可能的變形和/或添加,從而進行通用類序列化和消息傳遞。 – VonRiphenburger 2012-01-03 20:25:07

15

有可能與C++ 11和decltype。爲此,當成員從基類繼承時,我們將利用指向成員的指針不是指向派生類的指針。

例如:

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

類型的&derived::fvoid (base::*)(),不void (derived::*)()。這在C++ 03中已經是真實的,但是如果沒有真正指定它,就不可能獲得基類的類型。隨着decltype,這很容易,只需要這個小功能:

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

用法:

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

輸出:

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

作爲最後一個例子顯示,你需要使用一個部件,其實際上是您感興趣的基類的繼承成員。

再有更多的缺陷,但是:成員也必須明確地標識一個基類成員:

struct base2{ void f(){} }; 

struct not_deducible2 : base, base2{}; 

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

這是你能得到,雖然是最好的,沒有編譯器支持。

+0

謝謝Xeo,這是一個非常有趣的部分解決方案,與我正在尋找的非常相似。我會進一步研究 – VonRiphenburger 2012-01-04 05:22:06

2

什麼是基類?你是.NET還是Java程序員?

C++支持多重繼承,也沒有一個全球共同的基類。所以一個C++類型可能有零個,一個或多個基類。因此禁止使用定冠詞。

由於基類沒有意義,所以無法找到它。

+0

效率很高Java只允許一個基類,並且我傾向於重構我的C++代碼,因爲它是有用的子集 – VonRiphenburger 2012-01-03 20:15:57

+0

@VonRiphenburger:然後問題需要聲明:「對於只有一個直接基類的類,公開且非虛擬地繼承,是否可以發現基類的類型?」目前還不清楚你是在尋找直接的基礎班還是另一個祖先。而只允許一個基類的Java與效率毫無關係,並且與想要通過語言設計迫使編碼器停止「OOP的真正路徑」有很大關係。同樣缺乏免費功能。 – 2012-01-03 20:49:30

+0

我爲第一個宣佈的基地說。似乎顯而易見的是立即尋找基地,而不是隨機的祖先,但這一點本來可以更清楚地表明。恩,謝謝你的意見,本。 – VonRiphenburger 2012-01-04 04:57:33

0

我尋找類似問題的便攜式解決了幾個月。但我還沒有找到它。

G ++有__bases__direct_bases。您可以將它們包裝在一個類型列表中,然後訪問它的任何一個元素,例如std::tuplestd::tuple_element。有關使用情況,請參見libstdc++'s <tr2/type_traits>

但是,這不是便攜式。 Clang++ currently has no such intrinsics.

1

用C++ 11,你可以創建一個侵入性的方法來始終有一個base_t成員,當你的類只能從一個父繼承:

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(); 
    } 
}; 

與該類,你將永遠有一個parent_t別名,如果它是具有(可能)較短名稱的base構造函數和一個base_t別名來調用父構造函數,以使您的類不知道基類類型名稱,如果它長或模板嚴重。

parent_t別名受保護,不會向公衆公開。如果你不想base_t別名是公開的,你可以繼續使用labeled_base作爲protectedprivate,不需要改變labeled_base類的定義。

該基地應該有0運行時間或空間開銷,因爲它的方法是內聯,什麼都不做,並且沒有自己的屬性。