2010-09-14 5 views
3

Supposons que vous ayez une classe de vecteur qui a une longueur et un type de modèle, c'est-à-dire vec<2,float>. Ceux-ci peuvent également être imbriqués - vec<2,vec<2,vec<2,float> > > ou vec<2,vec<2,float> >. Vous pouvez calculer à quel point emboîtés l'un de ces vecteurs est comme ceci:Modèles C++: calculez des valeurs et prenez des décisions au moment de la compilation

template<typename T> 
inline int depth(const T& t) { return 0; } 
template<int N, typename T> 
inline int depth(const vec<N,T>& v) { return 1+depth(v[0]); } 

Le problème est que vous ne saurez pas si elle est profonde jusqu'à l'exécution, mais vous pouvez avoir besoin de connaître la profondeur à comile temps afin de faire quelque chose comme ceci:

// Do this one when depth(v1) > depth(v2) 
template<int N, typename T, int M, typename U> 
inline vec<N,T> operator +(const vec<N,T>& v1, const vec<M,U>& v2) { 
    return v1 + coerce(v2,v1); 
} 
// Do this one when depth(v1) < depth(v2) 
template<int N, typename T, int M, typename U> 
inline vec<M,U> operator +(const vec<N,T>& v1, const vec<M,U>& v2) { 
    return coerce(v1,v2) + v2; 
} 

Vous ne pouvez pas simplement jeter dans un « si » parce que (a) qui est plus profonde affecte le type de retour et (b) coerce() génère une erreur de construction si vous essayez de contraindre un vecteur imbriqué à un vecteur imbriqué.

Est-il possible de faire quelque chose comme ça ou suis-je poussé dans les limites des templates C++?

+0

Si 'coerce' est capable de vérifier au moment de la compilation, vous y trouverez probablement votre réponse! –

+2

Limites des modèles C++. Ha. – GManNickG

+2

Il n'y a aucune limite aux modèles C++. –

Répondre

5

C'est tout à fait possible. Essayez par exemple

template<int N, typename T, int M, typename U> 
inline typename enable_if<is_deeper<T, U>::value, vec<N,T> >::type 
operator +(const vec<N,T>& v1, const vec<M,U>& v2) { 
    return v1 + coerce(v2,v1); 
} 

template<int N, typename T, int M, typename U> 
inline typename enable_if<is_deeper<U, T>::value, vec<M,U> >::type 
operator +(const vec<N,T>& v1, const vec<M,U>& v2) { 
    return coerce(v1,v2) + v2; 
} 

is_deeper est quelque chose comme

/* BTW what do you want to do if none is deeper? */ 
template<typename T, typename U> 
struct is_deeper { static bool const value = false; }; 

template<typename T, int N, typename U> 
struct is_deeper<vec<N, U>, T> { 
    static bool const value = true; 
}; 

template<typename T, int N, typename U> 
struct is_deeper<T, vec<N, U> > { 
    static bool const value = false; 
}; 

template<typename T, int N, int M, typename U> 
struct is_deeper<vec<M, T>, vec<N, U> > : is_deeper<T, U> 
{ }; 
+2

parfois j'utilise 'mpl :: true_' comme' struct is_deeper , T>: mpl :: true_ {}; 'et faux également – Anycorn

+0

Absolument génial! Je vous remercie! (BTW, si aucun n'est plus profond - ils sont la même profondeur - il sera résolu à un opérateur + plus spécialisé qui ajoute chaque composant) – Chris

+1

Les caractères de type binaire doivent être évités car ils ne jouent pas bien avec la mémoisation. Cela entraînera la génération de nouveaux modèles O (Depth) pour chaque paire de types de tableaux comparés, qui peuvent aller jusqu'à O (Depth^3). Mieux vaut définir un seul trait de «profondeur» et comparer le résultat. – Potatoswatter

3

Modèle metaprogramming vous libérera. Je fais cela au moment de l'exécution, mais il est évalué à la compilation:

#include <iostream> 
#include <boost\static_assert.hpp> 
using namespace std; 

template<size_t Depth> class Vec 
{ 
public: 
enum {MyDepth = Vec<Depth-1>::MyDepth + 1}; 
}; 

template<> class Vec<1> 
{ 
public: 
enum {MyDepth = 1}; 
}; 

    BOOST_STATIC_ASSERT(Vec<12>::MyDepth == 12); 
// Un-commenting the following line will generate a compile-time error 
// BOOST_STATIC_ASSERT(Vec<48>::MyDepth == 12); 

int main() 
{ 
cout << "v12 depth = " << Vec<12>::MyDepth; 
} 

EDIT: Inclus un coup de pouce assert statique pour montrer comment cela est évalué à la compilation.

2

La spécialisation partielle est très utile pour l'introspection. Habituellement, il vaut mieux éviter les fonctions inline avec des résultats constants à la compilation. (C++ 0x pourrait atténuer ce un peu, mais je ne sais pas à quel point.)

Tout d'abord, votre modèle vec ressemble beaucoup boost::array/std::tr1::array/std::array, donc je vais juste appeler array.

template< class ArrT > 
struct array_depth; // in the general case, array depth is undefined 

template< class ElemT, size_t N > // partial specialization 
struct array_depth< array< ElemT, N > > { // arrays do have depth 
    enum { value = 0 }; // in the general case, it is zero 
}; 

template< class ElemT, size_t N1, size_t N2 > // more specialized than previous 
struct array_depth< array< array< ElemT, N1 >, N2 > { 
    enum { value = 1 + array_depth< array< ElemT, N1 > >::value }; // recurse 
}; 

// define specializations for other nested datatypes, C-style arrays, etc. 
// C++0x std::rank<> already defines this for C-style arrays 
+0

C'est une bonne solution. Y a-t-il une différence entre l'utilisation d'un membre/union de classe 'static' par rapport à C++ 03 ou C++ 0x? – Chubsdad

+0

@ Chubsdad Je ne pense pas que C++ 0x ait changé la sémantique 'static'. Je préfère 'enum' à' static const int' car il n'est pas nécessaire de le définir en dehors de la portée de la classe.Hmm, en fait oui, C++ 0x étend l'initialisation en classe à d'autres types ... mais c'est encore un peu inutile puisque la définition de l'espace de noms est toujours requise. – Potatoswatter

+0

@Chubsdad Si vous lisez uniquement les valeurs du membre statique et ne comptez jamais sur son adresse (en obtenant une valeur lvalue et en la stockant explicitement), vous n'avez pas besoin de définir un membre de données statiques. Un entier const initialisé en classe suffit alors (un tel membre de données statiques n'est pas considéré comme "utilisé" par l'ODR). Il y a des cas de bords qui le rendent "utilisé" qui peut être surprenant - par exemple dans 'a? lval1: lval2' pour les deux lvalues ​​ayant le même type "utilise" le référent de lval. Mais de tels cas sont rares. –

Questions connexes