2010-09-10 16 views
12

Ho due struct:const-ness come argomento di un template

// ----- non-const ----- 
    struct arg_adapter 
    { 
     EArgType type; // fmtA, fmtB, ... 

     union 
     { 
     TypeA * valueA; 
     TypeB * valueB; 
     // ... more types 
     } 

     arg_adapter(TypeA & value) : type(fmtA), valueA(&value) {} 
     arg_adapter(TypeB & value) : type(fmtB), valueB(&value) {} 
     // ... 
    } 

    // ----- const version ----- 
    struct const_arg_adapter 
    { 
     EArgType type; // fmtA, fmtB, ... 

     union 
     { 
     TypeA const * valueA; 
     TypeB const * valueB; 
     // ... more types 
     } 

     arg_adapter(TypeA const & value) : type(fmtA), valueA(&value) {} 
     arg_adapter(TypeB const & value) : type(fmtB), valueB(&value) {} 
     // ... 
    } 

che sono chiamati a essere usato in metodi come ad esempio:

Convert(const_arg_adapter from, arg_adapter to) 

ci sono TypeX multipla'(circa 5, può diventare più), molti dei quali primitivi. Questo per evitare di mantenere diversi prototipi.

Ora la mia domanda ;-)

C'è un modo per rendere il const-ness un parametro di modello? Il mio obiettivo è quello di mantenere una sola struct, cioè

template <Qualifier CONSTNESS> 
struct arg_adapter_t 
{ 
    ... 
    CONSTNESS TypeA * valueA; 
    ... 
} 

risposta

6

si può fare accettare un metafunction e si può applicare qualsiasi trasformazione che ti piace

template<template<typename> class F> 
struct arg_adapter 
{ 
    EArgType type; // fmtA, fmtB, ... 

    union 
    { 
     typename F<TypeA>::type * valueA; 
     typename F<TypeB>::type * valueB; 
     // ... more types 
    }; 

    arg_adapter(typename F<TypeA>::type & value) : type(fmtA), valueA(&value) {} 
    arg_adapter(typename F<TypeB>::type & value) : type(fmtB), valueB(&value) {} 
    // ... 
}; 

typename arg_adapter<boost::add_const> const_adapter; 
typename arg_adapter<boost::mpl::identity> nonconst_adapter; 

Oppure accettare uno metafunction class per ottenere una maggiore flessibilità (compresa la possibilità di rendere F gli argomenti predefiniti non noti al proprio e così via.

template<typename F> 
struct arg_adapter 
{ 
    EArgType type; // fmtA, fmtB, ... 

    union 
    { 
     typename apply<F, TypeA>::type * valueA; 
     typename apply<F, TypeB>::type * valueB; 
     // ... more types 
    }; 

    arg_adapter(typename apply<F, TypeA>::type & value) : type(fmtA), valueA(&value) {} 
    arg_adapter(typename apply<F, TypeB>::type & value) : type(fmtB), valueB(&value) {} 
    // ... 
}; 

typename arg_adapter< lambda< boost::add_const<_> >::type > const_adapter; 
typename arg_adapter< lambda< boost::mpl::identity<_> >::type > nonconst_adapter; 
+0

Forse aggiungere qualche 'typedef' eviterebbe di ripetere' typename applicare :: type' troppo :) –

+0

@Matthieu in C++ 0x, potremmo semplicemente dire "applica &'. Adoro gli alias del modello :) –

+0

così farò anch'io, così farò :) Sto aspettando con impazienza modelli di alias e template variadici, mi chiedo solo quale compilatore lo farà per primo:/ –

0

Forse non ho capito, ma perché non si può utilizzare

Convert(**const** arg_adapter from, arg_adapter to) 

Dichiarare una typedef per semplificare il lavoro

+0

"La costanza dell'oggetto puntato è una proprietà dell'argomento modello." Questo non riguarda i puntatori const qui. – Wok

+0

Ho bisogno di chiamare Converti con '(const TypeA &, TypeB &)', e arg_adapter non ha un CTor per 'const TypeA &'. – peterchen

7

Ho appena inciampato su un modo migliore utilizzando il tipo di selezione ideom hanno consegnato dalla Alexandrescu in "moderna C++ Design":

Questo è il selettore del tipo:

template<bool flag, typename T, typename U> 
struct Select { typedef T Result; } 

template<typename T, typename U> 
struct Select<false, T, U> { typedef U Result; } 

La classe sarebbe quindi assomigliare a questo:

template<bool isConst> 
struct arg_adapter 
{ 
    // define A and B as const or non-const 
    typedef typename Select<isConst, const TypeA, TypeA>::Result A; 
    typedef typename Select<isConst, const TypeB, TypeB>::Result B; 

    EArgType type; // fmtA, fmtB, ... 

    union 
    { 
    A * valueA; // this is either const TypeA* oder TypeA* depending on 
       // your choice of the isConst template parameter 
    B * valueB; 
    // ... more types 
    } 

    arg_adapter(A & value) : type(fmtA), valueA(&value) {} // same here with ref 
    arg_adapter(B & value) : type(fmtB), valueB(&value) {} 
    // ... 
} 

È possibile utilizzare typedefs per comodità:

struct nonconst_adapter : public arg_adapter<false> {}; 

struct const_adapter : public arg_adapter<true> {}; 

Questa è stata la mia risposta vecchio utilizzando semplici caratteri morfologici:

template<typename TypeTraits> 
struct arg_adapter 
{ 
    typedef typename TypeTraits::T T; 
    void bar(T a) { ... } // by value/reference 
    void bar(T* a) { ... } // by pointer 
} 

template<typename K> 
struct NonConstTraits { 
    typedef K T; 
} 

template<typename K> 
struct ConstTraits { 
    typedef const K T; 
} 

template<typename K> 
struct nonconst_adapter : public arg_adapter<NonConstTraits<K> > {}; 

template<typename K> 
struct const_adapter : public arg_adapter<ConstTraits<K> > {}; 
+0

grazie per la risposta - illustra bene che cosa fa la soluzione di Johannes con l'aumento. --- Non ho finito per usarlo, dato che la quantità di codice con tutti i typedefs era la stessa (e in caso di aggiunta, altri posti devono essere comunque aggiornati). Tuttavia, sono diventato più fiducioso di TMP. – peterchen

Problemi correlati