2017-06-10 4 views
1

Ainsi est ici le problème simplifié, supposons que nous avons deux typesComment avoir différentes la mise en œuvre d'une fonction de classe modèle basé sur le type d'un membre spécifique du type paramétrées

struct Shape2D 
{ 
    Vec2 Position; 
}; 

struct Shape3D 
{ 
    Vec3 Position; 
}; 

Je veux créer un modèle test de classe

template<class T> 
class Test 
{ 
    public: 
     int method1() 
     { 
      //return 1 if member T::Position is Vec2, 
      //return 2 if member T::Position is Vec3 
     } 
} 

tels que le code suivant fonctionne

Test<A> ta; 
Test<B> tb; 
assert(ta.method1() == 1); 
assert(tb.method1() == 2); 

le vrai Le contexte de ce problème concerne un moteur OpenGL. Je veux pouvoir utiliser la même classe de sérialisation pour les données de sommets 2D et 3D sans avoir à écrire une version 3D et 2D.

+0

Le problème lié au polymorphisme plutôt que des modèles. Ne sera-t-il pas possible de dériver shape3d et shape2d à partir de formes? –

+0

Tout dépend de ce que vous souhaitez réaliser ici. Voulez-vous juste tester si vous avez créé la bonne classe? –

+0

Hum, bien. J'ai tellement essayé de rendre le problème aussi simple que possible qui a fini par être très différent de celui que j'ai vraiment. Pour le problème en main, la surcharge est la meilleure solution. – alcoforado

Répondre

0

Vous pouvez avoir une fonction oveload pour exécuter du code différent, que vous avez int func (shape 2D x) et int func (shape3D x), donc int votre method1 vous appelez simplement func (T.position) et compilateur vous aider à résoudre l'appel.

1

Il y a plusieurs façons d'aborder cela.

Le plus simple serait d'utiliser simplement la résolution de surcharge normale:

template<class T> 
class Test 
{ 
private: 
    T myT; 

    int internal(Vec2) 
    { 
     return 1; 
    } 

    int internal(Vec3) 
    { 
     return 2; 
    } 
public: 
    Test() : myT{} {} 

    int method1() 
    { 
     return internal(myT.Position); 
    } 
}; 

Cela exige que vous avez réellement une instance de T. Si vous ne le faites pas, vous devrez utiliser une approche basée sur un modèle. C'est un sujet assez profond, mais une approche qui fait ce que vous voulez dans votre exemple est la suivante:

template <typename T> 
int internal(); 

template <> 
int internal<Vec2>() 
{ 
    return 1; 
} 

template <> 
int internal<Vec3>() 
{ 
    return 2; 
} 

template<class T> 
class Test 
{ 
public: 
    int method1() 
    { 
     return internal<decltype(T::Position)>(); 
    } 
}; 
1

Vous pouvez utiliser deux déclarations de fonction (définitions ne sont pas nécessaires dans ce cas), std::declval et std::integral_constant résoudre au moment de la compilation.
Il suit un minimum, par exemple de travail:

#include<type_traits> 
#include<utility> 

struct Vec2 {}; 
struct Vec3{}; 

struct Shape2D { Vec2 Position; }; 
struct Shape3D { Vec3 Position; }; 

template<class T> 
class Test { 
    static constexpr std::integral_constant<int, 1> method1(Vec2); 
    static constexpr std::integral_constant<int, 2> method1(Vec3); 

public: 
    constexpr int method1() { 
     return decltype(method1(std::declval<T>().Position))::value; 
    } 
}; 

int main() { 
    Test<Shape2D> ta; 
    Test<Shape3D> tb; 
    static_assert(ta.method1() == 1, "!"); 
    static_assert(tb.method1() == 2, "!"); 
} 

La solution ci-dessus échoue à compiler si T n'a pas un membre de données nommé Position dont le type est soit Vec2 ou Vec3.


Une autre approche possible qui nécessite une valeur par défaut à la place pourrait être ceci:

constexpr int method1() { 
    return 
     (std::is_same<decltype(std::declval<T>().Position), Vec2>::value 
     ? 1 : (std::is_same<decltype(std::declval<T>().Position), Vec3>::value 
     ? 2 : 0)); 
} 

C'est l'opérateur ternaire utilisé conjointement avec std::is_same, rien de plus.


Si vous pouvez utiliser 17 C++, vous pouvez également baser votre solution sur if/else constexpr:

constexpr int method1() { 
    if constexpr(std::is_same_v<decltype(std::declval<T>().Position), Vec2>) { 
     return 1; 
    } else if constexpr(std::is_same_v<decltype(std::declval<T>().Position), Vec3>) { 
     return 2; 
    } 
}