Ist das Captureless Lambda nach dem Standard garantiert leer?

12

Ich suche nach einer Möglichkeit, leere (Captureless) Lambdas von anderen Lambdas in einer Vorlagenfunktion zu identifizieren. Ich verwende derzeit C ++ 17, bin aber auch neugierig auf C ++ 20-Antworten.

Mein Code sieht folgendermaßen aus:

template<typename T>
auto func(T lambda) {
    // The aguments of the lambdas are unknown

    if constexpr (/* is captureless */) {
        // do stuff
    }
}

Wird durch den C ++ - Standard (17 oder 20) garantiert, dass ein nicht erfassbares Lambda, das in einen Funktionszeiger konvertierbar ist, auch die std::is_emptyAusbeute wahr macht?

Nehmen Sie diesen Code als Beispiel:

auto a = []{}; // captureless
auto b = [c = 'z']{}; // has captures

static_assert(sizeof(a) == sizeof(b)); // Both are the same size
static_assert(!std::is_empty_v<decltype(b)>); // It has a `c` member
static_assert(std::is_empty_v<decltype(a)>); // Passes. It is guaranteed?

Live Beispiel

Guillaume Racicot
quelle
2
Wenn Sie sich nur für Lambdas ohne Vorlage interessieren, können Sie mit SFINAE prüfen, ob die Konvertierung in einen Funktionszeiger ( +lambda) korrekt ist.
HolyBlackCat
@HolyBlackCat Ich habe darüber nachgedacht, aber soweit ich mich erinnere, lässt MSVC das nicht zu, da sie den Konvertierungsoperator überlastet haben.
Guillaume Racicot
@GuillaumeRacicot MS stellt einen separaten Konvertierungsoperator für alle verfügbaren Anrufkonventionen bereit. Wählen Sie einfach einen aus und versuchen Sie, das Lambda in einen vergleichbaren Funktionszeiger umzuwandeln, und prüfen Sie, ob dies erfolgreich ist oder fehlschlägt.
Remy Lebeau
+scheint zu funktionieren hier .
HolyBlackCat

Antworten:

13

Nein, tatsächlich gewährt der Standard Lambdas ausdrücklich die Erlaubnis, eine Größe zu haben, die nicht mit ihrer Deklaration übereinstimmt. [expr.prim.lambda.closure] / 2 Zustände

Der Schließungstyp wird im kleinsten Blockbereich, Klassenbereich oder Namespace-Bereich deklariert, der den entsprechenden Lambda-Ausdruck enthält. [Hinweis: Hiermit wird die Anzahl der Namespaces und Klassen festgelegt, die dem Schließungstyp zugeordnet sind ([basic.lookup.argdep]). Die Parametertypen eines Lambda-Deklarators wirken sich nicht auf diese zugeordneten Namespaces und Klassen aus. - Endnote] Der Verschlusstyp ist kein Aggregattyp. Eine Implementierung kann den Schließungstyp anders definieren als nachstehend beschrieben, vorausgesetzt, dies ändert das beobachtbare Verhalten des Programms nur durch Ändern von:

  • die Größe und / oder Ausrichtung des Verschlusstyps,

  • ob der Verschlusstyp trivial kopierbar ist ([class.prop]) oder (2.3)

  • Gibt an, ob der Verschlusstyp eine Standardlayoutklasse ist ([class.prop]).

Eine Implementierung darf keine Elemente des Referenztyps rvalue zum Schließungstyp hinzufügen.

Betonung meiner

Auf diese Weise kann die Implementierung dem Lambda ein Mitglied geben, auch wenn es ohne Erfassung ist. Ich glaube nicht, dass eine Implementierung dies jemals tun würde, aber sie dürfen dies gesetzlich tun.

NathanOliver
quelle