2013-04-05 51 views
3

我有一棵樹,其中每個節點基本上是這樣的:如何從TYPE_INFO模板獲得了類型參數

struct node 
{ 
    std::unordered_set<object*> objects; 
    std::map<std::type_index,node> children; 
}; 

當我遍歷樹添加一個新的類型,我想要做的檢查:

std::is_base_of<base,derived> 

但是,我對衍生類型唯一的信息是type_index/type_info*

是否有反正我可以將type_info*轉換爲template參數?

如果不是,我的其他選擇是什麼?我想可能會打電話decltype(*objects.begin()),但是這需要每個節點中的每個集合都不能爲空。

+1

模板是編譯時評估的,你有一個'type_info'表示對象的運行時類型。這應該如何實現?你已經知道它們的靜態類型,它是'object *'(這也是'decltype(* objects.begin())'將會返回),但這對你沒有任何幫助。所以模板完全沒有問題,因爲解決了完全不同的問題,不能用於RTTI。 – 2013-04-05 11:51:43

回答

4

如果我正確地理解了你,你想要一個類型爲inheritance_checker,這樣它的每個實例都與一個類型相關聯,但是inheritance_checker本身不是。類似於type_ifo,但可以在運行時檢查繼承關係。例如,你想以下工作:

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

// Note that a and b have the same type but they are "related" to distinct types. 
inheritance_checker a = inheritance_checker::create<A>(); 
inheritance_checker b = inheritance_checker::create<B>(); 

assert( a.is_base_of (b)); 
assert( a.derives_from(a)); // derives from or is equal to 
assert(! b.is_base_of (a)); 
assert( b.derives_from(b)); // derives from or is equal to 

如果是這樣的話,我可以建議你的東西,不幸的是,可能很慢!這取決於例外。

基本的想法是,如果你拋出B*然後catch (A*)是匹配。因此,我們給inheritance_checker投擲和捕捉在施工時給出的類型的指針的能力。但inheritance_checker不是模板,因此必須以類型擦除的方式提供此容量。這可以通過存儲具有固定簽名的函數的指針來實現,該簽名不依賴於在構造時傳遞的類型,但能夠引發/捕獲指定類型的指針。下面給出了inheritance_checker的可能實現。

#include <cassert> 

class inheritance_checker { 

    typedef void (*thrower_t)(); 
    typedef bool (*catcher_t)(thrower_t); 

public: 

    template <typename T> 
    static inheritance_checker create() { 
     return inheritance_checker(concrete_thrower<T>, concrete_catcher<T>); 
    } 

    bool is_derived_from(const inheritance_checker& other) const { 
     return other.catcher_(thrower_); 
    } 

    bool is_base_of(const inheritance_checker& other) const { 
     return catcher_(other.thrower_); 
    } 

private: 

    template <typename T> 
    static void concrete_thrower() { 
     throw static_cast<T*>(nullptr); 
    } 

    template <typename T> 
    static bool concrete_catcher(thrower_t thrower) { 
     try   { thrower(); } 
     catch (T*) { return true; } 
     catch (...) {    } 
     return false; 
    } 

    inheritance_checker(thrower_t thrower, catcher_t catcher) : 
     thrower_(thrower), catcher_(catcher) { 
    } 

    thrower_t thrower_; 
    catcher_t catcher_; 
}; 

class A {}; 
class B : public A {}; 
class C : public B {}; 
class D {}; 

