Solo quiero asegurarme de que T sea en realidad una instancia de una subclase o la clase en sí. El código dentro de la función que he proporcionado es bastante irrelevante.
phant0m
6
al contrario, es muy relevante. Determina si es una buena idea o no poner trabajo en esa prueba. En muchos (¿todos?) Casos, no hay absolutamente ninguna necesidad de imponer tales restricciones usted mismo, sino dejar que el compilador lo haga al crear una instancia. Por ejemplo, para la respuesta aceptada, sería bueno marcar si se Tderiva de Baseclass. A partir de ahora, esa verificación está implícita y no es visible para sobrecargar la resolución. Pero si en ninguna parte se aplica tal restricción implícita, no parece haber razón para una restricción artificial.
Johannes Schaub - litb
1
Sí estoy de acuerdo. Sin embargo, solo quería saber si hay una manera de lograr esto o no :) Pero, por supuesto, tiene un punto muy válido y gracias por la información.
phant0m
Respuestas:
53
En este caso puedes hacer:
template <classT>voidfunction(){
Baseclass *object = new T();
}
Esto no se compilará si T no es una subclase de Baseclass (o T es Baseclass).
ah sí, es una buena idea. ¡Gracias! Supongo que no hay forma de definirlo en la definición de la plantilla.
phant0m
2
@ phant0m: Correcto. No puede restringir explícitamente los parámetros de la plantilla (excepto mediante el uso de conceptos, que se consideraron para c ++ 0x pero luego se eliminaron). Todas las restricciones ocurren implícitamente por las operaciones que realiza en él (o en otras palabras, la única restricción es "El tipo debe admitir todas las operaciones que se realizan en él").
sepp2k
1
ah ic. ¡Muchas gracias por la aclaración!
phant0m
8
Eso ejecuta el constructor T () y requiere la existencia del constructor T (). Vea mi respuesta para ver una forma que evita esos requisitos.
Douglas Leeder
3
Agradable y claro, pero esto es un problema si T es una clase "pesada".
3Dave el
84
Con un compilador compatible con C ++ 11, puede hacer algo como esto:
template<classDerived>classMyClass {
MyClass() {
// Compile-time sanity checkstatic_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass");
// Do other construction related stuff...
...
}
}
Probé esto usando el compilador gcc 4.8.1 dentro de un entorno CYGWIN, por lo que también debería funcionar en entornos * nix.
Para mí también funciona así: template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");...
Matthias Dieter Wallnöfer
1
Creo que esta es la respuesta más legible que evita código adicional en tiempo de ejecución.
Kyle
50
Para ejecutar código menos inútil en tiempo de ejecución, puede consultar:
http://www.stroustrup.com/bs_faq2.html#constraints
que proporciona algunas clases que realizan la prueba de tiempo de compilación de manera eficiente y producen mensajes de error más agradables.
Para mí, esta es la mejor y más interesante respuesta. Asegúrese de consultar las preguntas frecuentes de Stroustrup para leer más sobre todo tipo de restricciones que podría aplicar de manera similar.
Esta es una respuesta genial. ¿Existen buenas formas de evitar las advertencias unused variable 'p'y unused variable 'pb'?
Filip S.
@FilipS. añadir (void)pb;después B* pb = p;.
bit2shift
11
No necesita conceptos, pero puede usar SFINAE:
template <typename T>
boost::enable_if< boost::is_base_of<Base,T>::value >::type function(){
// This function will only be considered by the compiler if// T actualy derived from Base
}
Tenga en cuenta que esto creará una instancia de la función solo cuando se cumpla la condición, pero no proporcionará un error sensible si no se cumple la condición.
¿Qué pasa si envuelve todas las funciones de esta manera? por cierto, ¿qué devuelve?
the_drow
El enable_iftoma un segundo parámetro de tipo predeterminado void. La expresión enable_if< true, int >::typerepresenta el tipo int. Realmente no puedo entender cuál es su primera pregunta, puede usar SFINAE para lo que desee, pero no entiendo muy bien qué pretende hacer con esto en todas las funciones.
David Rodríguez - dribeas
7
Desde C ++ 11 no necesita Boost o static_assert. C ++ 11 introduce is_base_of y enable_if. C ++ 14 introduce el tipo de conveniencia enable_if_t, pero si está atascado con C ++ 11, simplemente puede usar enable_if::typeen su lugar.
Alternativa 1
La solución de David Rodríguez se puede reescribir de la siguiente manera:
#include<type_traits>usingnamespacestd;
template <typename T>
enable_if_t<is_base_of<Base, T>::value, void> function() {
// This function will only be considered by the compiler if// T actualy derived from Base
}
Alternativa 2
Desde C ++ 17, tenemos is_base_of_v. La solución se puede volver a escribir en:
#include<type_traits>usingnamespacestd;
template <typename T>
enable_if_t<is_base_of_v<Base, T>, void> function() {
// This function will only be considered by the compiler if// T actualy derived from Base
}
Alternativa 3
También puede limitar la plantilla completa. Puede utilizar este método para definir clases completas. Observe cómo enable_if_tse ha eliminado el segundo parámetro de (anteriormente se había configurado como nulo). Su valor predeterminado es en realidad void, pero no importa, ya que no lo estamos usando.
#include<type_traits>usingnamespacestd;
template <typename T,
typename = enable_if_t<is_base_of_v<Base, T>>>
void function() {
// This function will only be considered by the compiler if// T actualy derived from Base
}
De la documentación de los parámetros de la plantilla, vemos que typename = enable_if_t...es un parámetro de la plantilla con un nombre vacío. Simplemente lo usamos para asegurarnos de que existe una definición de tipo. En particular, enable_if_tno se definirá si Baseno es una base de T.
La técnica anterior se da como ejemplo en enable_if.
T
deriva deBaseclass
. A partir de ahora, esa verificación está implícita y no es visible para sobrecargar la resolución. Pero si en ninguna parte se aplica tal restricción implícita, no parece haber razón para una restricción artificial.Respuestas:
En este caso puedes hacer:
template <class T> void function(){ Baseclass *object = new T(); }
Esto no se compilará si T no es una subclase de Baseclass (o T es Baseclass).
fuente
Con un compilador compatible con C ++ 11, puede hacer algo como esto:
template<class Derived> class MyClass { MyClass() { // Compile-time sanity check static_assert(std::is_base_of<BaseClass, Derived>::value, "Derived not derived from BaseClass"); // Do other construction related stuff... ... } }
Probé esto usando el compilador gcc 4.8.1 dentro de un entorno CYGWIN, por lo que también debería funcionar en entornos * nix.
fuente
template<class TEntity> class BaseBiz { static_assert(std::is_base_of<BaseEntity, TEntity>::value, "TEntity not derived from BaseEntity");
...Para ejecutar código menos inútil en tiempo de ejecución, puede consultar: http://www.stroustrup.com/bs_faq2.html#constraints que proporciona algunas clases que realizan la prueba de tiempo de compilación de manera eficiente y producen mensajes de error más agradables.
En particular:
template<class T, class B> struct Derived_from { static void constraints(T* p) { B* pb = p; } Derived_from() { void(*p)(T*) = constraints; } }; template<class T> void function() { Derived_from<T,Baseclass>(); }
fuente
unused variable 'p'
yunused variable 'pb'
?(void)pb;
despuésB* pb = p;
.No necesita conceptos, pero puede usar SFINAE:
template <typename T> boost::enable_if< boost::is_base_of<Base,T>::value >::type function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Tenga en cuenta que esto creará una instancia de la función solo cuando se cumpla la condición, pero no proporcionará un error sensible si no se cumple la condición.
fuente
enable_if
toma un segundo parámetro de tipo predeterminadovoid
. La expresiónenable_if< true, int >::type
representa el tipoint
. Realmente no puedo entender cuál es su primera pregunta, puede usar SFINAE para lo que desee, pero no entiendo muy bien qué pretende hacer con esto en todas las funciones.Desde C ++ 11 no necesita Boost o
static_assert
. C ++ 11 introduceis_base_of
yenable_if
. C ++ 14 introduce el tipo de convenienciaenable_if_t
, pero si está atascado con C ++ 11, simplemente puede usarenable_if::type
en su lugar.Alternativa 1
La solución de David Rodríguez se puede reescribir de la siguiente manera:
#include <type_traits> using namespace std; template <typename T> enable_if_t<is_base_of<Base, T>::value, void> function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Alternativa 2
Desde C ++ 17, tenemos
is_base_of_v
. La solución se puede volver a escribir en:#include <type_traits> using namespace std; template <typename T> enable_if_t<is_base_of_v<Base, T>, void> function() { // This function will only be considered by the compiler if // T actualy derived from Base }
Alternativa 3
También puede limitar la plantilla completa. Puede utilizar este método para definir clases completas. Observe cómo
enable_if_t
se ha eliminado el segundo parámetro de (anteriormente se había configurado como nulo). Su valor predeterminado es en realidadvoid
, pero no importa, ya que no lo estamos usando.#include <type_traits> using namespace std; template <typename T, typename = enable_if_t<is_base_of_v<Base, T>>> void function() { // This function will only be considered by the compiler if // T actualy derived from Base }
De la documentación de los parámetros de la plantilla, vemos que
typename = enable_if_t...
es un parámetro de la plantilla con un nombre vacío. Simplemente lo usamos para asegurarnos de que existe una definición de tipo. En particular,enable_if_t
no se definirá siBase
no es una base deT
.La técnica anterior se da como ejemplo en
enable_if
.fuente
template <class T : Base>
Se podría usar Boost Concepto Comprobar 's
BOOST_CONCEPT_REQUIRES
:#include <boost/concept_check.hpp> #include <boost/concept/requires.hpp> template <class T> BOOST_CONCEPT_REQUIRES( ((boost::Convertible<T, BaseClass>)), (void)) function() { //... }
fuente
Llamando a funciones dentro de su plantilla que existen en la clase base.
Si intenta crear una instancia de su plantilla con un tipo que no tiene acceso a esta función, recibirá un error en tiempo de compilación.
fuente
T
sea unBaseClass
porque los miembros declarados enBaseClass
podrían repetirse en la declaración deT
.