wake-up-neo.com

Was ist die Art von Lambda, wenn mit "auto" in C++ 11 abgeleitet wird?

Ich hatte die Vorstellung, dass ein Lambda-Typ ein Funktionszeiger ist. Als ich den folgenden Test durchführte, fand ich es falsch ( demo ).

#define LAMBDA [] (int i) -> long { return 0; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok
  assert(typeid(pFptr) == typeid(pAuto));  // assertion fails !
}

Fehlt dem obigen Code ein Punkt? Wenn nicht, was ist dann der typeof ein Lambda-Ausdruck, wenn er mit dem auto-Schlüsselwort abgeleitet wird?

110
iammilind

Der Typ eines Lambda-Ausdrucks ist nicht angegeben. 

Sie sind aber im Allgemeinen nur syntaktischer Zucker für Funktoren. Ein Lambda wird direkt in einen Funktor übersetzt. Alles, was sich in [] befindet, wird in Konstruktorparameter und Mitglieder des Funktionsobjekts umgewandelt, und die Parameter in () werden in Parameter für die Funktionsfunktion des Funktionselements operator() umgewandelt.

Ein Lambda, das keine Variablen erfasst (nichts innerhalb der []s) kann in einen Funktionszeiger umgewandelt werden (MSVC2010 unterstützt dies nicht, wenn dies Ihr Compiler ist, aber diese Konvertierung ist Teil des Standards).

Der eigentliche Typ des Lambda ist jedoch kein Funktionszeiger. Es ist ein nicht spezifizierter Funktortyp.

111
jalf

Es ist eine eindeutige unbenannte Struktur, die den Funktionsaufrufoperator überlastet. Jede Instanz eines Lambda führt einen neuen Typ ein.

Im Sonderfall eines nicht erfassenden Lambda weist die Struktur zusätzlich eine implizite Umwandlung in einen Funktionszeiger auf.

92
avakar

[C++11: 5.1.2/3]:Der Typ des Lambda-Ausdrucks (der auch der Typ des Abschlussobjekts ist) - ist ein eindeutiger, unbenannter Nicht-Union-Klassentyp - wird als Closure-Typ bezeichnet, dessen Eigenschaften im Folgenden beschrieben werden. Dieser Klassentyp ist kein Aggregat (8.5.1). Der Schließungstyp wird im kleinsten Blockbereich, Klassenbereich oder Namespace-Bereich deklariert, der den entsprechenden Lambda-Ausdruck enthält. [..]

In der Klausel werden verschiedene Eigenschaften dieses Typs aufgelistet. Hier sind einige Highlights:

[C++11: 5.1.2/5]: Der Abschlusstyp für einen Lambda-Ausdruck hat einen öffentlichen inline Funktionsaufrufoperator (13.5.4), dessen Parameter und Der Rückgabetyp wird durch die Lambda-Klausel des Parameterdeklarationssatzes und Trailing-Return-Typ . [..]

[C++11: 5.1.2/6]: Der Verschlusstyp für einen Lambda-Ausdruck ohne Lambda-Erfassung hat eine öffentliche, nicht virtuelle, nicht explizite const-Konvertierungsfunktion in einen Zeiger auf eine Funktion mit denselben Parametern und Rückgabetypen wie der Funktionsaufrufoperator des Abschlusstyps. Der von dieser Konvertierungsfunktion zurückgegebene Wert muss die Adresse einer Funktion sein, die beim Aufrufen dieselbe Wirkung hat wie beim Aufrufen des Funktionsaufrufoperators des Abschlusstyps.

Die Konsequenz dieser letzten Passage ist, dass Sie, wenn Sie eine Konvertierung verwenden, LAMBDA zu pFptr zuweisen können.

#include <iostream>
#include <typeinfo>

#define LAMBDA [] (int i)->long { return 0l; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok

  std::cout<<typeid( *pAuto ).name() << std::endl;
  std::cout<<typeid( *pFptr ).name() << std::endl;

  std::cout<<typeid( pAuto ).name() << std::endl;
  std::cout<<typeid( pFptr ).name() << std::endl;
}

Die Funktionstypen sind zwar identisch, aber das Lambda führt einen neuen Typ ein (wie einen Funktionscode).

1
BЈовић

Eine praktische Lösung von Wie kann ich ein boost :: bind-Objekt als Klassenmitglied speichern? , versuche boost::function<void(int)> oder std::function<void(int)>.

0
Gabriel

Es sollte auch beachtet werden, dass Lambda in Funktionszeiger konvertierbar ist. Typeid <> gibt jedoch ein nicht trviales Objekt zurück, das sich von Lambda zu generischem Funktionszeiger unterscheiden sollte. Der Test für typeid <> ist also keine gültige Annahme. Im Allgemeinen möchte C++ 11 nicht, dass wir uns um die Typspezifikation kümmern, wenn ein bestimmter Typ in einen Zieltyp konvertierbar ist.

0
Syed Raihan