Wie funktioniert generisches Lambda in C ++ 14?

113

Wie funktioniert generisches Lambda ( autoSchlüsselwort als Argumenttyp) im C ++ 14-Standard?

Basiert es auf C ++ - Vorlagen, bei denen der Compiler für jeden unterschiedlichen Argumenttyp eine neue Funktion mit demselben Text generiert, aber Typen ersetzt (Polymorphismus zur Kompilierungszeit), oder ähnelt er eher den Java-Generika (Typlöschung)?

Codebeispiel:

auto glambda = [](auto a) { return a; };
sasha.sochka
quelle
6
Behoben zu C ++ 14, ursprünglich verwendet C ++ 11 in Frage
sasha.sochka

Antworten:

130

Generische Lambdas wurden in eingeführt C++14.

Der durch den Lambda-Ausdruck definierte Schließungstyp hat einfach einen Aufrufoperator mit Vorlagen anstelle des regulären C++11Anrufoperators ohne Vorlage von Lambdas (natürlich, wenn er automindestens einmal in der Parameterliste erscheint).

Also dein Beispiel:

auto glambda = [] (auto a) { return a; };

Wird glambdaeine Instanz dieses Typs erstellen:

class /* unnamed */
{
public:
    template<typename T>
    T operator () (T a) const { return a; }
};

In Abschnitt 5.1.2 / 5 des C ++ 14 Standard Draft n3690 wird festgelegt, wie der Aufrufoperator des Schließungstyps eines bestimmten Lambda-Ausdrucks definiert wird:

Der Schließungstyp für einen nicht generischen Lambda-Ausdruck verfügt über einen öffentlichen Inline-Funktionsaufrufoperator (13.5.4), dessen Parameter und Rückgabetyp durch die Parameterdeklarationsklausel und den nachfolgenden Rückgabetyp des Lambda-Ausdrucks beschrieben werden. Für ein generisches Lambda verfügt der Closure-Typ über eine öffentliche Inline-Funktionsaufruf-Operator-Member-Vorlage (14.5.2), deren Template-Parameter-Liste aus einem erfundenen Type-Template-Parameter für jedes Auftreten von Auto in der Parameterdeklarationsklausel des Lambda besteht. in der Reihenfolge der Erscheinung. Der erfundene Typ template-parameter ist ein Parameterpaket, wenn die entsprechende Parameterdeklaration ein Funktionsparameterpaket deklariert (8.3.5). Der Rückgabetyp und die Funktionsparameter der Funktionsaufrufoperatorvorlage werden aus dem Trailing-Return-Typ und der Parameterdeklarationsklausel des Lambda-Ausdrucks abgeleitet, indem jedes Vorkommen von auto in den Deklarationsspezifizierern der Parameterdeklarationsklausel durch den Namen von ersetzt wird der entsprechende erfundene Template-Parameter.

Schließlich:

Ist es ähnlich wie Vorlagen, bei denen der Compiler für jeden unterschiedlichen Argumenttyp Funktionen mit demselben Text, aber geänderten Typen generiert, oder ähnelt es eher den Generika von Java?

Wie im obigen Absatz erläutert, sind generische Lambdas nur syntaktischer Zucker für einzigartige, unbenannte Funktoren mit einem Vorlagenbetreiber. Das sollte deine Frage beantworten :)

Andy Prowl
quelle
7
Sie erlauben jedoch auch eine lokal definierte Klasse mit einer Vorlagenmethode. Welches ist neu.
Yakk - Adam Nevraumont
2
@Yakk: Wurde die Einschränkung für funktionslokale Vorlagen nicht bereits mit C ++ 11 vollständig aufgehoben?
Sebastian Mach
2
@phresnel: Nein, diese Einschränkung wurde nicht aufgehoben
Andy Prowl
1
@ AndyProwl: Ich erkenne meinen Fehler. Was tatsächlich aufgehoben wurde, war die Verwendung lokaler Typen als Vorlagenargumente (wie in int main () { struct X {}; std::vector<X> x; })
Sebastian Mach
1
@phresnel: Richtig, das hat sich tatsächlich geändert
Andy Prowl
25

Leider sind sie nicht Teil von C ++ 11 ( http://ideone.com/NsqYuq ):

auto glambda = [](auto a) { return a; };

int main() {}

Mit g ++ 4.7:

prog.cpp:1:24: error: parameter declared auto
...

Die Art und Weise, wie es in C ++ 14 gemäß dem Portland-Vorschlag für generische Lambdas implementiert werden könnte :

[](const& x, & y){ return x + y; }

Dies würde zum größten Teil die übliche Erstellung einer anonymen Funktorklasse ergeben, aber mit dem Mangel an Typen würde der Compiler ein Mitglied mit Vorlagen ausgeben operator():

struct anonymous
{
    template <typename T, typename U>
    auto operator()(T const& x, U& y) const -> decltype(x+y)
    { return x + y; }
};

Oder gemäß dem neueren Vorschlag Vorschlag für generische (polymorphe) Lambda-Ausdrücke

auto L = [](const auto& x, auto& y){ return x + y; };

--->

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;

Ja, für jede Permutation von Parametern würde eine neue Instanziierung entstehen, die Mitglieder dieses Funktors würden jedoch weiterhin geteilt (dh die erfassten Argumente).

Sebastian Mach
quelle
6
Dieser Vorschlag, das Löschen des Typspezifizierers zuzulassen, ist äußerst grotesk.
Leichtigkeitsrennen im Orbit
Sie gingen mit g ++ - 4.9 ein . Sie müssen liefern -std=c++1y.
Emsr
Ich wusste nicht, dass ideone noch nicht über gcc-4.9 und C ++ 14 verfügt.
Emsr
Frage: Hat dies autodie gleichen Abzugsregeln wie das klassische Auto? Wenn wir uns auf die Vorlagenanalogie beziehen, würde dies bedeuten, dass das Auto nicht automatisch ist, sondern dieselben Regeln wie der Abzug des Vorlagentyps. Dann lautet die Frage: Ist der Vorlagenabzug gleichbedeutend mit auto?
v.oddou
@ v.oddou: "Classic Auto" ist gut. Für mich bedeutet "klassisches Auto" "Stapelvariable" und wurde einmal im Gegensatz zu staticoder verwendet. registerWie auch immer, ja, wenn Sie dort verwenden auto, wird unter der Haube eine normale Vorlage generiert. Tatsächlich wird ein Lambda compilerintern durch eine Funktorklasse ersetzt, und ein autoParameter bedeutet, dass template <T> ... (T ...)ausgegeben wird.
Sebastian Mach
17

Es ist eine vorgeschlagene C ++ 14-Funktion (nicht in C ++ 11), die Vorlagen ähnelt (oder sogar gleichwertig ist). Zum Beispiel bietet N3559 dieses Beispiel:

Zum Beispiel enthält dieser generische Lambda-Ausdruck die folgende Anweisung:

auto L = [](const auto& x, auto& y){ return x + y; };

Dies kann zur Erstellung eines Schließungstyps und eines Objekts führen, das sich ähnlich wie die folgende Struktur verhält:

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;
Cassio Neri
quelle