Quais são as regras para o token “…” no contexto de modelos variadic?


98

No C ++ 11, existem modelos variados como este:

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

Existem algumas curiosidades sobre isso: A expressão std::forward<Args>(args)...usa ambos Argse, argsmas apenas um ...token. Além disso, std::forwardé uma função de modelo não variável que usa apenas um parâmetro de modelo e um argumento. Quais são as regras de sintaxe para isso (aproximadamente)? Como pode ser generalizado?

Além disso: Na implementação da função, as reticências ( ...) estão no final da expressão de interesse. Existe uma razão para que na lista de argumentos do modelo e na lista de parâmetros as reticências estejam no meio?


2
Resumidamente na segunda parte: Ao "declarar" um pacote de parâmetros de modelo ou pacote de parâmetros de função, o ...vem antes do identificador ser introduzido. Ao usar um ou ambos os tipos de pacotes, o ...vem após o padrão de expressão para expandir.
aschepler

Respostas:


99

No contexto do modelo variadic, as reticências ...são usadas para descompactar o pacote de parâmetros do modelo se ele aparecer no lado direito de uma expressão (chame este padrão de expressão por um momento). A regra é que qualquer padrão que esteja no lado esquerdo de ...é repetido - os padrões descompactados ( agora chame-os de expressões ) são separados por vírgula ,.

Pode ser melhor compreendido por alguns exemplos. Suponha que você tenha este modelo de função:

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)
}

Agora, se eu chamar essa função passando Tcomo {int, char, short}, cada uma das chamadas de função será expandida 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) );

No código que você postou, std::forwardsegue o quarto padrão ilustrado pela n()chamada de função.

Observe a diferença entre x(args)...e y(args...)acima!


Você também pode usar ...para inicializar uma matriz 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 é expandido para este:

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

Acabei de perceber que um padrão pode até incluir especificador de acesso public, como mostrado no exemplo a seguir:

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

Neste exemplo, o padrão é expandido como:

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

Ou seja, mixturederiva publicamente de todas as classes base.

Espero que ajude.


1
Em relação à expressão que corresponde; Deve ser a maior expressão possível , não é? Por exemplo, x+args...deve ser expandido para x+arg0,x+arg1,x+arg2, não x+arg0,arg1,arg2.
bitmask de

Portanto, o ...aplica-se a todas as entidades expansíveis no padrão.
Leveza raças em Orbit

@bitmask: Sim. x+args...deve expandir para x+arg0,x+arg1,x+arg2, não x+arg0,arg1,arg2 .
Nawaz

1
@ Jarod42: Atualizarei esta resposta assim que o C ++ 17 for lançado.
Nawaz

3
@synther: sizeof...(T)não é necessário lá. Você pode simplesmente escrever:int a[] = { ___ };
Nawaz

48

O que se segue foi retirado da palestra "Variadic Templates are Funadic" por Andrei Alexandrescu em GoingNative 2012. Posso recomendá-lo para uma boa introdução sobre modelos variadic.


Existem duas coisas que podem ser feitas com um pacote variadic. É possível aplicar sizeof...(vs)para obter o número de elementos e expandi-lo.

Regras de expansão

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)

A expansão prossegue de dentro para fora. Ao expandir duas listas em etapa de bloqueio, elas devem ter o mesmo tamanho.

Mais exemplos:

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

Expande tudo Tsna lista de argumentos do modelo de Ae, em seguida, a função huné expandida com todos vs.

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

Expande tudo Tsna lista de argumentos do modelo de Ae todos vscomo os argumentos da função para hun.

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

Expande a função huncom Tse vsem etapas.

Nota:

Tsnão é um tipo e vsnão é um valor! Eles são apelidos para uma lista de tipos / valores. Qualquer uma das listas pode estar potencialmente vazia. Ambos obedecem apenas a ações específicas. Portanto, o seguinte não é possível:

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

Loci de expansão

Argumentos de função

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 membros

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

Listas de argumentos temporárias

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

Só será compilado se houver uma correspondência possível para os argumentos.

Listas de captura

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

Listas de atributos

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

Está na especificação, mas ainda não há nenhum atributo que possa ser expresso como um tipo.


Agradável. Os argumentos da função são deixados de fora dos locais, no entanto: P
Potatoswatter

Obrigado @Potatoswatter. A expansão na lista de argumentos da função não foi mencionada.
typ1232

@ typ1232 Desculpe pela edição, mas achei que sua postagem original estava perto demais do plágio. Aliás, eu também assisti aquela palestra há algum tempo - incrível.
Walter
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.