2010-01-06 8 views
38

Digamos que tengo las clases:C++ de baseclass específicos

class Base{}; 

class A: public Base{ 
    int i; 
}; 

class B:public Base{ 
    bool b; 
}; 

y ahora quiero definir una clase de plantilla:

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

Pero quiero definirlo de manera que sólo descendientes de clase Base se puede usar como parámetros de plantilla.

¿Cómo puedo hacer eso?

+3

Es necesario corregir su sintaxis de la plantilla. ¿Y quieres decir que A y B se derivan de forma privada? –

Respuesta

12

Más exactamente:

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 
} 
8

C++ aún no permite esto directamente. Puede darse cuenta de forma indirecta mediante el uso de un STATIC_ASSERT y type checking dentro de la clase:

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; 
}; 
+0

no quiero que los parámetros solo tengan la misma clase base; quiero que tengan mi clase específica Base como clase base – Mat

+0

@Math, oops, comprobación de tipo incorrecto. Todavía estaba cansado. –

+3

La palabra clave es "todavía". Para el beneficio de otros que llegan aquí desde un motor de búsqueda, 'static_assert' y' is_base_of' se graduaron a las características del lenguaje oficial en C++ 11. Hacen un código mucho más intuitivo/menos horrible que la respuesta aceptada. –

0

En primer lugar, fijar la declaración

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

A continuación, se puede declarar en una clase base alguna función privada Foo(); y decirle a la clase Base que tenga a BasePair como amigo; luego en amigo constructor solo tienes que llamar a esta función. De esta forma obtendrá un error de tiempo de compilación cuando alguien intente usar otras clases como parámetros de plantilla.

+3

"arregle la declaración" - lo mismo aplica para usted también. ;-) –

0
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

explica por favor ... ¿cómo garantiza esto que los parámetros de la plantilla son descendientes de B? – Mat

+0

Cuando crea una instancia de la plantilla P ok; el compilador intentará usar la función de plantilla static_cast para convertir el puntero de tipo D en puntero de tipo B. Esto funcionará bien pero, en el caso de P nok; esta conversión falla y recibirá un error de compilación (error C2440: 'static_cast': no ​​se puede convertir de 'U *' a 'B *'). Además, esto no tendrá ningún efecto de tiempo de ejecución (el código no es más lento). Es una comprobación de tipo estática o en tiempo de compilación. – user213546

2

Esta fue una gran pregunta! Al investigar esto a través de este link, se me ocurrió lo siguiente, que ciertamente no es muy diferente de la solución provista allí. Aprende algo todos los días ... ¡mira!

#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; 
} 
0

En la respuesta sugerida por unknown (yahoo), no es necesario tener realmente las variables de tipo X e Y como miembros. Estas líneas son suficientes en el constructor:

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

C++ 11 introduce <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; 
}; 
Cuestiones relacionadas