2013-02-22 30 views
5

Załóżmy, że mam następujące klasy:C++ wydedukować argumenty szablonu na podstawie innych argumentów szablonu

template <class T, class U, class V> Foo 
{ 
    ... 
}; 

Parametry szablonu mają wyraźną mapowanie, więc mogę wywnioskować inne argumenty szablonów U i V w oparciu o to, co T . Na przykład, jeśli T jest podwójne, U i V zawsze będą niektórymi klasami D1 i D2, a jeśli T jest zmiennoprzecinkowe, U i V będą zawsze innymi klasami F1 i F2.

Mając to na uwadze, czy istnieje sposób, w jaki mogę przekazać tylko jeden argument szablonu i czy kompilator dedukuje pozostałe dwa parametry?

Wiem, że prostą odpowiedzią byłoby po prostu uczynienie tych innych klas również szablonami i przekazanie im argumentu szablonu T, ale nie jestem w stanie uczynić tych klas szablonami (są one generowane automatycznie przez narzędzie).

Idealnie chciałbym móc używać typedef lub #define tak:

typedef Foo<double> Foo<double, D1, D2> 
typedef Foo<float> Foo<float, F1, F2> 

Jednak to nie skompilować. Zastanawiam się, czy istnieje sposób użycia metaprogramowania szablonów lub parametrów szablonów szablonu, aby rozwiązać ten problem, ale nie mogę zakryć głowy tymi koncepcjami i mam przeczucie, że prawdopodobnie istnieje jeszcze prostsza odpowiedź. Ktoś ma jakieś pomysły?

Odpowiedz

5

Odpowiedź udzielona przez Angew pokazuje właściwe podejście, ale nie pokazuje, w jaki sposób radzić sobie z sytuacjami, gdzie U i V nie można wywnioskować i musi być dostarczone przez klienta instancji.

Aby obsłużyć ten przypadek, można przypisać domyślne argumentów dla parametrów szablonu U i V:

struct D1 { }; struct D2 { }; 
struct F1 { }; struct F2 { }; 

// Primary template 
template<typename T> 
struct deduce_from 
{ 
}; 

// Specialization for double: U -> D1, V -> D2 
template<> 
struct deduce_from<double> 
{ 
    typedef D1 U; 
    typedef D2 V; 
}; 

// Specialization for float: U -> F1, V -> F2 
template<> 
struct deduce_from<float> 
{ 
    typedef F1 U; 
    typedef F2 V; 
}; 

// Give defaults to U and V: if deduce_from is not specialized for 
// the supplied T, and U or V are not explicitly provided, a compilation 
// error will occur 
template< 
    typename T, 
    typename U = typename deduce_from<T>::U, 
    typename V = typename deduce_from<T>::V 
    > 
struct Foo 
{ 
    typedef U typeU; 
    typedef V typeV; 
}; 

I tu jest prosty program do testowania poprawności powyższego roztworu:

#include <type_traits> 

int main() 
{ 
    static_assert(std::is_same<Foo<double>::typeU, D1>::value, "Error!"); 
    static_assert(std::is_same<Foo<double>::typeV, D2>::value, "Error!"); 
    static_assert(std::is_same<Foo<float>::typeU, F1>::value, "Error!"); 
    static_assert(std::is_same<Foo<float>::typeV, F2>::value, "Error!"); 

    // Uncommenting this will give you an ERROR! 
    // No deduced types for U and V when T is int 
    /* static_assert(
     std::is_same<Foo<int>::typeU, void>::value, "Error!" 
     ); */ 
    static_assert(
     std::is_same<Foo<int, bool, char>::typeU, bool>::value, "Error!" 
     ); // OK 
    static_assert(
     std::is_same<Foo<int, bool, char>::typeV, char>::value, "Error!" 
     ); // OK 
} 
+0

Dzięki! To działa idealnie. – thompsonja

+0

@thompsonja: Cieszę się, że pomogło :-) –

6

Można pozbyć U i V, tak:

template <typename T> 
struct Foo 
{ 
    typedef typename deduce_from<T>::U U; 
    typedef typename deduce_from<T>::V V; 
}; 

gdzie deduce_from obudowuje proces dedukcji.

+0

Klasa typu 'deduce_from' jest często nazywana" klasą cech ". –

+0

Dzięki! Twoja odpowiedź w połączeniu z Andym pomogła mi rozwiązać problem. – thompsonja

Powiązane problemy