5

假設我們有以下的模板類幫助型性狀

template<typename T> class Wrap { /* ... */ }; 

我們不能改變Wrap。這很重要。

讓我們從Wrap<T>派生類。例如,

class NewInt : public Wrap<int>  { /* ... */ }; 
class MyClass : public Wrap<myclass> { /* ... */ }; 
class Foo  : public Wrap<Bar>  { /* ... */ }; 

我們不能改變這些類了。以上所有課程均爲第三方。他們不是我的。

我需要以下編譯時type_traits

template<class T> 
struct is_derived_from_Wrap { 
    static const bool value = /* */; 
}; 

我需要做什麼?

assert(is_derived_from_Wrap<Int>::value == true); // Indeed I need static assert 
assert(is_derived_from_Wrap<MyClass>::value == true); 
assert(is_derived_from_Wrap<char>::value == false); 
struct X {}; 
assert(is_derived_from_Wrap<X>::value == false); 
+0

但是你能改變'Int'和'MyClass'嗎? :p – kennytm 2010-01-14 08:22:59

+0

不,謝謝你的提示。 – 2010-01-14 08:24:23

+2

你的類型特徵不會有更好的命名:'has_Wrap_for_base'?實際上,MyClass並不是Wrap的基礎。 – 2010-01-14 08:31:57

回答

9

如果你不知道怎麼回事就可以做到這一點使用SFINAE但它的那種神奇......

template<typename T> class Wrap { }; 

struct myclass {}; 
struct X {}; 

class Int  : public Wrap<int>  { /* ... */ }; 
class MyClass : public Wrap<myclass> { /* ... */ }; 

template< typename X > 
struct is_derived_from_Wrap 
{ 
    struct true_type { char _[1]; }; 
    struct false_type { char _[2]; }; 

    template< typename U > 
    static true_type test_sfinae(Wrap<U> * w); 
    static false_type test_sfinae(...); 

    enum { value = sizeof(test_sfinae((X*)(0)))==sizeof(true_type) }; 
}; 


#include <iostream> 
#define test(X,Y) std::cout<<(#X " == " #Y)<<" : "<<((X)?"true":"false") <<std::endl; 

int main() 
{ 
    test(is_derived_from_Wrap <Int>::value, true); 
    test(is_derived_from_Wrap <MyClass>::value, true); 
    test(is_derived_from_Wrap <char>::value, false); 
    test(is_derived_from_Wrap <X>::value, false); 
} 

這給預期的輸出

is_derived_from_Wrap <Int>::value == true : true 
is_derived_from_Wrap <MyClass>::value == true : true 
is_derived_from_Wrap <char>::value == false : false 
is_derived_from_Wrap <X>::value == false : false 

有一對夫婦的陷阱用我的代碼。如果類型是Wrap,它也會返回true。

assert( is_derived_from_Wrap< Wrap<char> >::value == 1); 

如果需要,可以使用更多的SFINAE魔術來解決這個問題。

它將返回false,如果推導不是公共派生(即是私人或保護)

struct Evil : private Wrap<T> { }; 
assert(is_derived_from_Wrap<Evil>::value == 0); 

我懷疑這不能固定。 (但我可能是錯的)。但我懷疑公有繼承就夠了。

+0

我想,那麼簡單的部分專業化,因爲在我的代碼將無法正常工作? – sbi 2010-01-14 09:29:37

+0

正確,因爲只有類型完全匹配,專業化纔會匹配。 Your is_Wrap < Wrap> :: value = true,但是is_Wrap :: value = false。 – 2010-01-14 09:48:53

+0

令人驚歎。我曾經認爲''test_sfinae(Wrap * w);'''X *'不能推導出'U'。 – 2010-01-14 11:38:49

0

下決定的東西是否一次總結:

template<class T> 
struct is_Wrap { static const bool value = false; }; 

template<typename T> 
struct is_Wrap< Wrap<T> > { static const bool value = true; }; 

由於推導的是-A的關係,從Wrap<T>衍生的一切也是一個Wrap<T>,應由本作中找到。

+0

我的名字'Wrap'的基礎是錯誤的。我的英語不好。當然,「從包裝中派生」。 – 2010-01-14 08:57:46

+0

這不是很好的解決方案。然後我需要爲每個班級制定DEFINE。我不知道他們全部。我想確認X是否來自Wrap ,其中Y可以是任何類型。 – 2010-01-14 09:05:40

+0

@Alexey:我會嘗試相應地調整我的代碼。但是你一定要在問你的問題時更加精確。 (這不僅僅是一個語言障礙問題。) – sbi 2010-01-14 09:19:42

0

您需要做一些相當複雜的模板元編程來確定一個類X是否從另一個Y派生出來,在一般情況下。基本上,X與Y派生如果:

  1. Y可以被隱式轉換爲X
  2. X和Y不是同一類型

Andrei Alexandrescu解釋如何做到這一點(與許多其他模板竅門)在他的書「現代C++設計」中。

您可以在由Alexandrescu編寫的Loki庫或uSTL實現中找到解決問題的代碼。

+1

重新閱讀這個問題後,我意識到我的建議並不能解決問題。我們不想知道X是否來自Y,但是X是否來自Y ,其中T可以是任何類型。 – 2010-01-14 08:47:22

+0

是的。你的答案不是解決方案。但是你已經意識到了這個問題。 – 2010-01-14 09:10:03

+0

你也可以引用boost :: is_base_of (由於Base參數不能是類模板,所以它沒有解決OP的問題)。 – 2010-01-14 10:40:11