c++ - plantillas - cómo declarar correctamente el tipo de función que toma la plantilla como parámetro(como una función std::)
para que sirven los templates c++ (4)
"Por qué necesito dar a la plantilla una forma general vacía con un parámetro de tipo vacío"
Porque, quiere una diferenciación explícita de tipo entre el "tipo de retorno" y los "tipos de argumento", con la lucidez similar del azúcar sintáctico. En C ++, no hay tal sintaxis disponible para la versión de primera mano de la template class
. Debe tener que especializarse, para poder distinguirlo.
Después de leer su Q, miré el archivo de encabezado <functional>
. Incluso std::function
hace el mismo truco :
// <functional> (header file taken from g++ Ubuntu)
template<typename _Signature>
class function;
// ...
/**
* @brief Primary class template for std::function.
* @ingroup functors
*
* Polymorphic function wrapper.
*/
template<typename _Res, typename... _ArgTypes>
class function<_Res(_ArgTypes...)>
Como puede ver en sus comentarios, tratan la versión especializada como la clase "Primaria". ¡Por lo tanto, este truco proviene del encabezado estándar en sí!
Descubrí que no es trivial tener una sintaxis ordenada como en:
std::function<int(float, bool)>
Si declaro la función como:
template <class RetType, class... Args>
class function {};
Sería una sintaxis ordinaria para definir los tipos de plantilla de la función:
function<int,float,bool> f;
Pero funciona con un truco extraño con la especialización de plantilla parcial
template <class> class function; // #1
template <class RV, class... Args>
class function<RV(Args...)> {} // #2
¿Porqué es eso? ¿Por qué tengo que dar a la plantilla una forma general vacía con el parámetro de tipo vacío (n. ° 1) o de lo contrario simplemente no compilará
Debes tener en cuenta que int (float, bool)
es un tipo . Más precisamente, es el tipo "función que toma dos parámetros de tipo float
y bool
, y devuelve int
".
Como es un tipo, es obvio que la plantilla debe tener un parámetro de tipo en el lugar donde desea usar la sintaxis int (float, bool)
.
Al mismo tiempo, tener solo el tipo de función es difícil de manejar. Claro, podrías hacerlo fácilmente. Por ejemplo, si todo lo que necesita hacer es un tipo de agente de reenvío:
template <class T>
struct CallForwarder
{
std::function<T> forward(std::function<T> f)
{
std::cout << "Forwarding!/n";
return f;
}
};
Sin embargo, tan pronto como desee acceder a los "componentes" del tipo de función, necesita una forma de introducir identificadores para ellos. La forma más natural de hacerlo es la especialización parcial para tipos de funciones, tal como lo hizo en su pregunta (y al igual que std::function
).
Esta es solo la forma en que se diseñó el lenguaje. Las plantillas primarias no pueden hacer una descomposición compleja de tipos como esa; necesitas usar especialización parcial.
Si entiendo correctamente, le gustaría simplemente escribir la segunda versión sin tener que proporcionar la plantilla principal. Pero piense en cómo los argumentos de la plantilla se correlacionan con los parámetros de la plantilla:
template <class RV, class Arg1, class... Args>
class function<RV(Arg1, Args...)> {}
function<int(float,bool)>; //1
function<int, float, bool>; //2
La opción 1
es lo que desea escribir, pero tenga en cuenta que pasa un tipo de función única a una plantilla donde los parámetros son dos parámetros de tipo y un paquete de parámetros de tipo. En otras palabras, escribir esto sin una plantilla primaria significa que los argumentos de la plantilla no necesariamente coincidirán con los parámetros de la plantilla. La opción 2
coincide con los parámetros de la plantilla, pero no coincide con la especialización.
Esto tiene aún menos sentido si tienes más de una especialización:
template <class RV, class Arg1, class... Args>
class function<RV(Arg1, Args...)> {}
template <class T, class RV, class Arg1, class... Args>
class function<RV (T::*) (Arg1, Args...)> {}
Tal vez puedas pensar algunas reglas para inferir la plantilla principal de las especializaciones, pero eso me parece bastante horrible.
Usted puede hacer esto:
template <typename T>
class X { };
X<int(char)> x;
Y dentro de la definición de X
podrías crear una std::function<T>
ya que std::function<int(char)>
una std::function<int(char)>
. El problema aquí es que no se puede (al menos fácilmente) acceder al tipo de devolución y al tipo de los parámetros del argumento ( int
y char
aquí).
Usando el "truco", puede acceder a estos sin problemas: "simplemente" hace que su código sea más limpio.