2011-10-20 14 views
11

Attualmente sto facendo qualche metaprogrammazione del modello. Nel mio caso posso gestire qualsiasi tipo "iterabile", cioè qualsiasi tipo per cui esiste uno typedef foo const_iterator nello stesso modo. Stavo cercando di usare la nuova metaprogrammazione del modello C++ 11 per questo, tuttavia non sono riuscito a trovare un metodo per rilevare se manca un determinato tipo.rilevamento typedef in fase di compilazione (metaprogrammazione modello)

Perché ho anche bisogno di attivare/disattivare altre specializzazioni template basate su altre caratteristiche, attualmente sto usando un modello con due parametri, e il secondo viene prodotto tramite std::enable_if. Ecco quello che sto facendo attualmente:

template <typename T, typename Enable = void> 
struct Foo{}; // default case is invalid 

template <typename T> 
struct Foo< T, typename std::enable_if<std::is_fundamental<T>::value>::type>{ 
    void do_stuff(){ ... } 
}; 

template<typename T> 
struct exists{ 
    static const bool value = true; 
}; 

template<typename T> 
struct Foo<T, typename std::enable_if<exists< typename T::const_iterator >::value >::type> { 
    void do_stuff(){ ... } 
}; 

non ero in grado di fare qualcosa di simile, senza il modello exists aiutante. Per esempio semplicemente facendo

template<typename T> 
struct Foo<T, typename T::const_iterator> { 
    void do_stuff(){ ... } 
}; 

non ha funzionato, perché in quei casi in cui deve essere utilizzato questa specializzazione, il caso di default non valido un'istanza invece.

Tuttavia non sono riuscito a trovare questo exists in qualsiasi punto del nuovo standard C++ 11, che per quanto ne so sta prendendo semplicemente da boost::type_traits per questo tipo di cose. Tuttavia su homepage per boost::type_traits non viene mostrato alcun riferimento a qualcosa che potrebbe essere utilizzato al suo posto.

Manca questa funzionalità oppure ho trascurato qualche altro modo ovvio per ottenere il comportamento desiderato?

risposta

13

Se si desidera semplicemente se un determinato tipo contiene const_iterator, di seguito è una versione semplificata n del codice:

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

template<typename T, typename = void> 
struct Foo {}; 

template<typename T> 
struct Foo <T, typename void_<typename T::const_iterator>::type> { 
     void do_stuff(){ ... } 
}; 

Vedi this answer qualche spiegazione di come funziona questa tecnica.

+3

Dovresti forse pubblicare un link alle tue domande su come funziona. :) Inoltre, sembra che tu ti sia piaciuto molto, visto che lo hai suggerito più volte. – Xeo

+0

@Xeo, sì, questo è piuttosto facile e diretto. Ma non ho la tua prima parte 'Dovresti forse postare un link alle tue domande su come funziona. :) ... intendi mentre rispondi devo inserire un link alle mie domande precedenti (invece del codice stesso)? Sospetto che non sia raccomandato su SO. – iammilind

+1

Nono, volevo dire che pubblichi un link a [la tua domanda in cui hai chiesto come funziona] (http://stackoverflow.com/questions/6543652/different-template-syntax-for-finding-if-argument -is-a-class-or-not), dato che all'inizio non è così ovvio. Woops, e ho appena notato che ho scritto "domande", intendevo solo quella domanda, naturalmente. – Xeo

7

È possibile creare un tratto has_const_iterator che fornisce un valore booleano e utilizzarlo nella specializzazione.

Qualcosa di simile potrebbe farlo:

template <typename T> 
struct has_const_iterator { 
private: 
    template <typename T1> 
    static typename T1::const_iterator test(int); 
    template <typename> 
    static void test(...); 
public: 
    enum { value = !std::is_void<decltype(test<T>(0))>::value }; 
}; 

e poi si può specializzare in questo modo:

template <typename T, 
      bool IsFundamental = std::is_fundamental<T>::value, 
      bool HasConstIterator = has_const_iterator<T>::value> 
struct Foo; // default case is invalid, so no definition! 

template <typename T> 
struct Foo< T, true, false>{ 
    void do_stuff(){// bla } 
}; 

template<typename T> 
struct Foo<T, false, true> { 
    void do_stuff(){//bla} 
}; 
+1

C'è una ragione si è scelto ' test (int) 'invece di, ad esempio,' test() '? Solo assicurandomi di capire come funziona la risoluzione di sovraccarico qui. Grazie! – nknight

+2

@nknight: Motivo: rendere la chiamata a 'test' non ambigua. – erenon

4

Ecco un'altra versione di un controllo di tipo membro tratto:

template<typename T> 
struct has_const_iterator 
{ 
private: 
    typedef char      yes; 
    typedef struct { char array[2]; } no; 

    template<typename C> static yes test(typename C::const_iterator*); 
    template<typename C> static no test(...); 
public: 
    static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
}; 
1

Ci sono un paio di modi per farlo. In C++ 03, è possibile utilizzare boost e enable_if per definire il tratto (docs, source):

BOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator); 

template <typename T, typename Enable = void> 
struct Foo; 

template <typename T> 
struct Foo< T, typename boost::enable_if<boost::is_fundamental<T> >::type>{ 
    void do_stuff(){ ... } 
}; 

template<typename T> 
struct Foo<T, typename boost::enable_if<has_const_iterator<T> >::type> { 
    void do_stuff(){ ... } 
}; 

In C++ 11, si potrebbe usare Tick come questo:

TICK_TRAIT(has_const_iterator) 
{ 
    template<class T> 
    auto require(const T&) -> valid< 
     has_type<typename T::const_iterator> 
    >; 
}; 

template <typename T, typename Enable = void> 
struct Foo; 

template <typename T> 
struct Foo< T, TICK_CLASS_REQUIRES(std::is_fundamental<T>::value)>{ 
    void do_stuff(){ ... } 
}; 

template<typename T> 
struct Foo<T, TICK_CLASS_REQUIRES(has_const_iterator<T>())> { 
    void do_stuff(){ ... } 
}; 

anche con Tick è possibile migliorare ulteriormente il tratto per rilevare effettivamente che lo const_iterator è in realtà un iteratore.Quindi diciamo che definiamo una semplice is_iterator caratteristica come questa:

TICK_TRAIT(is_iterator, 
    std::is_copy_constructible<_>) 
{ 
    template<class I> 
    auto require(I&& i) -> valid< 
     decltype(*i), 
     decltype(++i) 
    >; 
}; 

Possiamo quindi definire has_const_iterator caratteristica per verificare che il tipo di const_iterator corrisponde al is_iterator tratto in questo modo:

TICK_TRAIT(has_const_iterator) 
{ 
    template<class T> 
    auto require(const T&) -> valid< 
     has_type<typename T::const_iterator, is_iterator<_>> 
    >; 
}; 
Problemi correlati