23 votos

Buenas prácticas en cuanto a la especialización de la plantilla y la herencia

Plantilla de especialización no toma en cuenta la jerarquía de herencia. Por ejemplo, si me especializo una plantilla para Base y crear instancias de ella con Derived, la especialización de no ser elegido (ver código (1) a continuación).

Esto puede ser un obstáculo importante, ya que a veces llevan a la violación del principio de sustitución de Liskov. Por ejemplo, mientras se trabaja en esta pregunta, me di cuenta de que no podía usar el Boost.Rango de algoritmos de con std::sub_match , mientras yo podía con std::pair. Desde sub_match hereda públicamente desde pair, el sentido común dictaría que yo podría sustituir un sub_match todas partes un pair es usado, pero esta falla debido a un rasgo de las clases de uso de la plantilla de la especialización.

Podemos superar este problema mediante el uso parcial de la plantilla de especialización a lo largo de con enable_if y is_base_of (ver código (2)). Debo siempre a favor de esta solución con respecto a la especialización, sobre todo cuando se escribe código de la biblioteca? ¿Hay alguna desventaja de este enfoque, que he supervisado? Es una práctica que utiliza o ha visto a menudo se utilizan?


Códigos de ejemplo

(1)
#include <iostream>

struct Base {};
struct Derived : public Base {};

template < typename T >
struct Foo
{
    static void f() { std::cout << "Default" << std::endl; }
};

template <>
struct Foo< Base >
{
    static void f() { std::cout << "Base" << std::endl; }
};

int main()
{
    Foo<Derived>::f(); // prints "Default"
}

(2)
#include <type_traits>
#include <iostream>

struct Base {};
struct Derived : public Base {};

template <typename T, typename Enable = void>
struct Foo
{
    static void f() { std::cout << "Default" << std::endl; }
};

template <typename T>
struct Foo<
    T, typename 
    std::enable_if< std::is_base_of< Base, T >::value >::type
>
{
    static void f() { std::cout << "Base" << std::endl; }
};

int main()
{
    Foo<Derived>::f(); // prints "Base"
}

13voto

sehe Puntos 123151

enable_if es más flexible

Creo que usted debe realmente prefiero el enable_if enfoque: permite a todo lo que pueda requerir y más.

E. g. podría haber casos en que una clase Derivada es Liskov-Subsitutable para un Base, pero [no puede asumir/nose que desee aplicar] los mismos rasgos/especializaciones para ser válido (por ejemplo, porque la Base es la VAINA de la clase, mientras que los Derivados y no POD comportamiento o algo por el estilo que es completamente ortogonal a la composición de la clase).

enable_if le da el poder para definir exactamente las condiciones.

Enfoque híbrido

También se podría conseguir algo de un plano medio mediante la implementación de una clase de rasgos que se deriva de algunas aplicaciones específicas de los rasgos de propósito general de los rasgos. La "costumbre" rasgos podrían utilizar el enable_if y meta-técnicas de programación para aplicar rasgos como polymorphically como usted desea. De esa manera, sus implementaciones reales no se tiene que repetir algunos complicado enable_if/envío de baile, pero en lugar de simplemente consumir la costumbre, los rasgos de la clase (que oculta la complejidad).

Creo que algunos (muchos?) Impulso de las bibliotecas utilizan el enfoque híbrido (lo he visto en alguna capacidad, donde los puentes por ejemplo, la fusión/mpl, creo que también varios iterador rasgos en el Espíritu).

Personalmente, me gusta este método porque puede aislar efectivamente el 'fontanería' desde el core-business de una biblioteca, haciendo que el mantenimiento y la documentación (!) mucho más fácil.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X