2012-12-09 3 views
8

J'écris une classe de vecteurs simple et j'aimerais avoir des fonctions membres qui ne sont disponibles que dans des vecteurs de certaines longueurs (produit croisé pour un vecteur à 3 éléments par exemple). J'ai trébuché sur std :: enable_if et il semble qu'il puisse faire ce que je veux, mais je ne semble pas pouvoir le faire fonctionner correctement.Utilisation de C++ 11 std :: enable_if pour activer la fonction de membre si le vecteur est de longueur spécifique

#include <iostream> 
#include <type_traits> 

template<typename T, unsigned int L> 
class Vector 
{ 
    private: 
     T data[L]; 

    public: 
     Vector<T,L>(void) 
     { 
      for(unsigned int i = 0; i < L; i++) 
      { 
       data[i] = 0; 
      } 
     } 

     T operator()(const unsigned int i) const 
     { 
      return data[i]; 
     } 

     T& operator()(const unsigned int i) 
     { 
      return data[i]; 
     } 

     Vector<typename std::enable_if<L==3, T>::type, L> cross(const Vector<T,L>& vec2) const 
     { 
      Vector<T,L> result; 

      result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1); 
      result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2); 
      result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0); 

      return result; 
     } 
}; 

int main(void) 
{ 
    Vector<double,3> v1; 
    Vector<double,3> v2; 
    Vector<double,3> v3; 
    //Vector<double,4> v4; 

    v1(0) = 1; 
    v1(1) = 2; 
    v1(2) = 3; 

    v2(0) = 4; 
    v2(1) = 5; 
    v2(2) = 6; 

    v3 = v1.cross(v2); 

    std::cout << v3(0) << std::endl; 
    std::cout << v3(1) << std::endl; 
    std::cout << v3(2) << std::endl; 

    return 0; 
} 

Le code ci-dessus compile et fonctionne correctement, si je décommenter la déclaration de Vector<double,4> v4 je reçois l'erreur suivante à la compilation:

vec.cpp: In instantiation of ‘class Vector<double, 4u>’: 
vec.cpp:46:22: required from here 
vec.cpp:29:59: error: no type named ‘type’ in ‘struct std::enable_if<false, double>’ 

est quelqu'un capable de montrer où je me trompe?

+0

double possible de [Puis-je utiliser boost :: activer \ _Si une fonction de membre?] (Http: // stackoverflow .com/questions/4880922/can-i-use-boostenable-if-on-a-member-function) –

+1

Vous voulez probablement 'typename std :: enable_if > :: type' plutôt que 'Vector :: type, L>'. –

+1

(Vous voulez probablement que votre opérateur const() 'renvoie T const &' Le code actuel permet aux clients de faire quelque chose comme 'Vector v; Vector const & const_v = v; const_v (2) = 42.0; // Notez que l'écriture de 42 arrive à un temporaire Pas ce que l'utilisateur attendait) –

Répondre

8
template<unsigned LL = L> 
    Vector<typename std::enable_if<LL==3 && L == 3, T>::type, LL> 
    cross(const Vector<T,LL>& vec2) const 
    { 
    Vector<T,L> result; 

    result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1); 
    result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2); 
    result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0); 

    return result; 
    } 

PS. Pourquoi cela fonctionne de cette façon?

La définition de la variable v4 provoque une instanciation implicite du modèle de classe Vector, ce qui provoque, à son tour, instanciation implicite des déclarations de fonctions membres de la classe, entre autres (14.7.1 Implicite instanciation [temp.inst] #1). Cette dernière instanciation entraîne bien sûr une erreur.

Si nous changeons plutôt la fonction de membre d'être un modèle de membre, selon la même clause, à ce point le modèle de membre lui-même est instancié et ce instanciation regarde, plus ou moins, comme:

template<unsigned LL = 3> 
Vector<typename std::enable_if<LL==3 && 3 == 3, double>::type, LL> 
cross(const Vector<double,LL>& vec2) const; 

qui est une déclaration de modèle entièrement valide. Nous ne faisons pas (et nous ne pouvons pas) effectuer d'autres instanciations à ce stade. Cependant, lorsque nous essayons d'appeler cross, il s'agit sans aucun doute d'un "contexte qui nécessite l'existence de la définition membre/fonction", donc, selon (14.7.1 Instantiation implicite [temp.inst] # 2 , # 3), le modèle cross (le second modèle cross, celui qui résulte de l'instanciation du modèle de classe externe) est implicitement instancié et le std::enable_if a l'opportunité de faire son travail. En remarque, c'est la situation où le principe SFINAE est applicable.

PPS. Pour développer un peu plus loin, bien que n'étant pas directement lié à la question OP, mais toujours utile de mentionner qu'il n'est pas toujours nécessaire de déclarer les membres en tant que modèles afin de gérer des situations similaires.

Il y a des situations où un membre d'un modèle de classe ne sont pas « valide » pour une instanciation donnée, mais le modèle de classe peut être instanciée, par exemple:

#include <type_traits> 

template<typename T> 
struct S 
{ 
    T x; 

    T foo() const { return x; } 

    typename std::remove_pointer<T>::type bar() const { return *x; } 
}; 

S<int> x; 
S<int *> y; 

Apparemment, dans l'instanciation S<int> , l'expression *x n'est pas valide, car le type x est int. Ce programme est correct, cependant. Le point important est que lors de l'instanciation implicite, seules les déclarations des membres sont instanciées. Dans le cas ci-dessus, l'instanciation S<int> provoque l'instanciation de la déclaration int bar() const;, qui est une déclaration entièrement correcte.

Bien sûr, si nous essayons de suite instancier la définition de S<int>::bar, comme dans:

void f() 
{ 
    x.foo(); 
    // x.bar(); // error 
    y.foo(); 
    y.bar(); 
} 

nous aurons une erreur.

(Ceci suit encore des mentionnées ci-dessus deux paragraphes de la norme C++)

+0

Merci, même si je ne comprends pas très bien pourquoi c'est différent de ce que j'avais. Est-ce simplement parce que la fonction individuelle des membres doit également être modélisée? – rozzy

+0

@ aero117, aucun code n'est généré pour une fonction modèle jusqu'à ce qu'elle soit instanciée. Dans votre code, le compilateur va générer du code pour toute la classe (quand le template est instancié), y compris la fonction. Mais lorsque vous créez une fonction en tant que modèle, le code de cette fonction est généré uniquement lorsque ce modèle est instancié, par exemple en appelant la fonction. – soon

+0

Bravo pour une explication complète! –

Questions connexes