Envío de etiquetas versus métodos estáticos en clases parcialmente especializadas


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 envío 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 miembro estática 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? Que ¿me lo recomendarías?

Author: Aziz Shaikh, 2011-08-02

4 answers

Me gustaría enviar etiquetas porque:

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

Me parece difícil agregar la tercera variante en el segundo ejemplo. Cuando desee agregar, por ejemplo, el tipo no-POD-of-pods, tendrá que reemplazar bool en template <typename T, bool> struct f2; con algo más (int si desea =) ) y reemplazar todo struct f2<T, bool-value> con struct f2<T, another-type-value>. Así que para mí la segunda variante no parece extensible. Por favor corrígeme si me equivoco.

 14
Author: tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-03 13:00:21

Una legible alternativa a [boost|std]::enable_if, etiquetas y especialización parcial para simple compile-time dispatch 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). También, char (*)[n] es un puntero a una matriz de elementos n.]

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 quieres externalizar el predicado como en tu pregunta, puedes 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);
 15
Author: Alexandre C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-02 20:06:48

En realidad, ambos son solo del patrón de envío de etiquetas. El primero se llama tag dispatching by instancey el segundo es tag dispatching by type.

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

  • No es necesario crear instancias de la etiqueta ya que su único propósito es diferenciar{[14]]}
  • es fácil definir nuevos tipos y constantes basados en etiquetas
  • Los argumentos se pueden invertir en la interfaz, es decir, decir distance(point, polygon); y distance(polygon, point); ambos pueden tener una sola implementación
 10
Author: legends2k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-09 06:34:46

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)
{
}
 1
Author: Felics,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-21 19:35:49