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


98

En C ++ 11 hay plantillas variadas como esta:

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

Hay algunas curiosidades sobre esto: la expresión std::forward<Args>(args)...usa ambos Argsy, argspero solo una ...ficha. Además, std::forwardes una función de plantilla no variada que toma solo un parámetro de plantilla y un argumento. ¿Cuáles son las reglas de sintaxis para eso (aproximadamente)? ¿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?


2
Brevemente sobre la segunda parte: al "declarar" un paquete de parámetros de plantilla o un paquete de parámetros de función, el ...viene antes del identificador que se introduce. Al usar uno o ambos tipos de paquetes, el ...viene después del patrón de expresión para expandirse.
— aschepler

Respuestas:


99

En el contexto de la plantilla variadica, 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 esta expresión patrón por un momento). La regla es que cualquier patrón que esté en el lado izquierdo de ...se repite; los patrones desempaquetados (llámelos expresiones ahora) están separados por comas ,.

Puede entenderse mejor con algunos ejemplos. Suponga que tiene esta plantilla de función:

template<typename ...T>
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<T>(args)... );  //pattern = z<T>(args)
}

Ahora, si llamo a esta función pasando Tcomo {int, char, short}, entonces cada llamada de función se expande como:

g( arg0, arg1, arg2 );           
h( x(arg0), x(arg1), x(arg2) );
m( y(arg0, arg1, arg2) );
n( z<int>(arg0), z<char>(arg1), z<short>(arg2) );

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

¡Note 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<data_info> v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)}

que se expande a esto:

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

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

template<typename ... Mixins>
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, mixturederiva públicamente de todas las clases base.

Espero que ayude.


1
Respecto a la expresión que se corresponde; Debería ser la expresión más grande posible , ¿no es así? Por ejemplo, x+args...debería ampliarse a x+arg0,x+arg1,x+arg2, no x+arg0,arg1,arg2.
— máscara de bits

Por tanto, se ...aplica a todas las entidades expandibles del patrón.
— Lightness Races in Orbit

@bitmask: Sí. x+args...debería expandirse a x+arg0,x+arg1,x+arg2, no x+arg0,arg1,arg2 .
— Nawaz

1
@ Jarod42: Actualizaré esta respuesta una vez que se lance C ++ 17.
— Nawaz

3
@synther: sizeof...(T)no se necesita allí. Simplemente puede escribir:int a[] = { ___ };
— Nawaz

48

Lo siguiente fue tomado de la charla "Las plantillas variadas son Funadic" por Andrei Alexandrescu en GoingNative 2012. Puedo recomendarlo para una buena introducción a las plantillas variadas.


Hay dos cosas que se pueden 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<Ts,Y>::z...  x<T1,Y>::z, ..., x<Tn,Y>::z
x<Ts&,Us>...   x<T1&,U1>, ..., x<Tn&,Un>
func(5,vs)...  func(5,v1), ..., func(5,vn)

La expansión procede hacia adentro hacia afuera. Al expandir dos listas en el paso de bloqueo, deben tener el mismo tamaño.

Más ejemplos:

gun(A<Ts...>::hun(vs)...);

Expande todo Tsen la lista de argumentos de la plantilla de Ay luego la función hunse expande con todo vs.

gun(A<Ts...>::hun(vs...));

Expande todo Tsen la lista de argumentos de plantilla de Ay todos vscomo argumentos de función para hun.

gun(A<Ts>::hun(vs)...);

Expande la función huncon Tsy vsen paso de bloqueo.

Nota:

Tsno es un tipo y vsno es un valor. Son alias para una lista de tipos / valores. Cualquiera de las dos listas puede estar potencialmente vacía. Ambos obedecen solo a 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 <typename... Ts>
void fun(Ts... vs)

Listas de inicializadores

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

Especificadores de base

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

Listas de inicializadores de miembros

// Inside struct D
template <typename... Us>
D(Us... vs) : Box<Ts>(vs)... {}

Listas de argumentos temáticos

std::map<Ts...> m;

Solo se compilará si hay una posible coincidencia para los argumentos.

Capturar listas

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

Listas de atributos

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

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


Agradable. Sin embargo, los argumentos de función se dejan fuera de los loci: P
— Potatoswatter

@Potatoswatter Gracias. No se mencionó la expansión en la lista de argumentos de la función.
— typ1232

@ typ1232 Lo siento por la edición, pero sentí que tu publicación original estaba demasiado cerca del plagio. Por cierto, también vi esa charla hace algún tiempo, increíble.
— Walter
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.