2011-06-23 9 views
9

Nie jestem biegły w szablonach. Jak napisać funkcję szablonu o nazwie get, która wybiera tablicę, z której korzysta na podstawie typu szablonu? Patrz poniższy przykład:C++: Jak używać typu w funkcji szablonu do rozgałęzienia?

struct Foo 
{ 
    int iArr[10]; 
    char cArr[10]; 

    // How to pick array here based on template type? 
    template < typename T > 
    T get(int idx) 
    { 
     // This does NOT work! 
     switch (T) 
     { 
     case int: 
      return iArr[ idx ]; 
     case char: 
      return cArr[ idx ]; 
     } 
    } 
}; 

// Expected behaviour of get() 
Foo foo; 
int i = foo.get<int>(2); 
char c = foo.get<char>(4); 
+0

sposób, w jaki napisałeś, to nie jest możliwe, ponieważ nie ma nic "generycznego" na temat twojego kodu. Po prostu potrzebujesz selektora dla dwóch członków. Możesz po prostu użyć do tego zwykłego projektu klasy. Aby uzyskać coś naprawdę uniwersalnego, możesz spróbować boost.variant. –

+0

Kerrek: To tylko prosty przykład.W moim rzeczywistym programie mam wiele więcej tablic specyficznych dla danego typu. Nie chcę pisać osobnych funkcji dla nich wszystkich. –

+1

Czy wszystkie te tablice muszą być członkami twojej klasy? Byłoby to trudne, ponieważ klasy muszą być określone, ale jeśli masz tylko ograniczoną liczbę kontenerów, mam wrażenie, że szablony nie są tak naprawdę właściwą domeną problemu. –

Odpowiedz

8

trzeba by dodać strukturę wartości jakiegoś typu można wykorzystać w celu uzyskania wartości dla przełącznika-oświadczenie. Na przykład:

template<typename T> 
struct switch_value {}; 

template<> 
struct switch_value<int> 
{ 
    enum { value = 1 }; 
}; 

template<> 
struct switch_value<char> 
{ 
    enum { value = 2 }; 
}; 

//then inside you structure Foo 
template <typename T> 
T get(int idx) 
{ 
    switch (switch_value<T>::value) 
    { 
    case 1: 
     return iArr[ idx ]; 
    case 2: 
     return cArr[ idx ]; 
    } 
} 

Dobrą rzeczą jest to, co powinno rzucać błąd kompilatora, jeśli używasz typ, który nie ma prawidłową wartość, ponieważ domyślna wersja switch_value<T> nie definiuje element value, więc jeśli Haven 'wyspecjalizował strukturę dla określonego typu, wtedy taka instancja szablonu zakończy się niepowodzeniem.

+0

Jason: Wielkie dzięki! Działa to idealnie na mój problem. O ile ktoś nie oferuje bardziej eleganckiego rozwiązania, twoje rozwiązanie :-) –

0

Można specjalizować funkcji użytkownika:

struct foo 
{ 
    int iArr[10]; 
    char cArr[10]; 

    template<typename T> 
    T &get(int ipos) { assert(false && "Foo.get: Invalid type!"); return T(); } 

    template<> 
    int &get<int>(int ipos) { return iArr[ipos]; } 

    template<> 
    char &get<char>(int ipos) {return cArr[ipos]; } 

    // add more specialisations for any other types... 
}; 

Works with msvC++ 2010. nadzieję, że to pomaga.

Specjalizacja przełącznika zasugerowana przez Jasona powinna również działać poprawnie.

+1

To nie jest legalne C++; MSVC zezwala na to jako ciche rozszerzenie, ale każdy inny kompilator będzie się dławił. – ildjarn

9

Choć rozwiązanie proponowane przez Jason działa, to daleko od idiomatyczne i jest trudniejsze do utrzymania, ponieważ wartości case w zestawieniu 1 switch) nie mają pozorny sens („Magic Numbers”) i 2) można łatwo wydostać się z zsynchronizować z wartościami w specjalizacjach switch_value<>. Chciałbym zaproponować to zamiast:

struct Foo { 
    Foo() : iArr(), cArr() { } 

    template<typename T> 
    T get(std::size_t const idx) const  { 
     return Foo::get_dispatcher<T>::impl(*this, idx); 
    } 

private: 
    int iArr[10]; 
    char cArr[10]; 

    template<typename T> 
    struct get_dispatcher; 
}; 

template<> 
struct Foo::get_dispatcher<int> { 
    static int impl(Foo const& foo, std::size_t const idx) { 
     return foo.iArr[idx]; 
    } 
}; 

template<> 
struct Foo::get_dispatcher<char> { 
    static char impl(Foo const& foo, std::size_t const idx) { 
     return foo.cArr[idx]; 
    } 
}; 

Wywoływanie Foo::get<> z innego typu niż int lub char przyniesie błąd kompilatora.

+0

+1 Zgadzam się, fajne rozwiązanie :-) – Jason

1

To chyba przesada za przykład, ale jeśli naprawdę tylko trzeba przechowywać jeden układ na raz, a następnie można użyć klasy boost :: wariantowej i odwiedzającego np

#include <boost/variant.hpp> 
#include <iostream> 

template< typename T > 
class GetVisitor : public boost::static_visitor<T> 
{ 
    public: 
    GetVisitor(int index) : index_(index) {}; 

    template <typename U > 
    T operator() (U const& vOperand) const 
    { 
     return vOperand[index_]; 
    } 

    private: 
    int index_; 
}; 


int main() 
{ 
    int iArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 
    char cArr[10] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' }; 

    boost::variant<int*, char*> intVariant(iArr); //- assign integer array to variant 
    boost::variant<int*, char*> charVariant(cArr); //- assign character array to another variant 

    int testInt = boost::apply_visitor(GetVisitor<int>(2), intVariant); 
    char testChar = boost::apply_visitor(GetVisitor<char>(9), charVariant); 

    std::cout << "returned integer is " << testInt << std::endl; 
    std::cout << "returned character is " << testChar << std::endl; 

    return 0; 
} 

output is: 
returned integer is 3 
returned character is j 

ograniczenie wariantu domniemanych przez klasę GetVisitor jest to, że wszyscy członkowie wariantu musi implementować:

T operator[](int) 

Więc można też dodać np std :: vector i std :: deque jako potencjalnych członków wariantu.

0

Wszystkie te odpowiedzi są przesadą.

template <typename T> 
T get(int idx) 
{ 
    if (boost::is_same<T, int>::value) 
     return *(T*)&iArr[ idx ]; 
    else 
     return *(T*)&cArr[ idx ]; 
} 
+0

To nie zadziała, jeśli 'T' może być typem klasy, np. 'std :: list <>'. – ildjarn

+0

@ildjarn edytowane (nice name btw) –

0

zakładam to, co chcesz po prostu koncentrując się obok funkcji szablonu:

w pliku .h

template < typename T > 
struct Foo 
{ 
    T arr[10]; 

    T get(int idx) 
    { 
     return arr[ idx ]; 
    } 
}; 

gdzieś go używać jak:

Foo<int> foo1; 
Foo<char> foo2; 
int i = foo1.get(2); 
char c = foo2.get(4); 
Powiązane problemy