2010-09-10 4 views
12

J'ai deux struct:const-ness comme argument de modèle

// ----- 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) {} 
     // ... 
    } 

Ils sont censés être utilisés dans des méthodes telles que:

Convert(const_arg_adapter from, arg_adapter to) 

Il y a plusieurs TYPEx »(environ 5, peut devenir plus), la plupart d'entre eux primitifs. Ceci pour éviter de conserver différents prototypes.

Maintenant, ma question ;-)

Y at-il un moyen de faire la const-ness un paramètre de modèle? Mon objectif est de maintenir un seul struct, à savoir

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

Répondre

6

vous pouvez le faire accepter un metafunction et vous pouvez appliquer une transformation que vous aimez

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; 

Ou accepter un metafunction class pour obtenir plus de flexibilité (y compris la possibilité de faire F avoir des arguments par défaut inconnus à votre arg_adapter et autres.

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

Peut-être qu'ajouter quelque 'typedef' éviterait de répéter le' typename 'appliquer trop :: type' :) –

+0

@Matthieu en C++ 0x, on pourrait juste dire' appliquer & '. Je vais adorer les alias de template :) –

+0

et moi aussi :) Je suis impatient d'avoir des alias de templates et des templates variés, je me demande juste quel compilateur va le faire en premier:/ –

0

Peut-être que je ne comprenais pas, mais pourquoi utiliser ne peut pas vous

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

Déclarer un typedef pour simplifier le travail

+0

"Le constness de l'objet pointé est une propriété de l'argument de modèle." Ce n'est pas sur les pointeurs const ici. – Wok

+0

Je dois appeler Convert avec '(const TypeA &, TypeB &)', et arg_adapter n'a pas de CTor pour 'const TypeA &'. – peterchen

7

Je viens trébuché au sujet une meilleure façon d'utiliser le type de sélection ideom presentend par Alexandrescu dans "Modern C++ Design":

Ceci est le sélecteur de type:

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; } 

Votre classe serait alors ressembler à ceci:

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) {} 
    // ... 
} 

Vous pouvez utiliser typedefs pour plus de commodité:

struct nonconst_adapter : public arg_adapter<false> {}; 

struct const_adapter : public arg_adapter<true> {}; 

Ce fut ma vieille réponse en utilisant des traits de type simple:

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

merci pour la réponse - ça illustre bien ce que fait la solution de Johannes en utilisant boost. --- Je n'ai pas fini par l'utiliser, car la quantité de code avec toutes les typedefs était la même (et dans le cas d'un ajout, d'autres endroits doivent être mis à jour quand même). Pourtant, je suis devenu plus confiant de TMP. – peterchen

Questions connexes