2010-06-17 15 views
8

me pregunto ¿hay alguna manera de establecer restricciones a la clase de plantilla?
Especifique que cada tipo sustituido en la plantilla debe tener antecesor específico (realizar alguna interfaz).C++ restricciones plantilla

template < class B > //and every B must be a child of abstract C 
class A { 
public: 
    B * obj; 
    int f() { 
     return B::x + this->obj->f(); 
    } 
}; 

Como => en Haskell

func :: (Ord a, Show b) => a -> b -> c 
+1

¿Por qué cree que necesita esto? – fredoverflow

+0

posible duplicado de [plantilla de clase de C++ de la clase base específica] (http://stackoverflow.com/questions/2012950/c-class-template-of-specific-baseclass) – SLaks

+1

taaaaan no es una cuestión principiante :-). Los conceptos son en realidad bastante controvertidos. –

Respuesta

2

Puede use BOOST_STATIC_ASSERT o una biblioteca similar a hacer valer sus restricciones en el parámetro de plantilla.

Por ejemplo:

#include <limits> 
#include <boost/static_assert.hpp> 

template <class UnsignedInt> 
class myclass 
{ 
private: 
    BOOST_STATIC_ASSERT((std::numeric_limits<UnsignedInt>::digits >= 16) 
         && std::numeric_limits<UnsignedInt>::is_specialized 
         && std::numeric_limits<UnsignedInt>::is_integer 
         && !std::numeric_limits<UnsignedInt>::is_signed); 
public: 
    /* details here */ 
}; 

EDITAR: Para su ejemplo, puede escribir

template < class B > 
class A { 
    BOOST_STATIC_ASSERT(boost::is_base_of<C, B>); 

public: 
    B * obj; 
    int f() { 
     return B::x + this->obj->f(); 
    } 
}; 
2

Se puede usar un truco como este (si no desea utilizar Boost) :

class Base 
    { 
    public: 
     static const int TEMPLATE_REQUIRES_BASE_CLASS = 0; 
    }; 

class Correct : public Base 
    { 
    }; 

class Incorrect 
    { 
    }; 

template <typename T> 
class TMPL 
    { 
    static const int TEMPLATE_REQUIRES_BASE_CLASS = T::TEMPLATE_REQUIRES_BASE_CLASS; 
    T *m_t; 
    }; 

void main() 
{ 
TMPL<Correct> one;  // OK 
TMPL<Incorrect> two; // Will not compile 
} 

Se compilará la primera línea. El segundo no se compilará y dará el siguiente error:

test.cpp 
test.cpp(18) : error C2039: 'TEMPLATE_REQUIRES_BASE_CLASS' : is not a member of 'Incorrect' 
     test.cpp(12) : see declaration of 'Incorrect' 
     test.cpp(25) : see reference to class template instantiation 'TMPL<T>' being compiled 
     with 
     [ 
      T=Incorrect 
     ] 
test.cpp(18) : error C2065: 'TEMPLATE_REQUIRES_BASE_CLASS' : undeclared identifier 
test.cpp(18) : error C2057: expected constant expression 
+0

Este código no falla bajo g ++ 4.4, debido a alguna razón desconocida.En general, yo diría que en este caso, una enumeración se utiliza por lo general: enum {TEMPLATE_REQUIRES_BASE_CLASS = 0}; Esto tiene la ventaja adicional de no como se esperaba bajo g ++. – iksemyonov

+0

@Semen, GCC no es conforme. El estándar requiere la declaración del miembro de datos para crear una instancia cuando la clase se instancia de manera implícita, y un inicializador en clase es parte de la declaración en lugar de su definición. Sin embargo, el estándar es poco claro, diciendo que "la inicialización (y los efectos secundarios asociados) de un miembro de datos estáticos no se produce a menos que el miembro de datos estático es en sí utiliza de una manera que requiere la definición del miembro de datos estáticos de existir. " - Sin embargo esta regla parece aplicarse únicamente a un inicializador fuera de clase (que no tiene sentido de otra manera) –

+0

@Johannes: Gracias por la explicación, pensé que me estaba volviendo loco cuando construyó bien ... pero supongo enumeración sigue siendo más simple de usar. (Solo estoy siguiendo a Vandervourde :) – iksemyonov

6

Una versión futura de C++ apoyará esta forma nativa utilizando conceptos (que no lo hacen en C++ 11).

Una manera de abordar el problema es el uso de la especialización en un parámetro de plantilla dummy:

class C {}; 
template <class B, class dummy=void> 
class A; 

template <class B> 
class A<B, typename enable_if<is_base_and_derived<C, B> >::type> 
{ 
    // class definition here 
}; 

struct D : C {}; 

A<D> d;  // fine 
A<int> n; // compile error - undefined class A<B> 

He puesto definiciones independientes de enable_if y is_base_and_derivedhere.

+0

Gracias, increíble pieza de código – Andrew

1

plantillas son una especie de pato a escribir en C++.

Si la clase es compatible con todo lo que utiliza la plantilla, a continuación, se puede utilizar como parámetro de plantilla, de lo contrario no se puede.

Si en su plantilla que tiene algo así como

C *instance; 

void foo(T *t) 
{ 
    instance = t; 
} 

continuación, que están haciendo cumplir que T se deriva de C (o al menos asignacion-compatibles para los punteros)

3

las siguientes obras en VC10 utilizando static_assert. Acabo de ver esto usado y realmente no he profundizado mucho en lo que static_assert realmente hace, quizás alguien más pueda responder eso.

#include <type_traits> 

class Base 
{ 

}; 

class Derived : public Base 
{ 

}; 

class SomeRandomClass 
{ 

}; 

template<typename T> 
class A 
{ 
    static_assert(std::tr1::is_base_of<Base, T>::value, "T not derived from Base"); 
}; 



int _tmain(int argc, _TCHAR* argv[]) 
{ 
    argc; argv; 

    // 
    // This will compile 
    A<Derived> a; 

    // 
    // This will throw a compilation error 
    A<SomeRandomClass> b; 

    return 0; 
} 

El ser salida del compilador:

1>d:\temp\aaa\aaa\aaa.cpp(25): error C2338: T not derived from Base 
1>   d:\temp\aaa\aaa\aaa.cpp(41) : see reference to class template instantiation 'A<T>' being compiled 
1>   with 
1>   [ 
1>    T=SomeRandomClass 
1>   ]