c++ - tutorial - que es una variable en tag manager
¿Motivo de usar un parámetro de plantilla sin tipo en lugar del parámetro regular? (6)
Bueno, esta es la elección típica entre el polimorfismo en tiempo de compilación y el polimorfismo en tiempo de ejecución.
Según la redacción de su pregunta, parece que no ve nada inusual en los parámetros de plantilla "ordinarios", mientras que percibe los parámetros que no son de tipo como algo extraño y / o redundante. En realidad, el mismo problema se puede aplicar a los parámetros de tipo de plantilla (lo que usted llamó parámetros "ordinarios") también. La funcionalidad idéntica a menudo se puede implementar a través de clases polimórficas con funciones virtuales (polimorfismo en tiempo de ejecución) oa través de parámetros de tipo de plantilla (polimorfismo en tiempo de compilación). También se puede preguntar por qué necesitamos los parámetros del tipo de plantilla, ya que prácticamente todo puede implementarse utilizando clases polimórficas.
En el caso de los parámetros sin tipo , es posible que desee tener algo como esto un día
template <int N> void foo(char (&array)[N]) {
...
}
que no se puede implementar con un valor de tiempo de ejecución.
En C ++ puedes crear plantillas usando un parámetro de plantilla sin tipo como este:
template< int I >
void add( int& value )
{
value += I;
}
int main( int argc, char** argv )
{
int i = 10;
add< 5 >( i );
std::cout << i << std::endl;
}
Que imprime "15" para cout. ¿Para qué sirve esto? ¿Hay alguna razón para usar un parámetro de plantilla sin tipo en lugar de usar algo más convencional como:
void add( int& value, int amount )
{
value += amount;
}
Lo siento si esto ya se ha pedido (miré pero no pude encontrar nada).
El uso más frecuente para un parámetro de valor que puedo pensar es std::get<N>
, que recupera el enésimo elemento de std::tuple<Args...>
. El segundo uso más frecuente sería std::integral_constant
y sus principales derivadas std::true_type
y std::false_type
, que son omnipresentes en cualquier clase de clases de rasgos. De hecho, los rasgos de tipo están absolutamente repletos de parámetros de plantilla de valor. En particular, existen técnicas SFINAE que aprovechan una plantilla de firma <typename T, T>
para verificar la existencia de un miembro de la clase.
En ese caso particular, no hay realmente ninguna ventaja. Pero al usar parámetros de plantilla como ese, puede hacer muchas cosas que no podría hacer de otra manera, como vincular variables de manera efectiva a funciones (como boost::bind
), especificar el tamaño de una matriz en tiempo de compilación en una función o clase ( std::array
es un buen ejemplo de eso), etc.
Por ejemplo, con esa función, escribes una función como
template<typename T>
void apply(T f) {
f(somenum);
}
Entonces puede pasar apply
una función:
apply(&add<23>);
Es un ejemplo extremadamente simple, pero demuestra el principio. Las aplicaciones más avanzadas incluyen la aplicación de funciones a cada valor de una colección, el cálculo de elementos como el factorial de una función en tiempo de compilación y más.
No podrías hacer nada de eso de otra manera.
Hay muchas aplicaciones para argumentos de plantilla sin tipo; Aquí hay algunos:
Puede usar argumentos que no sean de tipo para implementar tipos genéricos que representen matrices o matrices de tamaño fijo. Por ejemplo, puede parametrizar un tipo de Matrix
sobre sus dimensiones, por lo que podría hacer una Matrix<4, 3>
o una Matrix<2, 2>
. Si luego define operadores sobrecargados para estos tipos correctamente, puede prevenir errores accidentales al agregar o multiplicar matrices de dimensiones incorrectas, y puede hacer funciones que comuniquen explícitamente las dimensiones esperadas de las matrices que aceptan. Esto evita que se produzca una gran clase de errores de tiempo de ejecución al detectar las violaciones en tiempo de compilación.
Puede usar argumentos sin tipo para implementar la evaluación de funciones en tiempo de compilación mediante la metaprogramación de plantillas. Por ejemplo, aquí hay una plantilla simple que computa factorial en tiempo de compilación:
template <unsigned n> struct Factorial {
enum {
result = n * Factorial<n - 1>::result
};
};
template <> struct Factorial<0> {
enum {
result = 1
};
};
Esto le permite escribir código como Factorial<10>::result
para obtener, en tiempo de compilación, el valor de 10 !. Esto puede evitar la ejecución adicional de código en tiempo de ejecución.
Además, puede usar argumentos que no sean de tipo para implementar el análisis dimensional en tiempo de compilación , que le permite definir tipos de kilogramos, metros, segundos, etc. de modo que el compilador pueda asegurarse de que no use accidentalmente kilogramos en el caso de que quisiera decir metros , etc.
¡Espero que esto ayude!
Hay muchas razones, como hacer metaprogramación de plantillas (consulte Boost.MPL). Pero no hay necesidad de ir tan lejos, la std::tuple
C ++ 11 tiene un accesodor std::get<i>
que necesita ser indexado en tiempo de compilación, ya que el resultado depende del índice.
Probablemente tenga razón en este caso, pero hay casos en los que necesita conocer esta información en tiempo de compilación:
Pero ¿qué tal esto?
template <std::size_t N>
std::array<int, N> get_array() { ... }
std::array
necesita conocer su tamaño en el momento de la compilación (ya que está asignado en la pila).
No puedes hacer algo como esto:
std::array<int>(5);