c++ - que - Especialización de plantillas basada en heredar clase
polimorfismo en c++ (4)
Quiero hacer este especial sin cambio principal. ¿Es posible especializar algo basado en su clase base? Eso espero.
-editar-
Tendré varias clases que heredarán de SomeTag. No quiero escribir la misma especialización para cada uno de ellos.
class SomeTag {};
class InheritSomeTag : public SomeTag {};
template <class T, class Tag=T>
struct MyClass
{
};
template <class T>
struct MyClass<T, SomeTag>
{
typedef int isSpecialized;
};
int main()
{
MyClass<SomeTag>::isSpecialized test1; //ok
MyClass<InheritSomeTag>::isSpecialized test2; //how do i make this specialized w/o changing main()
return 0;
}
Bueno, el artículo en la respuesta anterior apareció en febrero de 2002. Mientras funciona, hoy sabemos que hay mejores formas. Alternativamente, puede usar enable_if
:
template<bool C, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false, T> { };
template<typename, typename>
struct is_same {
static bool const value = false;
};
template<typename A>
struct is_same<A, A> {
static bool const value = true;
};
template<typename B, typename D>
struct is_base_of {
static D * create_d();
static char (& chk(B *))[1];
static char (& chk(...))[2];
static bool const value = sizeof chk(create_d()) == 1 &&
!is_same<B volatile const,
void volatile const>::value;
};
struct SomeTag { };
struct InheritSomeTag : SomeTag { };
template<typename T, typename = void>
struct MyClass { /* T not derived from SomeTag */ };
template<typename T>
struct MyClass<T, typename enable_if<is_base_of<SomeTag, T>::value>::type> {
typedef int isSpecialized;
};
int main() {
MyClass<SomeTag>::isSpecialized test1; /* ok */
MyClass<InheritSomeTag>::isSpecialized test2; /* ok */
}
En su caso, la única forma que veo es especializar explícitamente MyClass
para InheritSomeTag
. Sin embargo, el documento SeqAn propone un mecanismo llamado "clasificación de plantillas" que hace lo que usted desea, aunque con una sintaxis de herencia diferente, por lo que el código no es compatible con su función main
actual.
// Base class
template <typename TSpec = void>
class SomeTag { };
// Type tag, NOT part of the inheritance chain
template <typename TSpec = void>
struct InheritSomeTag { };
// Derived class, uses type tag
template <typename TSpec>
class SomeTag<InheritSomeTag<TSpec> > : public SomeTag<void> { };
template <class T, class Tag=T>
struct MyClass { };
template <class T, typename TSpec>
struct MyClass<T, SomeTag<TSpec> >
{
typedef int isSpecialized;
};
int main()
{
MyClass<SomeTag<> >::isSpecialized test1; //ok
MyClass<SomeTag<InheritSomeTag<> > >::isSpecialized test2; //ok
}
Esto ciertamente se ve extraño y es muy engorroso, pero permite un verdadero mecanismo de herencia con funciones polimórficas que se ejecuta en tiempo de compilación. Si quieres ver esto en acción, echa un vistazo a algunos ejemplos de SeqAn .
Dicho esto, creo que SeqAn es un caso especial y no muchas aplicaciones se beneficiarían de esta sintaxis extremadamente difícil (descifrar los errores del compilador relacionados con SeqAn es un verdadero dolor en el * ss).
Este artículo describe un buen truco: http://www.gotw.ca/publications/mxc++-item-4.htm
Aquí está la idea básica. Primero necesita una clase IsDerivedFrom (esto proporciona el tiempo de ejecución y la verificación en tiempo de compilación):
template<typename D, typename B>
class IsDerivedFrom
{
class No { };
class Yes { No no[3]; };
static Yes Test( B* ); // not defined
static No Test( ... ); // not defined
static void Constraints(D* p) { B* pb = p; pb = p; }
public:
enum { Is = sizeof(Test(static_cast<D*>(0))) == sizeof(Yes) };
IsDerivedFrom() { void(*p)(D*) = Constraints; }
};
Entonces su MyClass necesita una implementación potencialmente especializada:
template<typename T, int>
class MyClassImpl
{
// general case: T is not derived from SomeTag
};
template<typename T>
class MyClassImpl<T, 1>
{
// T is derived from SomeTag
public:
typedef int isSpecialized;
};
y MyClass en realidad se ve así:
template<typename T>
class MyClass: public MyClassImpl<T, IsDerivedFrom<T, SomeTag>::Is>
{
};
Entonces su principal estará bien de la manera que es:
int main()
{
MyClass<SomeTag>::isSpecialized test1; //ok
MyClass<InheritSomeTag>::isSpecialized test2; //ok also
return 0;
}
Y la versión corta ahora, 2014, usando C ++ - 11:
#include <type_traits>
struct SomeTag { };
struct InheritSomeTag : SomeTag { };
template<typename T, bool = std::is_base_of<SomeTag, T>::value>
struct MyClass { };
template<typename T>
struct MyClass<T, true> {
typedef int isSpecialized;
};
int main() {
MyClass<SomeTag>::isSpecialized test1; /* ok */
MyClass<InheritSomeTag>::isSpecialized test2; /* ok */
}