Je suis un peu novice en C ++, en particulier en ce qui concerne les modèles. J'ai une classe de modèle "Foo" qui est destinée à prendre différentes structures comme paramètres de modèle. J'ai également besoin d'une fonction membre de la classe qui fonctionne différemment en fonction du paramètre de modèle de type, donc je spécialise une telle fonction. L'image générale serait la suivante

struct A
{

    float paramOfA;

};

struct B
{

    float paramOfB; 

};


template <typename T> 
class Foo
{
    public:
        void doSomethingOnType(T& arg);

    //...more functions and stuff...

};

// function specialisation for A's
template<> void Foo<A>::doSomethingOnType(A& a){

    //do something on member of A
    a.paramOfA = ...;

    std::cout<< "I've done something on a's member..."<<std::endl;


}

// function specialisation for B's
template<> void Foo<B>::doSomethingOnType(B& b){
    //do something on member of B
    b.paramOfB = ...;

    std::cout<< "I've done something on b's member..."<<std::endl;

}

Jusqu'ici tout va bien, non? Imaginez que maintenant j'ai une structure C qui dérive de B:

struct C:B
{

    float paramOfC;

};

Maintenant, lorsque j'instancie un objet Foo qui prend le type de modèle de structure C, je voudrais que la fonction "doSomethingOnType" conserve le même comportement de la fonction pour les types B sur le membre de C qui dérive de B (paramOfB), même si je ne l'ai pas fait spécialisé une telle fonction pour les types de structure C. Par exemple

Foo<C> o;
C oneC;

o.doSomethingOnType(oneC);

Je suis sûr que lors de l'exécution du morceau de code ci-dessus, la fonction prendra toute implémentation donnée dans la classe basée sur un modèle, pas dans la version spécialisée pour B.Mais je veux vraiment conserver cette dernière implémentation de la fonction lors de l'utilisation de types C puisque, étant C dérivé de B, cela aurait beaucoup de sens pour moi et cela me ferait gagner du temps d'avoir à écrire plus de lignes de code pour une spécialisation de fonction pour C qui a le même comportement que pour B (imaginez que B a 50 membres au lieu d'un seul). Est-il possible de le faire sans, comme je l'ai dit, spécialiser la fonction pour les types de structure C?

Merci d'avance pour toute aide!

Vraiment excité de me poser la première question dans stackoverflow :-)

1
Javi RD 26 déc. 2015 à 14:37

2 réponses

Meilleure réponse

Vous pouvez faire quelque chose de crochet dans la fonction de modèle générale, bien que je déteste ça .... ces codes peuvent comme suit:

template <typename T>
class Foo {
public:
  void doSomethingOnType(T& arg)
  {
    if (dynamic_cast<B*>(&arg) != NULL) {
      Foo<B> fb;
      fb.doSomethingOnType(arg);
    } else {
      std::cout << "I've done something on Foo's member..." << std::endl;
    }
  }

};

0
Roland Yim 26 déc. 2015 à 17:04

Bien que Comment forcer une modélisation à correspondre à classe de base? est une solution élégante si votre fonction prend une instance de T comme paramètre, je voudrais vous présenter plusieurs façons si ce n'est pas le cas:

Cas de test

class A1 {};
class A2:public A1 {};

class B{};
class C{};

Fonction enveloppée

Cela pourrait être la solution la plus simple:

template<typename T>
class Bar
{
        public:
        void fun()
        {
                fun_impl((T*)(0));
        }
        void fun_impl( A1* const)
        {
                cout << "A1" << endl;
        }
        void fun_impl(B* const)
        {
                cout << "B" << endl;
        }
        void fun_impl(void*)
        {
                cout << "Neither A nor B" << endl;
        }
};

Bar<A2>().fun(); // A1
Bar<B>().fun();  // B
Bar<C>().fun();  // Neither A nor B

Parce que la priorité de fun_impl qui correspond exactement (ou en tant que classe de base accessible de) le type & gt; ceux qui nécessitent une conversion void*, la version correcte sera activée. (REMARQUE: Ceci est vrai sur clang3.7 et gcc5.3, mais je ne fais pas référence à la norme)

Cependant, si nous avons class A3: private A1, une erreur sera générée lors de la compilation de Bar<A3>().fun().

Les deux solutions suivantes nécessitent C ++ 11:

Spécialisation partielle du modèle de classe

template<typename T, bool  = std::is_base_of<A1, T>::value,

Bool = std :: is_base_of :: value> struct Foo {void fun (); }; // Version principale

template<typename T>
struct Foo<T,false,false>
{
        void fun();
};  //Specialization of Neither A nor B

template<typename T>
void Foo<T,false,false>::fun()
{
        cout << "neither A nor B" << endl;
}

template<typename T>
struct Foo<T,true,false>
{
        void fun();
};  //Specialization of A

template<typename T>
void Foo<T,true,false>::fun()
{
        cout << "A" << endl;
}

template<typename T>
struct Foo<T, false, true>
{
        void fun();
};  //Specialization of B

template<typename T>
void Foo<T,false,true>::fun()
{
        cout << "B" << endl;
}

    Foo<A2>().fun();  //A
    Foo<B>().fun();   //B
    Foo<C>().fun();   //Neither A nor B
    Foo<APrivate>().fun();    //A

SFINAE

Si vous ne voulez pas spécialiser toute la classe, SFINAE dans une classe pourrait être le meilleur choix:

namespace
{
        class Helper1 {};
        class Helper2 {};
} // Helper classes to avoid ambiguity

template<typename T>
class Foo
{
        public:

        template<typename TIn= T, typename U= typename std::enable_if<std::is_base_of<A1, TIn>::value>::type >
                void fun(Helper1 = Helper1())
                {
                        cout << "A" << endl;
                }
        template<typename TIn=T ,typename U = typename std::enable_if<std::is_base_of<B, TIn>::value>::type >
                void fun(Helper2 = Helper2())
                {
                        cout << "B" << endl;
                }
        template<typename TIn = T, typename = typename std::enable_if<!std::is_base_of<A1,TIn>::value>::type ,
                typename = typename std::enable_if<!std::is_base_of<B,TIn>::value>::type >
                void fun()
                {
                        cout << "Neither A nor B" << endl;
                }
};

Dans le cas ci-dessus, une fonction ne sera instanciée que si elle remplit certaines conditions. Puisque trois void fun() ne sont pas autorisés dans une classe, des classes d'assistance sont requises.

0
Community 23 mai 2017 à 10:28