Das +
im Ausdruck +[](){}
ist der unäre +
Operator. Es ist wie folgt in [expr.unary.op] / 7 definiert:
Der Operand des unären +
Operators muss eine arithmetische, nicht skalierte Aufzählung oder einen Zeigertyp haben, und das Ergebnis ist der Wert des Arguments.
Das Lambda ist nicht vom arithmetischen Typ usw., kann aber konvertiert werden:
[expr.prim.lambda] / 3
Der Typ des Lambda-Ausdrucks [...] ist ein eindeutiger, unbenannter Nicht-Union-Klassentyp - der so genannte Closure-Typ - dessen Eigenschaften im Folgenden beschrieben werden.
[expr.prim.lambda] / 6
Der Schließungstyp für einen Lambda-Ausdruck ohne Lambda-Erfassung verfügt über eine public
Nicht- virtual
Nicht- explicit
const
Konvertierungsfunktion zum Zeiger auf eine Funktion mit denselben Parameter- und Rückgabetypen wie der Funktionsaufrufoperator des Schließungstyps. Der von dieser Konvertierungsfunktion zurückgegebene Wert ist die Adresse einer Funktion, die beim Aufrufen den gleichen Effekt hat wie das Aufrufen des Funktionsaufrufoperators des Schließungstyps.
Daher +
erzwingt der Unäre die Konvertierung in den Funktionszeigertyp, der für dieses Lambda gilt void (*)()
. Daher ist der Typ des Ausdrucks +[](){}
dieser Funktionszeigertyp void (*)()
.
Die zweite Überladung void foo(void (*f)())
wird zu einer exakten Übereinstimmung in der Rangfolge für die Überlastungsauflösung und wird daher eindeutig ausgewählt (da die erste Überladung KEINE genaue Übereinstimmung ist).
Die Lambda [](){}
kann umgewandelt werden std::function<void()>
über die nicht explizit Vorlage Ctor von std::function
, die jede Art nimmt , die die erfüllt Callable
und CopyConstructible
Anforderungen.
Das Lambda kann auch void (*)()
über die Konvertierungsfunktion des Verschlusstyps umgerechnet werden (siehe oben).
Beide sind benutzerdefinierte Konvertierungssequenzen und haben denselben Rang. Aus diesem Grund schlägt die Überlastungsauflösung im ersten Beispiel aufgrund von Mehrdeutigkeiten fehl .
Laut Cassio Neri, unterstützt durch ein Argument von Daniel Krügler, sollte dieser unäre +
Trick ein bestimmtes Verhalten sein, dh Sie können sich darauf verlassen (siehe Diskussion in den Kommentaren).
Trotzdem würde ich empfehlen, eine explizite Umwandlung in den Funktionszeigertyp zu verwenden, wenn Sie die Mehrdeutigkeit vermeiden möchten: Sie müssen SO nicht fragen, was funktioniert und warum es funktioniert;)
std::bind
an einstd::function
Objekt binden , das ähnlich wie ein Funktionswert aufgerufen werden kann.operator +()
zu einem zustandslosen Schließungstyp bei. Angenommen, dieser Operator gibt etwas anderes als den Zeiger auf die Funktion zurück, in die der Verschlusstyp konvertiert wird. Dies würde dann das beobachtbare Verhalten eines Programms ändern, das gegen 5.1.2 / 3 verstößt. Bitte lassen Sie mich wissen, wenn Sie dieser Argumentation zustimmen.operator +
, aber dies ist vergleichbar mit der Situation, mit der es zunächst keine gibtoperator +
. Es ist jedoch nicht festgelegt, dass der Verschlusstyp keineoperator +
Überlastung aufweisen soll. "Eine Implementierung kann den Verschlusstyp anders definieren als nachstehend beschrieben, vorausgesetzt, dies ändert das beobachtbare Verhalten des Programms nicht anders als durch [...]", aber IMO, das einen Operator hinzufügt, ändert den Verschlusstyp nicht in etwas anderes als das, was ist "unten beschrieben".operator +()
ist genau die, die vom Standard beschrieben wird. Der Standard ermöglicht es einer Implementierung, etwas anderes als das Angegebene zu tun. Zum Beispiel hinzufügenoperator +()
. Wenn dieser Unterschied jedoch von einem Programm beobachtet werden kann, ist er illegal. Einmal habe ich in comp.lang.c ++ gefragt, ob ein Closure-Typ ein typedef fürresult_type
und das anderetypedefs
erforderlich machen könnte, um sie anpassbar zu machen (zum Beispiel vonstd::not1
). Mir wurde gesagt, dass dies nicht möglich sei, da dies beobachtbar sei. Ich werde versuchen, den Link zu finden.