Saltar a contenido

Última actualización:2026-10-12

Plantillas de variable en C++17

C++那些事: plantillas de variable

Al escribir código con plantillas, a menudo necesitamos definir y usar diversos rasgos de tipo (type traits). Estos rasgos se emplean normalmente para comprobar tipos en tiempo de compilación; los std::is_const, std::is_same y std::is_reference que proporciona la STL son ejemplos de ello. Gracias a estos rasgos, quien desarrolla puede tomar decisiones en tiempo de compilación según las propiedades de los tipos.

Este artículo presentará cómo usar las plantillas de variable de C++14 (variable templates) para simplificar la escritura de rasgos de tipo, junto con un ejemplo sencillo.

C++11

En C++11, escribir un rasgo de tipo suele requerir usar struct o std::integral_constant para encapsular un valor booleano. Por ejemplo, para escribir un rasgo de tipo is_float que determine si un tipo es float, podemos implementarlo así:

template <typename T>
struct is_float {
    static constexpr bool value = std::is_same<T, float>::value;
};

O algo más conciso, usando un alias de tipo y std::integral_constant:

template <typename T>
using is_float = std::integral_constant<bool, std::is_same<T, float>::value>;

Aunque esto es más conciso que usar directamente un struct, al final seguimos definiendo un tipo en lugar de obtener directamente un valor booleano. Para usar este rasgo hay que obtener el booleano a través del miembro ::value:

template <typename T>
void test(T t){
    if (is_float<T>::value){
        std::cout << "I'm a float" << std::endl;
    } else {
        std::cout << "I'm not a float" << std::endl;
    }
}

Esto funciona, pero presenta dos carencias: 1. Hay que acceder a ::value cada vez que se usa el rasgo de tipo. 2. Al definir un rasgo nuevo hay que declarar un tipo o alias de tipo nuevo, lo que añade redundancia al código.

La introducción de las plantillas de variable en C++14

C++14 introdujo las plantillas de variable, que permiten definir directamente variables que dependen de tipos. Esta característica simplifica la escritura de rasgos de tipo: ya no hace falta usar alias de tipo o struct para encapsular el booleano, sino que puede definirse directamente el valor booleano.

El rasgo is_float reescrito queda así:

template <typename T>
constexpr bool is_float = std::is_same<T, float>::value;

Esta forma es más concisa y clara, y elimina el código innecesario. Además, su uso es más cómodo:

template <typename T>
void test(T t){
    if (is_float<T>){
        std::cout << "I'm a float" << std::endl;
    } else {
        std::cout << "I'm not a float" << std::endl;
    }
}

En comparación con la escritura anterior, al usar plantillas de variable ya no hace falta acceder al booleano mediante ::value, y el código resulta más conciso e intuitivo.

Ventajas de las plantillas de variable

  1. Menos código redundante: las plantillas de variable permiten definir directamente variables basadas en tipos, evitando el código redundante de encapsular booleanos con struct o std::integral_constant.
  2. Mayor legibilidad: al simplificar la sintaxis, las plantillas de variable hacen el código más claro, sobre todo cuando se escriben muchos rasgos de tipo.
  3. Soporte de especialización de plantillas: a diferencia de las plantillas de alias de tipo, las plantillas de variable pueden especializarse, tanto de forma total como parcial. Podemos definir lógica específica para tipos concretos sin preocuparnos por la extensibilidad.

Mejoras adicionales en C++17

C++17 amplió aún más la aplicación de las plantillas de variable, proporcionando utilidades en forma de plantilla de variable para cada rasgo de tipo. Por ejemplo, std::is_same incorporó la plantilla de variable std::is_same_v, que simplifica el acceso al valor del rasgo de tipo:

template <typename T>
constexpr bool is_float = std::is_same_v<T, float>;

De esta manera, el uso de los rasgos de tipo es más conciso: se pueden usar directamente las plantillas de variable sin acceder a ::value cada vez.

Caso práctico

Esta característica se usa bastante en el mundo real; por ejemplo: para asignar bloques de memoria de distintos tamaños en una base de datos, podemos inicializar mediante plantillas tamaños de bloque para diferentes tipos.

template <typename T>
constexpr size_t block_size = 1024;  // 默认大小

template <>
constexpr size_t block_size<double> = 2048;  // double 类型需要更多内存

Temas relacionados

Comentarios