web-dev-qa-db-fra.com

Modèle de classe C ++ de classe de base spécifique

Disons que j'ai les cours:

class Base{};

class A: public Base{
    int i;
};

class B:public Base{
    bool b;
};

Et maintenant, je veux définir une classe basée sur des modèles:

template < typename T1, typename T2 >
class BasePair{
    T1 first;
    T2 second;
};

Mais je veux le définir de telle sorte que seuls les descendants de la classe Base puissent être utilisés comme paramètres de modèle.

Comment puis je faire ça?

47
Mat

Plus exactement:

class B {};
class D1 : public B {};
class D2 : public B {};
class U {};

template <class X, class Y> class P {
    X x;
    Y y;
public:
    P() {
        (void)static_cast<B*>((X*)0);
        (void)static_cast<B*>((Y*)0);
    }
};

int main() {
    P<D1, D2> ok;
    P<U, U> nok; //error
}
12
user213546

C++ 11 introduit <type_traits>

template <typename T1, typename T2>
class BasePair{
static_assert(std::is_base_of<Base, T1>::value, "T1 must derive from Base");
static_assert(std::is_base_of<Base, T2>::value, "T2 must derive from Base");

    T1 first;
    T2 second;
};
42
Hashbrown

C++ ne le permet pas encore directement. Vous pouvez le réaliser indirectement en utilisant un STATIC_ASSERT et vérification de type à l'intérieur de la classe:

template < typename T1, typename T2 >
class BasePair{
    BOOST_STATIC_ASSERT(boost::is_base_of<Base, T1>);
    BOOST_STATIC_ASSERT(boost::is_base_of<Base, T2>);
    T1 first;
    T2 second;
};
9
Konrad Rudolph

C'était une excellente question! En recherchant ceci via ceci lien , j'ai trouvé ce qui suit, qui n'est certes pas très différent de la solution fournie là-bas. Apprenez quelque chose tous les jours ... vérifiez!

#include <iostream>
#include <string>
#include <boost/static_assert.hpp>

using namespace std;

template<typename D, typename B>
class IsDerivedFrom
{
  class No { };
  class Yes { No no[3]; };

  static Yes Test(B*); // declared, but not defined
  static No Test(...); // declared, but not defined

public:
  enum { IsDerived = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) };
};


class Base
{
public:
    virtual ~Base() {};
};

class A : public Base
{
    int i;
};

class B : public Base
{
    bool b;
};

class C
{
    string z;
};


template <class T1, class T2>
class BasePair
{
public:
    BasePair(T1 first, T2 second)
        :m_first(first),
         m_second(second)
    {
        typedef IsDerivedFrom<T1, Base> testFirst;
        typedef IsDerivedFrom<T2, Base> testSecond;

        // Compile time check do...
        BOOST_STATIC_ASSERT(testFirst::IsDerived == true);
        BOOST_STATIC_ASSERT(testSecond::IsDerived == true);

        // For runtime check do..
        if (!testFirst::IsDerived)
            cout << "\tFirst is NOT Derived!\n";
        if (!testSecond::IsDerived)
            cout << "\tSecond is NOT derived!\n";

    }

private:
    T1 m_first;
    T2 m_second;
};


int main(int argc, char *argv[])
{
    A a;
    B b;
    C c;

    cout << "Creating GOOD pair\n";
    BasePair<A, B> good(a, b);

    cout << "Creating BAD pair\n";
    BasePair<C, B> bad(c, b);
    return 1;
}
2
RC.

Tout d'abord, corrigez la déclaration

template < class T1, class T2 >
class BasePair{
    T1 first;
    T2 second;
};

Ensuite, vous pouvez déclarer dans une classe de base une fonction privée Foo (); et dire à la classe Base d'avoir BasePair comme ami; puis dans ami constructeur, il vous suffit d'appeler cette fonction. De cette façon, vous obtiendrez une erreur de compilation lorsque quelqu'un essaie d'utiliser d'autres classes comme paramètres de modèle.

0
alemjerus
class B
{
};
class D : public B
{
};
class U
{
};

template <class X, class Y> class P
{
    X x;
    Y y;
public:
    P()
    {
        (void)static_cast<X*>((Y*)0);
    }
};
0
user213546

Dans la réponse suggérée par unknown (yahoo), il n'est pas nécessaire d'avoir réellement les variables de type X et Y comme membres. Ces lignes sont suffisantes dans le constructeur:

static_cast<B*>((X*)0);
static_cast<B*>((Y*)0);
0
mukeshkumar