2012-08-09 24 views
2

用戶通過定義指定所需選項的類來定製庫模板類。稱之爲清單。這個想法是在清單中有可選的typedef。例如,如果用戶的清單包含H的typedef,我希望庫代碼使用指定的類型作爲其「H」。如果用戶清單中沒有typedef,庫將使用默認值。C++如何通過可選的typedef使用SFINAE或其他來選擇類

我懷疑有一個優雅的方式來利用新的C++ 11功能來做到這一點,但我現在空了。我有一個解決方案,基於SFINAE的維基百科條目。這是醜陋的。它需要一個新的模板功能has_typedef_H對於每個新的H.我含糊其辭地認爲它利用了可以指整數或空指針的屬性。只是似乎太糟糕了。

有沒有更好的方法?最好能用VC++ 2010工作嗎?

在玩具的例子中有H1,H2,U0,U1和U2五類。 H1和H2是庫類L的「助手」的例子。H1是默認值。 U是用戶定義的類的示例。在這個例子中,我省略了定義庫類L,只是使用main()的主體來根據U中的typedefs(或缺少)來選擇H。 H2類型的主題。

 

struct H1{ 
    void operator()(){ std::cout << "H1" << std::endl;} 
}; 

struct H2{ 
    void operator()(){ std::cout << "H2" << std::endl;} 
}; 


struct default_H: public H1 {}; 

struct U2 { 
    typedef H2 H; 
}; 

struct U1 { 
    typedef H1 H; 
}; 

struct U0 { 
}; 


template <typename T> 
class has_typedef_H { 

    typedef char no[false+1]; 
    typedef char yes[true+1]; 

    template 
    static yes& test(typename C::H*); 

    template 
    static no& test(...); 

public: 
    static const bool value = sizeof(test(0))-1; 
}; 


template<typename U, bool > 
struct type_H_B: public default_H{}; 

template<typename U> 
struct type_H_B<U, true>: public U::H {}; 

template<typename U> 
struct H_type: public type_H_B<U, has_typedef_H<U>::value> {}; 

int main() { 

    H_type<U0> h0; 
    H_type<U1> h1; 
    H_type<U2> h2; 

    // Prints H1 H1 H2 
    h0(); 
    h1(); 
    h2(); 
    return 0; 
} 
 

回答

3

你並不真的需要爲嵌套類型中的每一個,可以做一個簡單一點複合性狀。 Here是一個例子:

// Helper to map any type to void, needed by SFINAE below 
template <typename T> 
struct void_type { 
    typedef void type; 
}; 

// Selects a nested typedef or a default type D (using a macro to reduce boilerplate): 
#define SELECT_NESTED_TYPE(TYPE)          \ 
template <typename T, typename D, typename _ = void>      \ 
struct select_##TYPE{             \ 
    typedef D type;              \ 
};                  \ 
template <typename T, typename D>          \ 
struct select_##TYPE<T, D, typename void_type<typename T::TYPE>::type> { \ 
    typedef typename T::TYPE type;          \ 
};                  

SELECT_NESTED_TYPE(int_t); 
SELECT_NESTED_TYPE(float_t); 
//... 
#undef SELECT_NESTED_TYPE 

// Use 
template <typename T> 
class TheTemplate { 
public: 
    typedef typename select_int_t<T,int>::type int_t; 
    typedef typename select_float_t<T,double>::type float_t; 
    //.... 
}; 

// Test: 
template <typename T, typename U> struct same_type { 
    static const bool value = false; 
}; 
template <typename T> struct same_type<T,T> { 
    static const bool value = true; 
}; 
struct test1 { 
}; 
struct test2 { 
    typedef long long int_t; 
    typedef float float_t; 
}; 
int main() { 
    // test1 has the default typedefs 
    assert((same_type< TheTemplate<test1>::int_t, int>::value)); 
    assert((same_type< TheTemplate<test1>::float_t, double>::value)); 
    // test2 has the ones in the type 
    assert((same_type< TheTemplate<test2>::int_t, long long>::value)); 
    assert((same_type< TheTemplate<test2>::float_t, float>::value)); 
} 

