¿Cuáles son las reglas para el token “…” en el contexto de plantillas variadic?

En C ++ 11 hay plantillas variadas como esta:

template unique_ptr make_unique( Args&&... args ) { return unique_ptr(new T(std::forward(args)...)); } 

Hay algunas curiosidades sobre esto: La expresión std::forward(args)... usa tanto Args como args pero solo un ... token. Además, std::forward es una función de plantilla no variadica que toma solo un parámetro de plantilla y un argumento. ¿Cuáles son las reglas de syntax para eso (más o menos)? ¿Cómo se puede generalizar?

Además: en la implementación de la función, la elipsis ( ... ) está al final de la expresión de interés. ¿Hay alguna razón por la que en la lista de argumentos de la plantilla y en la lista de parámetros, la elipsis esté en el medio?

En el contexto de la plantilla variadic, la elipsis ... se usa para descomprimir el paquete de parámetros de la plantilla si aparece en el lado derecho de una expresión (llame a este patrón de expresión por un momento). La regla es que cualquier patrón que esté en el lado izquierdo de ... se repite: los patrones desempaquetados (ahora se llaman expresiones ) están separados por comas,.

Se puede entender mejor con algunos ejemplos. Supongamos que tiene esta plantilla de función:

 template void f(T ... args) { g( args... ); //pattern = args h( x(args)... ); //pattern = x(args) m( y(args...) ); //pattern = args (as argument to y()) n( z(args)... ); //pattern = z(args) } 

Ahora si llamo a esta función que pasa T como {int, char, short} , cada una de las llamadas de funciones se expande como:

 g( arg0, arg1, arg2 ); h( x(arg0), x(arg1), x(arg2) ); m( y(arg0, arg1, arg2) ); n( z(arg0), z(arg1), z(arg2) ); 

En el código que publicó, std::forward sigue el cuarto patrón ilustrado por la llamada a la función n() .

¡Observe la diferencia entre x(args)... y y(args...) arriba!


Puede usar ... para inicializar una matriz también como:

 struct data_info { boost::any data; std::size_t type_size; }; std::vector v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)} 

que se expande a esto:

 std::vector v { {arg0, sizeof(int)}, {arg1, sizeof(char)}, {arg2, sizeof(short)} }; 

Me acabo de dar cuenta de que un patrón podría incluir especificador de acceso como public , como se muestra en el siguiente ejemplo:

 template struct mixture : public Mixins ... //pattern = public Mixins { //code }; 

En este ejemplo, el patrón se expande como:

 struct mixture__instantiated : public Mixin0, public Mixin1, .. public MixinN 

Es decir, la mixture deriva públicamente de todas las clases base.

Espero que ayude.

Lo siguiente está tomado de la charla “Variadic Templates are Funadic” de Andrei Alexandrescu en GoingNative 2012. Lo puedo recomendar para una buena introducción sobre plantillas variadic.


Hay dos cosas que uno puede hacer con un paquete variadic. Es posible aplicar sizeof...(vs) para obtener el número de elementos y expandirlo.

Reglas de expansión

 Use Expansion Ts... T1, ..., Tn Ts&&... T1&&, ..., Tn&& x::z... x::z, ..., x::z x... x, ..., x func(5,vs)... func(5,v1), ..., func(5,vn) 

La expansión procede hacia adentro y hacia afuera. Al expandir dos listas en lock-step, deben tener el mismo tamaño.

Más ejemplos:

 gun(A::hun(vs)...); 

Expande todos los Ts en la lista de argumentos de la plantilla de A y luego la función hun se expande con todos los vs

 gun(A::hun(vs...)); 

Expande todos los Ts en la lista de argumentos de la plantilla de A y todos los vs como los argumentos de la función para hun .

 gun(A::hun(vs)...); 

Expande la función hun con Ts y vs en lock-step.

Nota:

Ts no es un tipo y vs no es un valor! Son alias para una lista de tipos / valores. Cualquiera de las listas puede estar potencialmente vacía. Ambos obedecen solo acciones específicas. Entonces lo siguiente no es posible:

 typedef Ts MyList; // error! Ts var; // error! auto copy = vs; // error! 

Loci de expansión

Argumentos de función

 template  void fun(Ts... vs) 

Listas de inicializadores

 any a[] = { vs... }; 

Especificadores de base

 template  struct C : Ts... {}; template  struct D : Box... { /**/ }; 

Listas de inicialización de miembros

 // Inside struct D template  D(Us... vs) : Box(vs)... {} 

Listas de argumentos de Tempate

 std::map m; 

Solo comstackrá si hay una posible coincidencia para los argumentos.

Listas de captura

 template  void fun(Ts... vs) { auto g = [&vs...] { return gun(vs...); } g(); } 

Listas de atributos

 struct [[ Ts... ]] IAmFromTheFuture {}; 

Está en la especificación, pero aún no hay ningún atributo que se pueda express como un tipo.