template sirven que poo plantillas plantilla para multiples los c++ templates c++11 variadic-templates template-templates

sirven - template typename t c++



Argumento de plantilla de plantilla variadica C++ que coincide con cualquier tipo de parĂ¡metros (3)

Me preguntaba si es posible escribir una función de plantilla que pueda tomar cualquier otra plantilla arbitraria como parámetro y que coincida adecuadamente con el nombre de la plantilla (es decir, no solo la clase resultante). Lo que sé que funciona es esto:

template<template<typename ...> class TemplateT, typename... TemplateP> void f(const TemplateT<TemplateP...>& param);

Que coincidirá, por ejemplo, para f(std::vector<int>()) o f(std::list<int>()) pero no funcionará para f(std::array<int, 3>()) , ya que el segundo parámetro es un size_t y ningún tipo.

Ahora creo que uno podría hacer algo loco como:

template<template<typename ...> class TemplateT, size... Sizes, typename... TemplateP> void f(const TemplateT<Sizes..., TemplateP...>& param);

Esperando que el compilador derivara correctamente la elipsis de TemplateP o los puntos suspensivos de los Sizes para estar vacía. Pero no solo es feo, también solo funcionará para las plantillas que toman cualquiera de los tipos o parámetros size_t . Todavía no coincidirá con las plantillas arbitrarias, por ejemplo, con los parámetros de bool .

Lo mismo ocurre con un enfoque con sobrecarga:

template<template<typename ...> class TemplateT, typename... TemplateP> void f(const TemplateT<TemplateP...>& param); template<template<typename ...> class TemplateT, size... Sizes> void f(const TemplateT<Sizes...>& param);

Además, dicho enfoque no funcionará si quisiéramos mezclar size_t y typenames . Entonces, lo que se requeriría para que coincida con algo sería algo como esto, donde no hay restricciones en absoluto para lo que está permitido en las elipsis:

template<template<...> class TemplateT, ... Anything> void f(const TemplateT<Anything...>& param);

Esa sintaxis no funciona, pero tal vez haya otra sintaxis para definir algo como esto.

Esto es principalmente lo que me pregunto qué es posible en el lenguaje, pensé que en realidad podría ser útil, si tienes diferentes plantillas donde el primer parámetro siempre es fijo y te gustaría cambiarlo en función del tipo de devolución y guardar todo lo demás . Algo como esto:

template< template<typename ValueT, ...> class TemplateT, ... Anything, typename ValueT, typename ResultT = decltype(some_operation_on_value_t(std::declval<ValueT>())> TemplateT<ResultT, Anything...> f(const TemplateT<ValueT, Anything...>& in);

Entonces, ¿hay alguna manera de hacer que esto funcione de una manera completamente genérica usando la coincidencia de patrones?

Esto no es puramente un experimento mental, ya que el caso de uso para el que estaba estancado fue crear primitivas funcionales puras que operan en contenedores y construirán implícitamente contenedores de resultados inmutables. Si el contenedor de resultados tiene un tipo de datos diferente, necesitamos saber el tipo en que opera el contenedor, por lo que el único requisito en cualquier contenedor sería que el primer parámetro de la plantilla deba ser el tipo de entrada para que pueda ser reemplazado por un diferente tipo de salida en el resultado, pero el código debe ser ajeno a cualquier argumento de plantilla que venga después de eso y no debería importar si es un tipo o un valor.


Debe tener una metafunción que vuelva a enlazar el tipo de contenedor. Porque no puedes simplemente reemplazar el primer parámetro de plantilla:

vector<int, allocator<int> > input; vector<double, allocator<int> > just_replaced; vector<double, allocator<double> > properly_rebound;

Entonces, solo escriba una metafunción para un conjunto conocido de contenedores.

template<class Container, class NewValue> class rebinder; // example for vectors with standard allocator template<class V, class T> class rebinder< std::vector<V>, T > { public: typedef std::vector<T> type; }; // example for lists with arbitrary allocator template<class V, class A, class T> class rebinder< std::list<V,A>, T > { typedef typename A::template rebind<T>::other AT; // rebind the allocator public: typedef std::list<T,AT> type; // rebind the list }; // example for arrays template<class V, size_t N> class rebinder< std::array<V,N>, T > { public: typedef std::array<T,N> type; };

Las reglas de reenlace pueden variar para diferentes contenedores.

Además, es posible que necesite una metafunción que extraiga el tipo de valor de un contenedor arbitrario, no solo conforme a std ( typedef *unspecified* value_type )

template<class Container> class get_value_type { public: typedef typename Container::value_type type; // common implementation }; template<class X> class get_value_type< YourTrickyContainer<X> > { ...... public: typedef YZ type; };


Sería increíble si tuviéramos tal cosa, ya que nos permitiría escribir un rasgo is_same_template en un is_same_template y is_same_template .
Hasta entonces, nos especializamos todo el camino.


Tu construcción interesante tiene dos niveles con plantillas variadas.

  • Una lista de parámetros de plantilla variadic externa TemplateP & Sizes para una plantilla de función
  • Un paquete de parámetros interno como los parámetros de plantilla de su plantilla, parámetro de plantilla TemplateT , una plantilla de clase

Primero, veamos la clase interna de TemplateT : ¿por qué el operador de puntos suspensivos no coincide con algo como TemplateT< int, 2 > ? Bueno, el estándar define plantillas variadic en §14.5.3 como

template<class ... Types> struct Tuple { }; template<T ...Values> struct Tuple2 { };

donde el paquete de argumentos de la plantilla en el primer caso solo puede coincidir con los tipos y en la segunda versión solo los valores de tipo T En particular,

Tuple < 0 > error; // error, 0 is not a type! Tuple < T, 0 > error2; // T is a type, but zero is not! Tuple2< T > error3; // error, T is not a value Tuple2< T, 0 > error4; // error, T is not a value

están todos malformados. Además, no es posible recurrir a algo así como

template<class ... Types, size_t ...Sizes> struct Tuple { };

porque el estándar establece en §14.1.11:

Si un parámetro de plantilla de una plantilla de clase principal o plantilla de alias es un paquete de parámetros de plantilla, será el último parámetro de plantilla. Un paquete de parámetros de plantilla de una plantilla de función no debe ser seguido por otro parámetro de plantilla a menos que ese parámetro de plantilla pueda deducirse de la lista de tipos de parámetros de la plantilla de función o tenga un argumento predeterminado (14.8.2).

En otras palabras, para las plantillas de clase, solo un paquete de parámetros variadic puede aparecer en la definición. Por lo tanto, la definición de clase (doble) -variada anterior está mal formada. Debido a que la clase interna siempre necesita esa combinación, es imposible escribir algo tan general como lo concibió.

¿Qué se puede rescatar? Para la plantilla de función externa, algunos fragmentos se pueden juntar, pero no te gustará. Siempre que el segundo paquete de parámetros se pueda deducir del primero, pueden aparecer dos paquetes de parámetros (en una plantilla de función). Por lo tanto, una función como

template < typename... Args, size_t... N > void g(const std::array< Args, N > &...arr); g(std::array< double, 3 >(), std::array< int, 5>());

está permitido, porque los valores enteros se pueden deducir. Por supuesto, esto debería estar especializado para cada tipo de contenedor y está muy lejos de lo que habías imaginado.