Quali sono le regole per il token "..." nel contesto dei modelli variadici?


98

In C ++ 11 ci sono modelli variadici come questo:

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

Ci sono alcune curiosità su questo: L'espressione std::forward<Args>(args)...usa entrambi Argse argsma solo un ...token. Inoltre std::forwardè una funzione template non variadica che accetta solo un parametro template e un argomento. Quali sono le regole di sintassi per questo (approssimativamente)? Come può essere generalizzato?

Inoltre: nell'implementazione della funzione i puntini di sospensione ( ...) sono alla fine dell'espressione di interesse. C'è una ragione per cui nell'elenco degli argomenti del modello e nell'elenco dei parametri i puntini di sospensione si trovano al centro?


2
Brevemente sulla seconda parte: quando si "dichiara" un pacchetto di parametri modello o un pacchetto di parametri di funzione, ...viene prima dell'identificatore introdotto. Quando si utilizzano uno o entrambi i tipi di pacchetti, ...viene visualizzato dopo il modello di espressione da espandere.
aschepler

Risposte:


99

Nel contesto del modello variadico, i puntini di sospensione ...vengono utilizzati per decomprimere il pacchetto di parametri del modello se appare sul lato destro di un'espressione (chiamare questo modello di espressione per un momento). La regola è che qualsiasi modello si trova sul lato sinistro di ...viene ripetuto: i modelli decompressi (chiamali ora espressioni ) sono separati da virgola ,.

Può essere meglio compreso con alcuni esempi. Supponi di avere questo modello di funzione:

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

Ora, se chiamo questa funzione passando Tcome {int, char, short}, ciascuna chiamata di funzione viene espansa come:

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

Nel codice che hai pubblicato, std::forwardsegue il quarto schema illustrato dalla n()chiamata di funzione.

Nota la differenza tra x(args)...e y(args...)sopra!


È possibile utilizzare ...per inizializzare un array anche come:

struct data_info
{
     boost::any  data;
     std::size_t type_size;
};

std::vector<data_info> v{{args, sizeof(T)}...}; //pattern = {args, sizeof(T)}

che è ampliato a questo:

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

Mi sono appena reso conto che un pattern potrebbe anche includere identificatori di accesso come public, come mostrato nell'esempio seguente:

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

In questo esempio, il modello viene espanso come:

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

Cioè, mixturederiva pubblicamente da tutte le classi di base.

Spero che aiuti.


1
Per quanto riguarda l'espressione che viene abbinata; Dovrebbe essere la più ampia espressione possibile , no? Ad esempio, x+args...dovrebbe essere esteso a x+arg0,x+arg1,x+arg2, not x+arg0,arg1,arg2.
maschera di bit

Quindi si ...applica a ogni entità espandibile nel pattern.
Gare di leggerezza in orbita

@bitmask: Sì. x+args...dovrebbe espandersi in x+arg0,x+arg1,x+arg2, non x+arg0,arg1,arg2 .
Nawaz

1
@ Jarod42: aggiornerò questa risposta una volta rilasciato C ++ 17.
Nawaz,

3
@synther: sizeof...(T)non è necessario lì. Puoi semplicemente scrivere:int a[] = { ___ };
Nawaz

48

Quanto segue è tratto dal discorso "I modelli variadici sono funadici" di Andrei Alexandrescu a GoingNative 2012. Posso consigliarlo per una buona introduzione sui modelli variadici.


Ci sono due cose che si possono fare con un pacchetto variadico. È possibile applicare sizeof...(vs)per ottenere il numero di elementi ed espanderlo.

Regole di espansione

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)

L'espansione procede verso l'interno verso l'esterno. Quando si espandono due elenchi in blocco, devono avere la stessa dimensione.

Altri esempi:

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

Espande tutto Tsnell'elenco degli argomenti del modello Ae quindi la funzione hunviene espansa con tutti vs.

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

Espande tutto Tsnell'elenco degli argomenti del modello Ae tutti vscome argomenti della funzione per hun.

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

Espande la funzione huncon Tse vsin blocco.

Nota:

Tsnon è un tipo e vsnon è un valore! Sono alias per un elenco di tipi / valori. Entrambi gli elenchi potrebbero essere potenzialmente vuoti. Entrambi obbediscono solo ad azioni specifiche. Quindi non è possibile quanto segue:

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

Luoghi di espansione

Argomenti della funzione

template <typename... Ts>
void fun(Ts... vs)

Elenchi di inizializzatori

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

Specificatori di base

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

Elenchi di inizializzatori membri

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

Elenchi di argomenti temporanei

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

Verrà compilato solo se esiste una possibile corrispondenza per gli argomenti.

Cattura elenchi

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

Elenchi di attributi

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

È nella specifica, ma non esiste ancora alcun attributo che possa essere espresso come tipo.


Bello. Tuttavia, gli argomenti delle funzioni sono esclusi dai loci: P
Potatoswatter

@ Potatoswatter Grazie. L'espansione nell'elenco degli argomenti della funzione non è stata menzionata.
typ1232

@ typ1232 Scusa per la modifica, ma ho sentito che il tuo post originale era troppo vicino al plagio. A proposito, ho anche guardato quel discorso qualche tempo fa - fantastico.
Walter
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.