2010-12-23 7 views
17

E 'possibile in C++ verificare il tipo passato in una funzione modello? Per esempio:Passaggio passato tipo da modello

template <typename T> 
void Foo() 
{ 
    if (typeof(SomeClass) == T) 
     ...; 
    else if (typeof(SomeClass2) == T) 
     ...; 
} 

risposta

27

Sì, è ... ma probabilmente non funzionerà come ti aspetti.

template < typename T > 
void foo() 
{ 
    if (is_same<T,SomeClass>::value) ...; 
    else if (is_same<T,SomeClass2>::value) ...; 
} 

È possibile ottenere is_same da std:: o boost:: a seconda del vostro desiderio/compilatore. Il primo è solo in C++ 0x.

Il problema si presenta con ciò che è in .... Se ti aspetti di essere in grado di fare una chiamata di funzione specifica a quei tipi in foo, ti stai sbagliando tristemente. Si verificherà un errore del compilatore anche se quella sezione di codice non viene mai eseguita quando si passa a qualcosa che non obbedisce all'interfaccia prevista.

Per risolvere QUEL problema è necessario fare qualcosa di leggermente diverso. Mi consiglia di dispacciamento tag:

struct v1_tag {}; 
struct v2_tag {}; 

template < typename T > struct someclass_version_tag; 
template < > struct someclass_version_tag<SomeClass> { typedef v1_tag type; }; 
template < > struct someclass_version_tag<SomeClass2> { typedef v2_tag type; }; 

void foo(v1_tag) { ... } 
void foo(v2_tag) { ... } 
template < typename T > void foo() 
{ 
    typedef typename someclass_version_tag<T>::type tag; 
    foo(tag()); 
} 

Nota che non sarà essere affetti alcun overhead runtime-polimorfismo qui e con le ottimizzazioni attivate esso dovrebbe comportare lo stesso o addirittura più piccola dimensione del codice e la velocità (anche se si shouldn' t preoccupiamoci comunque fino a quando non avrai eseguito un profiler).

+0

È possibile restituire diversi tipi basati su version_tag? – Boying

+0

Sicuro. Devi trovare un modo per interrogare le informazioni se sei bloccato in C++ 03, o usare auto in C++ 11 e versioni successive. Finché non provi a restituire diversi tipi all'interno della stessa istanza, non avrai alcun problema. –

+0

ora con C++ 17, è possibile utilizzare le istruzioni di constexpr if per farlo banalmente – xaxxon

10

Se si vuole fare qualcosa di specifico in base al tipo, si specializzano il modello:

template <typename T> 
void Foo() { } 

template <> 
void Foo<SomeClass>() { } 

template <> 
void Foo<SomeClass2>() { } 

// etc. 

(In realtà non desidera specializzarsi la funzione di modello, anche se, questo è solo per esposizione. potrete sia vuole sovraccaricare il modello, se è possibile, o delegare ad un modello di classe specializzato. per ulteriori informazioni su perché e come evitare specializzata modelli di funzione, leggere di Why Not Specialize Function Templates? Herb Sutter)

2

Sì. Dovrai usare type traits. Per esempio:

#include <boost/type_traits/is_same.hpp> 

template <typename T> 
void Foo() 
{ 
    if ((boost::is_same<T, SomeClass>::value)) 
     ...; 
    else if ((boost::is_same<T, SomeClass2>::value)) 
     ...; 
} 

seconda di cosa si sta cercando di ottenere, utilizzando template specialization potrebbe essere molto migliore scelta.

Inoltre, è possibile utilizzare enable_if/disable_if per abilitare/disabilitare determinate funzioni/metodi. La combinazione di questo con i tratti del tipo consentirà, ad esempio, di utilizzare una funzione per un insieme di tipi e un'altra funzione per un altro insieme di tipi.

+0

Il tuo codice non verrà compilato. O hai bisogno di istanziare l'istanza is_same come variabile, o devi accedere alla sua definizione interna di valore. –

+0

@Noah: Giusto. Questo è stato un suggerimento veloce e sporco :-) L'ho modificato. –

+0

È possibile farlo per un insieme di valori? Esegui qualcos'altro per ogni valore enum? – paulm

2

No, ma è possibile utilizzare la specializzazione parziale:

template<typename T> 
struct Bar { static void foo(); }; 
template<typename T> 
template<> inline void Bar<T>::foo() { 
//generic 
} 
template<> inline void Bar<int>::foo() { 
//stuff for int 
} 
template<> inline void Bar<QString>::foo() { 
//QString 
} 

Modifica Sì con tratti di tipo, tuttavia non è realmente necessario. Modifica 2 tipo_traits esempio.

#include <type_traits> 
template<typename T> void foo() { 
    using std::is_same; 
    if<is_same<T, T2>::value || is_same<T, T1>::value) { 
     /* stuff */ 
    } 
} 
+0

Cosa succede se voglio fare qualcosa come 'if (is_same || is_same )'? –

+0

Quindi è necessario utilizzare i tratti di carattere '#include e std :: is_same :: value' se il compilatore supporta C++ 0x (VS2010/qualsiasi recente gcc) o semplicemente utilizzare boost per la compatibilità con il compilatore precedente. – OneOfOne

+3

Questa non è una specializzazione parziale, è una specializzazione completa. C'è una differenza piuttosto significativa. Probabilmente il più significativo è che sarebbe impossibile eseguire una specializzazione parziale in quel modo, poiché non è consentito con funzioni di alcun tipo. –

Problemi correlati