2009-07-10 62 views
7

我們有一個子項目'commonUtils',它有許多在父項目中使用的通用代碼片段。 一個這樣有趣的東西我看到的是: -測試一個類是否是多態的

/********************************************************************* 
If T is polymorphic, the compiler is required to evaluate the typeid 
stuff at runtime, and answer will be true. If T is non-polymorphic, 
the compiler is required to evaluate the typeid stuff at compile time, 
whence answer will remain false 
*********************************************************************/ 
template <class T> 
bool isPolymorphic() { 
    bool answer=false; 
    typeid(answer=true,T()); 
    return answer; 
} 

我相信評論,認爲這是一個相當有趣的模板,雖然它不是在整個項目中使用 。我試圖用它只是爲了好奇...

class PolyBase { 
public: 
    virtual ~PBase(){} 
}; 

class NPloyBase { 
public: 
    ~NBase(){} 
}; 


if (isPolymorphic<PolyBase>()) 
    std::cout<<"PBase = Polymorphic\n"; 
if (isPolymorphic<NPolyBase>()) 
    std::cout<<"NBase = Also Polymorphic\n"; 

但是,這些都沒有返回true。 MSVC 2005沒有提供警告,但Comeau警告typeid表達式沒有效果。 C++標準中的第5.2.8節並沒有說出什麼像評論說的那樣,即typeid是在編譯時爲非多態類型和在運行時爲多態類型評估的。

1)所以我想這個評論是誤導性的/錯誤的,或者因爲這段代碼的作者是相當高級的C++程序員,我錯過了什麼嗎?

2)OTOH,我想知道是否可以使用某種技術測試一個類是否是多態的(至少有一個虛函數)?

3)何時想知道某個類是否爲多態?胡亂猜測;通過使用dynamic_cast<void*>(T)來獲得類的起始地址(因爲dynamic_cast僅適用於多態類)。

等待您的意見。

由於提前,

+0

呃,如果作者是高級C++程序員,爲什麼不先和他覈對一下? ...你會經常從有經驗的人那裏學到很多東西。 – stefanB 2009-07-10 06:13:42

+9

那麼,如果我可以我不會問它在stackoverflow :-) – Abhay 2009-07-10 06:16:46

回答

8

我無法想象任何可能的方式是如何的typeid可以用來檢查類型是多態的。它甚至不能用來聲明它,因爲typeid可以用於任何類型。 Boost有一個實現here。至於爲什麼它可能是必要的 - 我知道的一個案例是Boost.Serialization庫。如果你正在保存非多態類型,那麼你可以保存它。如果要保存多態,則必須使用typeid獲取其動態類型,然後調用該類型的序列化方法(在某個表中查找它)。

更新:看來我其實是錯的。考慮這個變體:

template <class T> 
bool isPolymorphic() { 
    bool answer=false; 
    T *t = new T(); 
    typeid(answer=true,*t); 
    delete t; 
    return answer; 
} 

這實際上的確如名稱暗示的那樣工作,完全按照原始代碼段中的評論。如果「不指定多態類類型的左值」(std 3.2/2),則不會評估typeid中的表達式。因此,在上面的情況下,如果T不是多態,則不評估typeid表達式。如果T是多態的,那麼* t確實是多態類型的左值,所以必須評估整個表達式。

現在,您的原始示例仍然是錯誤的:-)。它使用T(),而不是*t。和T()創建右值(標準差3.10/6)。所以,它仍然產生一個不是「多態類左值」的表達式。

這是一個相當有趣的技巧。另一方面,它的實用價值是有限的 - 因爲雖然boost :: is_polymorphic給你一個編譯時常量,這個給你一個運行時值,所以你不能爲多態和非多態類型實例化不同的代碼。

3


class PolyBase { 
public: 
    virtual ~PolyBase(){} 
}; 

class NPolyBase { 
public: 
    ~NPolyBase(){} 
}; 

template<class T> 
struct IsPolymorphic 
{ 
    struct Derived : T { 
     virtual ~Derived(); 
    }; 
    enum { value = sizeof(Derived)==sizeof(T) }; 
}; 


void ff() 
{ 
    std::cout << IsPolymorphic<PolyBase >::value << std::endl; 
    std::cout << IsPolymorphic<NPolyBase>::value << std::endl; 
} 

+1

我不知道這是否完全是傻瓜證明。編譯器可以在子對象之間添加填充,這種情況下,sizeof()技巧將無法工作。 – Abhay 2009-07-10 07:05:56

-1

我在這裏有點困惑,並希望得到一些關於這個答案的評論,解釋我失蹤了。

當然,如果你想知道一個類是否是多態的,你所要做的就是詢問它是否支持dynamic_cast,是不是正確?

template<class T, class> struct is_polymorphic_impl : false_type {}; 
template<class T> struct is_polymorphic_impl 
    <T, decltype(dynamic_cast<void*>(declval<T*>()))> : true_type {}; 

template<class T> struct is_polymorphic : 
    is_polymorphic_impl<remove_cv_t<T>, void*> {}; 

任何人都可以指出這個實現中的缺陷嗎?我想在過去的某個時間點必須有一個,或者肯定有一個,因爲the Boost documentation仍然聲稱is_polymorphic「不能用C++語言輕鬆實現」。

但「便攜」是一種黃鼠狼的詞,對吧?也許他們只是暗指MSVC如何不支持表達式SFINAE,或者一些方言如Embedded C++不支持dynamic_cast。也許當他們說「C++語言」時,他們的意思是「C++語言的最低公共分母子集」。但我有一個嘮叨的懷疑,也許他們的意思是他們說的,我是誰錯過了什麼。

OP中的typeid方法(經稍後使用左值而不是右值的答案修改)也似乎很好,但它當然不是constexpr,它需要實際構建一個T,這可能會非常昂貴。所以這個dynamic_cast的方法似乎更好......除非它由於某種原因不起作用。思考?

相關問題