2008-11-11 16 views
21

Quiero hacer esta especialización sin cambiar la principal. ¿Es posible especializar algo basado en su clase base? Eso espero.Especialización de plantillas basada en heredar la clase

operación -Editar-

voy a tener varias clases que heredan 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; 
} 

Respuesta

23

este artículo describe un buen truco: http://www.gotw.ca/publications/mxc++-item-4.htm

Aquí está la idea básica. Primero tiene una clase IsDerivedFrom (esto proporciona tiempo de ejecución y tiempo de compilación comprobació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; } 
}; 

Luego, su MiClase necesita una aplicación que está potencialmente especializados:

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 MiClase realidad se parece a:

template<typename T> 
class MyClass: public MyClassImpl<T, IsDerivedFrom<T, SomeTag>::Is> 
{ 
}; 

Entonces su principal estará bien tal como está:

int main() 
{ 
    MyClass<SomeTag>::isSpecialized test1; //ok 
    MyClass<InheritSomeTag>::isSpecialized test2; //ok also 
    return 0; 
} 
+0

La mente se llena! Supongo que el punto de la función indefinida 'No' es ..... – John

+0

Hermosa. Tengo solo una pregunta realmente estúpida: ¿hay alguna razón por la que 'clase Yes' se declare como' No no [3] 'en lugar de, por ejemplo,' No no [2] '? Supongo que eso también funcionaría, pero me puede estar perdiendo algo importante ... –

3

En su caso, la única manera que veo sería especializarse explícitamente MyClass para InheritSomeTag. Sin embargo, el SeqAn paper propone un mecanismo llamado "plantilla subclase" que hace lo que quiere, aunque con una sintaxis de herencia diferente, por lo que el código no es compatible con su función actual main.

// 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 sin duda tiene un aspecto extraño y es muy engorroso sino que 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 SeqAn examples.

Dicho esto, creo que SeqAn es un caso especial y no hay muchas aplicaciones se beneficiarían de esta sintaxis extremadamente difícil (descifrar los errores de compilación relacionados SeqAn-es un verdadero dolor en el * ss!)

19

Bueno, el artículo en la respuesta anterior apareció en febrero de 2002. Mientras funciona, hoy sabemos que hay mejores formas. Como alternativa, puede utilizar 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 */ 
} 
+1

Hombre, me olvidé por completo de SFINAE cuando escribí mi respuesta. Aún así, estoy dejando que sea una alternativa extraña. –

+0

@litb: aunque la definición de las estructuras de utilidades 'enable_if' y' is_base_of' es redundante si se puede usar TR1; pero gracias por hacerlo aquí, me ayudó a entender :) – legends2k

+0

'boost' también tiene esta funcionalidad incorporada: [enable_if] (http://www.boost.org/doc/libs/1_47_0/libs/utility/enable_if.html) y [is_base_of] (http://www.boost.org/doc/libs/1_41_0/libs/type_traits/doc/html/boost_typetraits/reference/is_base_of.html).(Esto fue útil para mí, ya que ya estaba usando boost en mi paquete, tal vez sea útil para otra persona). –

14

Y ahora la versión corta de 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 */ 
} 
Cuestiones relacionadas