Welche Regeln gelten für das Token „…“ im Zusammenhang mit variablen Vorlagen?

98

In C ++ 11 gibt es verschiedene Vorlagen wie diese:

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

Hier gibt es einige Kuriositäten: Der Ausdruck std::forward<Args>(args)...verwendet beide Argsund argsnur einen ...Token. Darüber hinaus std::forwardist eine nicht variadische Vorlagenfunktion nur ein Vorlagenparameter und ein Argument. Was sind die Syntaxregeln dafür (ungefähr)? Wie kann es verallgemeinert werden?

Außerdem: In der Funktionsimplementierung steht die Ellipse ( ...) am Ende der Interessenbekundung. Gibt es einen Grund dafür, dass in der Liste der Vorlagenargumente und in der Parameterliste die Auslassungspunkte in der Mitte stehen?

Ralph Tandetzky
quelle
2
Kurz zum zweiten Teil: Wenn ein Vorlagenparameterpaket oder ein Funktionsparameterpaket "deklariert" wird, ...kommt das vor der Einführung des Bezeichners. Wenn Sie eine oder beide Arten von Paketen verwenden, wird ...das Ausdrucksmuster nach dem Ausdruck erweitert.
Aschepler

Antworten:

99

Im Zusammenhang mit der variadische schablonen die Auslassungspunkte ...verwendet , um die Template - Parameter Packung auszupacken , wenn er auf der rechten Seite des Ausdrucks angezeigt (nennen diesen Ausdruck Muster für einen Moment). Die Regel ist, dass jedes Muster auf der linken Seite ...wiederholt wird - die entpackten Muster (nennen sie jetzt Ausdrücke ) werden durch Komma getrennt ,.

Dies lässt sich am besten anhand einiger Beispiele verstehen. Angenommen, Sie haben diese Funktionsvorlage:

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

Wenn ich diese Funktion Tals Übergabe aufrufe {int, char, short}, wird jeder Funktionsaufruf wie folgt erweitert:

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

In dem von Ihnen geposteten Code std::forwardfolgt das vierte Muster, das durch einen n()Funktionsaufruf dargestellt wird.

Beachten Sie den Unterschied zwischen x(args)...und y(args...)über!


Sie können ...ein Array auch folgendermaßen initialisieren:

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

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

was dazu erweitert wird:

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

Ich habe gerade festgestellt, dass ein Muster sogar Zugriffsspezifizierer enthalten kann public, wie im folgenden Beispiel gezeigt:

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

In diesem Beispiel wird das Muster wie folgt erweitert:

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

Das heißt, mixtureleitet sich öffentlich von allen Basisklassen ab.

Hoffentlich hilft das.

Nawaz
quelle
1
In Bezug auf den Ausdruck, der übereinstimmt; Es sollte der größtmögliche Ausdruck sein, nicht wahr? Zum Beispiel x+args...sollte erweitert werden x+arg0,x+arg1,x+arg2, nicht x+arg0,arg1,arg2.
Bitmaske
Das ...gilt also für jede erweiterbare Entität im Muster.
Leichtigkeitsrennen im Orbit
@bitmask: Ja. x+args...sollte erweitern x+arg0,x+arg1,x+arg2, nicht x+arg0,arg1,arg2 .
Nawaz
1
@ Jarod42: Ich werde diese Antwort aktualisieren, sobald C ++ 17 veröffentlicht ist.
Nawaz
3
@synther: sizeof...(T)wird dort nicht benötigt. Sie können einfach schreiben:int a[] = { ___ };
Nawaz
48

Das Folgende stammt aus dem Vortrag "Variadic Templates are Funadic" von Andrei Alexandrescu auf der GoingNative 2012. Ich kann es für eine gute Einführung in Variadic Templates empfehlen.


Es gibt zwei Dinge, die man mit einem variadic Pack machen kann. Es ist möglich sizeof...(vs), die Anzahl der Elemente zu ermitteln und zu erweitern.

Erweiterungsregeln

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)

Die Expansion erfolgt nach innen nach außen. Wenn Sie zwei Listen im Sperrschritt erweitern, müssen sie dieselbe Größe haben.

Mehr Beispiele:

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

Erweitert alle Tsin der Vorlagenargumentliste von Aund dann wird die Funktion hunmit allen erweitert vs.

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

Erweitert alle Tsin der Vorlagenargumentliste von Aund alle vsals Funktionsargumente für hun.

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

Erweitert die Funktion hunmit Tsund vsim Sperrschritt.

Hinweis:

Tsist kein Typ und vskein Wert! Sie sind Aliase für eine Liste von Typen / Werten. Jede Liste ist möglicherweise leer. Beide gehorchen nur bestimmten Handlungen. Folgendes ist also nicht möglich:

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

Erweiterungsorte

Funktionsargumente

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

Initialisierungslisten

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

Basisspezifizierer

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

Mitgliederinitialisierungslisten

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

Tempate Argumentlisten

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

Wird nur kompiliert, wenn eine mögliche Übereinstimmung für die Argumente vorliegt.

Erfassungslisten

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

Attributlisten

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

Es ist in der Spezifikation enthalten, aber es gibt noch kein Attribut, das als Typ ausgedrückt werden kann.

typ1232
quelle
Nett. Funktionsargumente werden jedoch aus den Loci herausgelassen: P
Potatoswatter
@ Potatoswatter Danke. Die Erweiterung in der Funktionsargumentliste wurde nicht erwähnt.
typ1232
@ typ1232 Entschuldigung für die Bearbeitung, aber ich hatte das Gefühl, dass Ihr ursprünglicher Beitrag dem Plagiat zu nahe kam. Übrigens habe ich dieses Gespräch auch vor einiger Zeit gesehen - großartig.
Walter