2010-06-09 12 views
15

Sto provando a scrivere codice che utilizza un typedef membro di un argomento modello, ma voglio fornire un tipo predefinito se l'argomento modello non ha quel typedef. Un esempio semplificato che ho provato è questo:Specializzazione modello per utilizzare il tipo predefinito se il membro della classe typedef non esiste

struct DefaultType { DefaultType() { printf("Default "); } }; 
struct NonDefaultType { NonDefaultType() { printf("NonDefault "); } }; 

struct A {}; 
struct B { typedef NonDefaultType Type; }; 

template<typename T, typename Enable = void> struct Get_Type { 
    typedef DefaultType Type; 
}; 
template<typename T> struct Get_Type< T, typename T::Type > { 
    typedef typename T::Type Type; 
}; 

int main() 
{ 
    Get_Type<A>::Type test1; 
    Get_Type<B>::Type test2; 
} 

mi aspetto questo per stampare "Default non predefinite", ma invece esso stampa "Default Default". La mia aspettativa è che la seconda riga in main() corrisponda alla versione specializzata di Get_Type, perché B :: Type esiste. Tuttavia, questo non succede.

Qualcuno può spiegare cosa sta succedendo qui e come risolverlo o un altro modo per raggiungere lo stesso obiettivo?

Grazie.

Edit:

Georg ha dato un metodo alternativo, ma io sono ancora curioso di sapere il motivo per cui questo non funziona. Secondo la la spinta enable_if docs, un modo di specializzarsi un modello per i diversi tipi è in questo modo:

template <class T, class Enable = void> 
class A { ... }; 

template <class T> 
class A<T, typename enable_if<is_integral<T> >::type> { ... }; 

template <class T> 
class A<T, typename enable_if<is_float<T> >::type> { ... }; 

Questo funziona perché enable_if < vera> ha caratteristiche di un typedef, ma enable_if < false> non lo fa.

Non capisco come questo sia diverso dalla mia versione, dove invece di usare enable_if sto usando solo T :: Type direttamente. Se T :: Type esiste non è lo stesso di enable_if < true> :: type nell'esempio sopra e causa la specializzazione da scegliere? E se T :: Type non esiste, non sarebbe lo stesso di enable_if < false> :: type non esistente e causando la scelta della versione predefinita nell'esempio sopra?

+0

Uh .. qual è l'obiettivo? –

+0

L'obiettivo è che Get_Type :: Type sarà T :: Type se esiste, o DefaultType se non esiste. – Frank

risposta

8

Per rispondere alla tua aggiunta, il tuo argomento di specializzazione supera il membro typedef e si aspetta che produca come tipo void. Non c'è nulla di magico in questo: usa solo un argomento predefinito. Vediamo come funziona. Se si dice Get_Type<Foo>::type, il compilatore utilizza l'argomento predefinito di Enable, che è void, e il nome del tipo diventa Get_Type<Foo, void>::type. Ora il compilatore verifica se corrispondenze di specializzazione parziale.

L'elenco degli argomenti di specializzazione parziale <T, typename T::Type> viene dedotto dall'elenco degli argomenti originali <Foo, void>. Ciò dedurrà T a Foo e sostituirà successivamente lo Foo nel secondo argomento della specializzazione, ottenendo un risultato finale di <Foo, NonDefaultType> per la specializzazione parziale. Tuttavia, ciò non corrisponde affatto alla lista degli argomenti originali <Foo, void>!

è necessario un modo per ottenere il tipo di void, come nell'esempio seguente:

template<typename T> 
struct tovoid { typedef void type; }; 

template<typename T, typename Enable = void> struct Get_Type { 
    typedef DefaultType Type; 
}; 
template<typename T> 
struct Get_Type< T, typename tovoid<typename T::Type>::type > { 
    typedef typename T::Type Type; 
}; 

Ora, questo funzionerà come ci si aspetta. Utilizzando MPL, è possibile utilizzare al posto di alwaystovoid

typename apply< always<void>, typename T::type >::type 
+0

Grazie Johannes. Non mi rendevo conto che il tipo stesso doveva essere nullo per combaciare, ma questo ha perfettamente senso. – Frank

7

è possibile farlo utilizzando SFINAE:

template<class T> struct has_type { 
    template<class U> static char (&test(typename U::Type const*))[1]; 
    template<class U> static char (&test(...))[2]; 
    static const bool value = (sizeof(test<T>(0)) == 1); 
}; 

template<class T, bool has = has_type<T>::value> struct Get_Type { 
    typedef DefaultType Type; 
}; 

template<class T> struct Get_Type<T, true> { 
    typedef typename T::Type Type; 
}; 
+0

Grazie George, funziona. Ho modificato la mia domanda con un'altra domanda sul perché la mia implementazione non funziona, perché sembra lo stesso principio su cui si basa enable_if. enable_if ha un typedef, ma enable_if no. Non capisco perché non sia la stessa cosa se T :: Type esiste o meno. – Frank

+0

@ Frank: Johannes ha già risposto molto bene in un'altra risposta. –

4

Primo passo: smettere di usare "Tipo" e utilizzare lo standard MPL "tipo".


BOOST_MPL_HAS_XXX_DEF(Type) 

template < typename T > 
struct get_type { typedef typename T::Type type; }; 

template < typename T > 
struct calculate_type : boost::mpl::if_ 
< 
    has_Type<T> 
, get_type<T> 
, boost::mpl::identity<default_type> 
>::type {} 

typedef calculate_type<A>::type whatever; 

Se è stato utilizzato "tipo" invece di "Tipo" nelle METAFUNCTIONS non richiederebbe il "get_type" Fetcher per convertirlo e può solo tornare T in quel caso.

Problemi correlati