int main() { 

    auto a = inheritance_checker::create<A>(); 
    auto b = inheritance_checker::create<B>(); 
    auto c = inheritance_checker::create<C>(); 
    auto d = inheritance_checker::create<D>(); 

    assert(a.is_base_of(a)); 
    assert(a.is_base_of(b)); 
    assert(a.is_base_of(c)); 
    assert(!a.is_base_of(d)); 

    assert(a.is_derived_from(a)); 
    assert(!a.is_derived_from(b)); 
    assert(!a.is_derived_from(c)); 
    assert(!a.is_derived_from(d)); 

    assert(!b.is_base_of(a)); 
    assert(b.is_base_of(b)); 
    assert(b.is_base_of(c)); 
    assert(!b.is_base_of(d)); 

    assert(b.is_derived_from(a)); 
    assert(b.is_derived_from(b)); 
    assert(!b.is_derived_from(c)); 
    assert(!b.is_derived_from(d)); 

    assert(!c.is_base_of(a)); 
    assert(!c.is_base_of(b)); 
    assert(c.is_base_of(c)); 
    assert(!c.is_base_of(d)); 

    assert(c.is_derived_from(a)); 
    assert(c.is_derived_from(b)); 
    assert(c.is_derived_from(c)); 
    assert(!c.is_derived_from(d)); 

    assert(!d.is_base_of(a)); 
    assert(!d.is_base_of(b)); 
    assert(!d.is_base_of(c)); 
    assert(d.is_base_of(d)); 

    assert(!d.is_derived_from(a)); 
    assert(!d.is_derived_from(b)); 
    assert(!d.is_derived_from(c)); 
    assert(d.is_derived_from(d)); 
} 

如果你願意,你可以一個type_info*成員添加到inheritance_checker,以獲得額外的功能,type_info提供。

請注意is_base_ofderives_from之間的對稱性。其實,你可以刪除其中的一個。

我建議你閱讀this文章。

+1

這真的很棒!我沒有完全遵循這一點,但它真的給了我想法!這裏真正重要的是函數指針。我通過給節點創建一個lambda表達式來實現我所需要的(就像你的inheritance_checker :: create <>)。 lambda通過模板創建並調用dynamic_cast 。然後我可以調用lambda WITHOUT模板。謝謝。我知道可以混合使用靜態和動態輸入! – David 2013-04-05 21:24:14

+1

@David Jensen:的確,如果你考慮的所有類型都來自同一個多態基類,那麼像我一樣,使用'dynamic_cast'比'throw' /'catch'更有效。 'throw' /'catch'技巧的優勢在於它更通用,但付出的代價是速度更慢。 – 2013-04-06 16:31:37

6

是否有反正我可以將type_info*轉換爲模板參數?

不,沒有辦法。模板是編譯時的事情,RTTI是運行時的。兩者之間沒有任何聯繫。

我猜可能會打電話給decltype(*objects.begin()),但是這需要每個節點中的每個集合都不能爲空。

它不會要求。 decltype不評估它的論點 - 它不需要。它只需要檢查類型。你可以愉快地做decltype(*a_null_pointer)而不調用UB,因爲表達式不會求 - 這就是所謂的未評估的背景下sizeof屬於同一類別。

請注意,這並不會給你買太多,但是 - 你只會得到object*&。如果不先準備映射,通常無法從運行時信息中獲取類型。

+0

decltype(base *)是否給我「真正」的鏤空類型?因爲我的對象集*確實擁有比對象*更多的定義類型。 – David 2013-04-05 11:49:18

+1

@David:哦,不,顯然不是,因爲這只是一個編譯時的事情。 :)如果不事先準備映射,則無法從運行時信息中獲取類型。 – Xeo 2013-04-05 11:51:24

+1

您可能會明確地在第二段中添加'decltype(* objects.begin())',根本不會購買任何東西,只要返回'object *'即使您的第一段已經說明了這一點。 – 2013-04-05 11:54:05

4

is_base_ofdecltype純粹是編譯時構造;他們不會反映你的對象的動態類型。

type_info不提供檢查子類關係的工具;在運行時執行該檢查的唯一方法是使用dynamic_cast。您需要存儲unique_ptr<base>並查看它是否可以將dynamic_cast指定爲派生類型。

+0

不幸的是,我猜你的權利。該死的這會讓我的樹結構變慢。 – David 2013-04-05 11:55:29

+0

但仍然dynamic_cast需要一個真正的類型,而不是一個type_info *。我回到了原點。 – David 2013-04-05 12:00:37

+0

@David這個想法是完全擺脫'type_info'。 – 2013-04-05 12:14:46