你可以選擇,如果宏接受默認類型使用和噴射在默認情況下(沒有定義嵌套類型時),以提供一個稍微簡單的解決方案。無可否認,這需要爲每個嵌套類型創建特徵,但特徵僅僅是幾行(並且不太難以定義爲宏)。或者,如果只有幾個潛在的typedef,您可以在沒有額外樣板的情況下執行,並直接在目標類型上使用SFINAE。


完全不同的方法......如果你能

如果你能修改在圖書館使用的類型,那麼你可以使用一個更簡單的(雖然沒有那麼解決方案)通過濫用繼承權。創建一個基類,只保留要使用的默認類型的類型定義,並讓每個用戶類都從提供默認類的類派生。如果用戶想要提供比默認更好的幫助,他們只需要提供typedef。如果他們不提供一個typedef,查找會發現默認越往上層次:

struct default_helpers { 
    typedef Helper1 helper1_t; 
    typedef Helper2 helper2_t; 
// ... 
}; 
struct user_type_1 : default_helpers { 
}; 
struct user_type_2 : default_helpers { 
    typedef MyHelper helper1_t;   // I prefer this one... 
}; 
int main() { 
    assert(same_type< user_type1::helper1_t, default_helpers::helper1_t >::value); 
    assert(!same_type< user_type2::helper1_t, default_helpers::helper1_t >::value); 
    assert(same_type< user_type1::helper2_t, user_type2::helper2_t>::value); 
} 
+0

這兩項的解決方案發生在我身上。我不太喜歡從一類默認值中派生出來,因爲在典型情況下,用戶甚至不需要知道這些東西。 「清單」只是一個函數對象,實現了他想要最小化的多元函數。自從我使用#macros以來,我已經忘記了如何將字符串拼湊在一起。 :-)直到下一組很酷的功能被添加到C++(C++ 25?),這可能是最好的。甚至可能在之後。 – 2012-08-09 04:18:39

+0

另一個解決方案,也許最好的方法是使用type_traits的方式。這是非侵入性的。但是這個答案並沒有解決這個難題。我愛拼圖。 – 2012-08-09 04:30:21

+0

@JiveDadson:第一種方法*是* type_traits方法。這個類型被傳遞給一個提取特徵的函數,它可以是一個默認類型,也可以是嵌套類型......在等待新標準時,如果* static if *前進,這可能是一個選項,但關於這個特定問題我不認爲它會增加除簡單語法之外的任何其他內容。一切(我相信),可以通過濫用繼承來解決* static中的* static *。 – 2012-08-09 04:36:27

0

由於大衛的answer表明,宏將有助於減少樣板代碼。在我看來,David的方法是優越的,但對於新的元程序員來說,這可能令人望而生畏。因此,我想通過提供可能稍微更加明確的潛在替代方法來幫助清理一些kludger。


當無的情況下是不是一個模板,0被視爲int

template <typename T> 
class has_typedef_H { 
    typedef char no[1]; 
    typedef char yes[2]; 

    template <typename C> 
    static yes& test(typename C::H*); 

// template <typename> 
    static no& test(...); 

public: 
    static const bool value = sizeof(yes) == sizeof(test(0)); 
}; 

在這種example

  • 的是情況進行檢查與C是推斷爲int,導致替代總是失敗,並且int::H。在嘗試使用0作爲表示空指針的值之前會發生這種情況,因爲函數簽名仍在嘗試識別。
  • 使用可變參數int檢查無例。這總是被選中,因爲yes-case總是失敗。

當無案件變成模板函數時,沒有模板參數可以推導出來,所以沒有案例不是候選者。

template <typename T> 
class has_typedef_H { 
    typedef char no[1]; 
    typedef char yes[2]; 

