variable usar una sirve que para metodos metodo instancia estaticos estatico definicion cuando clase c++ metaprogramming generic-programming partial-specialization

c++ - usar - Despacho de etiquetas frente a métodos estáticos en clases parcialmente especializadas



que es una instancia en java (4)

Supongamos que quiero escribir una función genérica void f<T>() , que hace una cosa si T es un tipo POD y otra cosa si T no es POD (o cualquier otro predicado arbitrario).

Una forma de lograr esto sería usar un patrón de despacho de etiquetas como lo hace la biblioteca estándar con las categorías de iteradores:

template <bool> struct podness {}; typedef podness<true> pod_tag; typedef podness<false> non_pod_tag; template <typename T> void f2(T, pod_tag) { /* POD */ } template <typename T> void f2(T, non_pod_tag) { /* non-POD */ } template <typename T> void f(T x) { // Dispatch to f2 based on tag. f2(x, podness<std::is_pod<T>::value>()); }

Una alternativa sería usar la función de miembro estático de tipos parcialmente especializados:

template <typename T, bool> struct f2; template <typename T> struct f2<T, true> { static void f(T) { /* POD */ } }; template <typename T> struct f2<T, false> { static void f(T) { /* non-POD */ } }; template <typename T> void f(T x) { // Select the correct partially specialised type. f2<T, std::is_pod<T>::value>::f(x); }

¿Cuáles son los pros y los contras de usar un método sobre el otro? ¿Cuál recomendarías?


En realidad, ambos son solo del patrón de despacho de etiquetas. El anterior se llama despacho de etiqueta por instancia y el último es despachador de etiqueta por tipo .

Barend, el autor principal de Boost.Geometry, explains ambos métodos y prefiere este último. Esto se usa en Boost.Geometry extensivamente. Aquí están las ventajas resumidas:

  • No es necesario crear una instancia de la etiqueta ya que su único propósito es diferenciar
  • Es fácil definir nuevos tipos y constantes basados ​​en etiquetas
  • Los argumentos pueden invertirse en la interfaz, es decir, decir distance(point, polygon); y distance(polygon, point); ambos pueden tener solo una implementación

Me gustaría despachar la etiqueta porque:

  • Fácil de extender con nuevas etiquetas
  • Herencia fácil de usar ( example )
  • Es una técnica bastante común en la programación genérica

Me parece complicado agregar una tercera variante en el segundo ejemplo. Cuando desee agregar, por ejemplo, un tipo que no sea POD-of-PODs, tendrá que reemplazar bool en la template <typename T, bool> struct f2; con algo diferente ( int si te gusta =)) y reemplaza todas las struct f2<T, bool-value> con struct f2<T, another-type-value> . Entonces, para mí, la segunda variante parece difícilmente extensible. Por favor corrígeme si me equivoco.


Sé que esta es una vieja pregunta con la respuesta ya aceptada, pero esta podría ser una alternativa viable:

template<typename T> std::enable_if_t<std::is_pod<T>::value> f(T pod) { } template<typename T> std::enable_if_t<!std::is_pod<T>::value> f(T non_pod) { }


Una alternativa legible para [boost|std]::enable_if , tags y especialización parcial para el simple despacho en tiempo de compilación que me gusta es la siguiente:

[Recuerde que los booleanos tienen conversión a enteros, que los arrays de longitud cero no son válidos y que las plantillas ofensivas se descartan (SFINAE). Además, char (*)[n] es un puntero a una matriz de n elementos.]

template <typename T> void foo(T, char (*)[is_pod<T>::value] = 0) { // POD } template <typename T> void foo(T, char (*)[!is_pod<T>::value] = 0) { // Non POD }

También tiene la ventaja de no necesitar clases externas que contaminen el espacio de nombres. Ahora, si quiere externalizar el predicado como en su pregunta, puede hacer:

template <bool what, typename T> void foo(T, char (*)[what] = 0) { // taken when what is true } template <bool what, typename T> void foo(T, char (*)[!what] = 0) { // taken when what is false }

Uso:

foo<std::is_pod<T>::value>(some_variable);