2010-04-13 91 views
16

一位同事最近向我展示了他在網上找到的一些代碼。它似乎允許編譯時確定一個類型是否與另一個類型有「是」關係。我認爲這是完全可怕的,但我不得不承認,我對這種實際工作方式毫無頭緒。任何人都可以向我解釋這個嗎?C++編譯時間類型確定

template<typename BaseT, typename DerivedT> 
inline bool isRelated(const DerivedT&) 
{ 
    DerivedT derived(); 
    char test(const BaseT&); // sizeof(test()) == sizeof(char) 
    char (&test(...))[2]; // sizeof(test()) == sizeof(char[2]) 
    struct conversion 
    { 
     enum { exists = (sizeof(test(derived())) == sizeof(char)) }; 
    }; 
    return conversion::exists; 
} 

一旦這個函數的定義,你可以使用它像這樣:

#include <iostream> 

class base {}; 
class derived : public base {}; 
class unrelated {}; 

int main() 
{ 
    base b; 
    derived d; 
    unrelated u; 

    if(isRelated<base>(b)) 
     std::cout << "b is related to base" << std::endl; 

    if(isRelated<base>(d)) 
     std::cout << "d is related to base" << std::endl; 

    if(!isRelated<base>(u)) 
     std::cout << "u is not related to base" << std::endl; 
} 
+0

這是相當該死的酷伏都教。 – zneak 2010-04-13 23:06:04

+0

+1令人敬畏的技巧。 – SLaks 2010-04-13 23:11:38

+7

如果您對這些東西感興趣,請獲取Alexandrescus *「Modern C++ Design」*的副本。 – 2010-04-13 23:12:10

回答

11

它聲明瞭兩個重載的函數,名爲test,一個取值爲Base,另一個取值爲(...),返回不同的類型。

然後它調用函數Derived並檢查其返回類型的大小以查看調用了哪個超載。 (它實際上調用函數的返回值,返回值爲Derived,以避免使用內存)

因爲enum是編譯時常量,所有這些都是在編譯時在類型系統內完成的。由於這些函數最終不會在運行時被調用,因此它們沒有主體並不重要。

+0

'derived()'不是用於保存內存 - 無論如何sizeof()'表達式在編譯時計算 - 這是一種不需要'Derived'是默認可構造的方法避免使用'test(Derived())')。 – 2010-04-13 23:37:25

+0

@gf:我的意思是與聲明一個指向「Dervied」的指針相反。 – SLaks 2010-04-13 23:40:51

6

我沒有C++高手,但它看起來對我來說,關鍵是讓編譯器之間作出選擇兩個超載test()。如果Derived來自Base那麼將使用第一個,其將返回char,否則將使用第二個 - 其返回char[2]。然後sizeof()運營商確定發生了哪些情況並相應地設置了值conversion::exists

+0

true..its僅基於重載分辨率。 – mukeshkumar 2010-04-14 07:28:16

+0

不錯的解釋:) – 2013-07-02 10:02:36

5

這很酷,但它並不實際工作,因爲用戶定義的轉換優先於省略號匹配,並且const引用可以綁定用戶定義轉換的臨時結果。所以char*p; is_related<bool>(p);將返回true(在VS2010中測試)。

如果您想真正測試繼承關係,可以採取類似的方法,但使用指針而不是引用。

2

順便說一句,您可以使用std::tr1(MSCV 2008編譯器對此具有固有支持)中引入的「type_traits」中的__is_base_of

5

是否有任何理由,你不會用這樣的事情,而不是:

template<typename BaseT, typename DerivedT> 
struct IsRelated 
{ 
    static DerivedT derived(); 
    static char test(const BaseT&); // sizeof(test()) == sizeof(char) 
    static char (&test(...))[2]; // sizeof(test()) == sizeof(char[2]) 

    enum { exists = (sizeof(test(derived())) == sizeof(char)) }; 
} 

例如爲:

IsRelated<Base, Derived>::exists 

你必須在編譯時獲取信息的方式。

1

原始代碼將構造派生的對象,它可能會帶來意想不到的結果。下面的工作可能是一個替代選擇:

template<typename BaseT, typename CheckT> 
inline bool isDerived(const CheckT &t){ 
    char test(const BaseT *t); 
    char (&test(...))[2]; 
    return (sizeof(test(&t)) == sizeof(char)); 
}