    template <typename C> 
    static yes& test(typename C::H*); 

    template <typename> 
    static no& test(...); 

public: 
    static const bool value = sizeof(yes) == sizeof(test(0)); 
}; 

可以看出here,因爲沒有匹配的功能是發現test(int)編譯失敗。由於扣除不適用於無的情況下,test調用必須明確選擇要使用的專業化:

template <typename T> 
class has_typedef_H { 
    typedef char no[1]; 
    typedef char yes[2]; 

    template <typename C> 
    static yes& test(typename C::H*); 

    template <typename> 
    static no& test(...); 

public: 
    static const bool value = sizeof(yes) == sizeof(test<T>(0)); 
}; 

在這種example0的雙交涉並開始發揮作用。

  • 在肯定的情況下,0檢查作爲T::H*有效的初始值。
  • 在無情況下,0int

爲了除去這種雙重表示,一個int參數被添加到是 - 的情況下,和C::H*參數設置的NULL的默認值。

template <typename T> 
class has_typedef_H { 
    typedef char no[1]; 
    typedef char yes[2]; 

    template <typename C> 
    static yes& test(int, typename C::H* = NULL); 

    template <typename> 
    static no& test(...); 

public: 
    static const bool value = sizeof(yes) == sizeof(test<T>(1)); 
}; 

在這種exampletest<T>()可以與任何整數被調用。

  • 在肯定的情況下,1int,並C::H*明確設置爲NULL
  • 在沒有的情況下,1也是int

由於sizeof正在評估的表達式,並且不執行它,它能夠避免在編譯時已知的值傳遞給test,如圖this例子。

template <typename T> 
class has_typedef_H { 
    typedef char no[1]; 
    typedef char yes[2]; 

    template <typename C> 
    static yes& test(T*, typename C::H* = NULL); 

    template <typename> 
    static no& test(...); 

    static T* t; 

public: 
    static const bool value = sizeof(yes) == sizeof(test<T>(t)); 
}; 

最後,我發現使用0是在SFINAE相當普遍的情況。有了這個說法,如果有人對使用0的方法有了解,那麼稍微更明確的方法應該不難理解。

0

除了已經發布的優秀作品之外,另一種方法是使用很少使用的dominance in virtual inheritance feature。我顯示了兩個定製的類型,其可以被擴展用於任意數量的N(受基類編譯器限制的數目)中的溶液:

#include <stdio.h> 

struct A1 {}; 
struct A2 {}; 
struct B1 {}; 
struct B2 {}; 

struct DefaultManifest { 
    typedef A1 A; 
    typedef B1 B; 
}; 

template<class T> 
struct A_is : virtual DefaultManifest { typedef T A; }; 

template<class T> 
struct B_is : virtual DefaultManifest { typedef T B; }; 


template<class T> struct Name { static char const* value; }; 
template<> char const* Name<A1>::value = "A1"; 
template<> char const* Name<A2>::value = "A2"; 
template<> char const* Name<B1>::value = "B1"; 
template<> char const* Name<B2>::value = "B2"; 

struct na1 : virtual DefaultManifest {}; 
struct na2 : virtual DefaultManifest {}; 

template<class T1 = na1, class T2 = na2> 
struct Library { 
    struct Manifest : T1, T2 {}; 
    typedef typename Manifest::A A; 
    typedef typename Manifest::B B; 

    Library() { 
     printf("A is %s, B is %s\n", Name<A>::value, Name<B>::value); 
    } 
}; 

int main() { 
    Library<> lib1; 
    Library<A_is<A2> > lib2; 
    Library<B_is<B2> > lib3; 
    Library<A_is<A2>, B_is<B2> > lib4; 
    Library<B_is<B2>, A_is<A2> > lib5; 
} 

上述輸出:

A is A1, B is B1 
A is A2, B is B1 
A is A1, B is B2 
A is A2, B is B2 
A is A2, B is B2 
相關問題