programacion - C++ 14 Plantillas variables: ¿cuál es su propósito? Cualquier ejemplo de uso?
manual de programacion android pdf (5)
C ++ 14 permitirá la creación de variables que están modeladas. El ejemplo habitual es una variable ''pi'' que se puede leer para obtener el valor de la constante matemática π para varios tipos (3 para int
; el valor más cercano posible con float
, etc.)
Además de eso, podemos tener esta función simplemente envolviendo una variable dentro de una estructura o clase con plantilla, ¿cómo se mezcla esto con las conversiones de tipo? Veo un poco de superposición.
Y aparte del ejemplo pi, ¿cómo funcionaría con las variables no const? ¿Algún ejemplo de uso para comprender cómo aprovechar al máximo esta característica y cuál es su propósito?
Y aparte del ejemplo pi, ¿cómo funcionaría con las variables no const?
Actualmente, parece crear instancias de las variables por separado para el tipo. es decir, podría asignar 10 a n<int>
y sería diferente de la definición de la plantilla.
template<typename T>
T n = T(5);
int main()
{
n<int> = 10;
std::cout << n<int> << " "; // 10
std::cout << n<double> << " "; // 5
}
Si la declaración es const
, es de solo lectura. Si es un constexpr
, como todas constexpr
declaraciones constexpr
, no tiene mucho uso fuera de constexpr
(ressions).
Además de eso, podemos tener esta función simplemente envolviendo una variable dentro de una estructura o clase con plantilla, ¿cómo se mezcla esto con las conversiones de tipo?
Debe ser una propuesta simple. No puedo ver cómo afecta las conversiones de tipo de una manera significativa. Como ya dije, el tipo de variable es el tipo con el que creó la instancia de la plantilla. es decir, decltype(n<int>)
es int. decltype((double)n<int>)
es doble y así sucesivamente.
¿Algún ejemplo de uso para comprender cómo aprovechar al máximo esta característica y cuál es su propósito?
N3651 proporciona una razón de ser sucinta.
Por desgracia, las reglas existentes de C ++ no permiten que una declaración de plantilla declare una variable. Existen soluciones bien conocidas para este problema:
• usar miembros de datos estáticos constexpr de plantillas de clase
• utilizar plantillas de función constexpr devolviendo los valores deseados
Estas soluciones temporales se conocen desde hace décadas y están bien documentadas. Las clases estándar como std :: numeric_limits son ejemplos arquetípicos. Aunque estas soluciones no son perfectas, sus inconvenientes fueron tolerables hasta cierto punto porque en la era de C ++ 03 solo las constantes simples de tipos incorporados disfrutaban de soporte de tiempo de compilación directo y eficiente sin restricciones. Todo eso cambió con la adopción de variables constexpr en C ++ 11, que extendió el soporte directo y eficiente a constantes de tipos definidos por el usuario. Ahora, los programadores están haciendo constantes (de tipos de clases) cada vez más evidentes en los programas. Así que aumente la confusión y las frustraciones asociadas con las soluciones.
...
Los principales problemas con el "miembro de datos estáticos" son:
• requieren declaraciones "duplicadas": una vez dentro de la plantilla de la clase, una vez fuera de la plantilla de la clase para proporcionar la definición "real" en caso de que se utilicen las constantes.
• Los programadores están molestos y confundidos por la necesidad de proporcionar el doble de la misma declaración. Por el contrario, las declaraciones constantes "ordinarias" no necesitan declaraciones duplicadas.
...
Los ejemplos bien conocidos en esta categoría son probablemente funciones miembro estáticas de numeric_limits, o funciones como
boost::constants::pi<T>()
, etc. Las plantillas de funciones Constexpr no sufren el problema de "declaraciones duplicadas" que los miembros de datos estáticos tienen ; además, proporcionan abstracción funcional. Sin embargo, obligan al programador a elegir de antemano, en el sitio de definición, cómo deben entregarse las constantes: ya sea mediante una referencia constante, o mediante un tipo simple sin referencia. Si se entrega por referencia constante, las constantes se deben asignar sistemáticamente en almacenamiento estático; si por tipo de no referencia, las constantes necesitan copia. La copia no es un problema para los tipos incorporados, pero es una maravilla para los tipos definidos por el usuario con semántica de valores que no son solo envoltorios alrededor de pequeños tipos integrados (por ejemplo, matriz o entero o bigfloat, etc.) Por el contrario, " las variables comunes "const (expr) no sufren este problema. Se proporciona una definición simple, y la decisión de si las constantes realmente necesitan diseñarse en almacenamiento solo depende del uso, no de la definición.
podemos tener esta característica simplemente envolviendo una variable dentro de una estructura o clase con plantilla
Sí, pero eso sería una sal sintáctica gratuita. No saludable para la presión arterial.
pi<double>
transmite la intención mejor que pi<double>::value
. Corto y al grano. Esa es una razón suficiente en mi libro para permitir y fomentar esta sintaxis.
Bueno, puedes usar esto para escribir código de tiempo de compilación como este:
#include <iostream>
template <int N> const int ctSquare = N*N;
int main() {
std::cout << ctSquare<7> << std::endl;
}
Esto es una mejora significativa sobre el equivalente
#include <iostream>
template <int N> struct ctSquare {
static const int value = N*N;
};
int main() {
std::cout << ctSquare<7>::value << std::endl;
}
que las personas solían escribir para realizar la metaprogramación de plantillas antes de que se introdujeran las plantillas variables. Para los valores que no son de tipo, pudimos hacer esto desde C ++ 11 con constexpr
, por lo que las variables de plantilla tienen la ventaja de permitir cálculos basados en los tipos de las plantillas variables.
TL; DR: No nos permiten hacer nada que no pudiéramos hacer antes, pero hacen que la metaprogramación de plantillas sea menos de un PITA.
Me pregunto si algo como esto sería posible: (asumiendo la disponibilidad de la plantilla lambdas)
void some_func() {
template<typename T>
std::map<int, T> storage;
auto store = []<typename T>(int key, const T& value) { storage<T>[key] = value; };
store(0, 2);
store(1, "Hello"s);
store(2, 0.7);
// All three values are stored in a different map, according to their type.
}
Ahora, ¿esto es útil?
Como un uso más simple, observe que la inicialización de pi<T>
usa conversión explícita (llamada explícita de un constructor unario) y no una inicialización uniforme. Lo que significa que, dado un tipo de radians
con un constructor radians(double)
, puede escribir pi<radians>
.
Otro ejemplo práctico para las plantillas de variables de C ++ 14 es cuando necesita una función para pasar algo a std::accumulate
:
template<typename T>
T const & (*maxer) (T const &, T const &) = std::max<T>;
std::accumulate(some.begin(), some.end(), initial, maxer<float>);
Tenga en cuenta que usar std::max<T>
es insuficiente porque no puede deducir la firma exacta. En este ejemplo particular, puede usar max_element
en max_element
lugar, pero el punto es que hay toda una clase de funciones que comparten este comportamiento.