2011-01-14 3 views
3

Warum scheitern wird dies zu kompilieren:Warum wird diese C++ Lamba-Funktion nicht kompilieren?

int myVar = 0; 
myVar ? []()->void{} : []()->void{}; 

mit folgendem Fehler msg:

Fehler 2 Fehler C2446: ':': von 'red_black_core :: `anonymous-Namespace' keine Umwandlung :: < lambda1>‘red_black_core :: anonymous-Namespace :: < lambda0>

Während dies korrekt erfüllt:

void left() 
{} 
void right() 
{} 

int myVar = 0; 
myVar ? left() : right(); 
+0

Welchen Compiler benutzen Sie und was sind die Warnungen/Fehler? – schnaader

+0

Wie üblich, was ist die Fehlermeldung? – MSalters

+0

@schnaader VS2010 sp beta1 –

Antwort

5

Regeln für die bedingte Operator im Entwurf N3225 sagt an einer Stelle

Andernfalls ist das Ergebnis ein prvalue ist. Wenn der zweite und der dritte Operand nicht den gleichen Typ haben und entweder über eine (möglicherweise cv-qualifizierte) Klassenart verfügt, wird die Überladungsauflösung verwendet, um die Konvertierungen (falls vorhanden) auf die Operanden anzuwenden (13.3.1.2, 13.6). Wenn die Überladungsauflösung fehlschlägt, ist das Programm fehlerhaft. Andernfalls werden die so ermittelten Konvertierungen angewendet, und die konvertierten Operanden werden anstelle der ursprünglichen Operanden für den Rest dieses Abschnitts verwendet.

Bis zu diesem Punkt, jede andere Alternative (wie, um den einen Operanden zu konvertieren) gescheitert, so werden wir jetzt tun, was dieser Absatz sagt. Die Konvertierungen, die wir anwenden werden, werden durch die Überladungsauflösung bestimmt, indem a ? b : c in operator?(a, b, c) umgewandelt wird (ein imaginärer Funktionsaufruf zu einer so genannten Funktion). Wenn Sie schauen, was die Kandidaten für die imaginäre operator? sind, finden Sie (unter anderem)

Für jeden Typ T, wobei T ein Zeiger, Zeiger-to-Mitglied oder scoped Aufzählungstyp, gibt es Bewerber Funktionen der Form

T operator?(bool, T , T); 

Und dazu gehört auch ein Kandidat für die T der Typ void(*)() ist. Dies ist wichtig, da Lambda-Ausdrücke ein Objekt einer Klasse ergeben, die in einen solchen Typ konvertiert werden kann. Die Spezifikation sagt

Der Verschlusstyp für einen Lambda-Ausdruck ohne Lambda-Erfassung eine öffentliche nicht-virtuelle nicht-explizite const Umwandlungsfunktion die gleichen Parameter funktionieren Zeiger mit und Rückgabetypen wie der Funktion des Verschlusstypen Anruf Betreiber. Der Wert, der von dieser Konvertierungsfunktion zurückgegeben wird, ist die Adresse einer Funktion, die, wenn sie aufgerufen wird, den gleichen Effekt hat wie das Aufrufen des Funktionsaufrufoperators des Schließungstyps.

Die Lambda-Ausdrücke können nicht auf einen anderen der Parametertypen aufgelistet, konvertiert sein, die Überladungsauflösung bedeutet, erfolgreich ist, findet eine einzige operator? und wird beide Lambda-Ausdrücke zu Funktionszeiger umwandeln. Der Rest des Bedingungsoperatorabschnitts wird dann wie üblich fortfahren, wobei er nun zwei Verzweigungen für den Bedingungsoperator desselben Typs aufweist.

Deshalb ist auch Ihre erste Version in Ordnung, und warum GCC es richtig annimmt. Allerdings verstehe ich nicht wirklich, warum Sie die zweite Version überhaupt zeigen - wie andere erklärt haben, macht es etwas anderes und es ist nicht überraschend, dass es funktioniert, während das andere nicht funktioniert (auf Ihrem Compiler). Beim nächsten Mal sollten Sie versuchen, keinen nutzlosen Code in die Frage aufzunehmen.

+1

Meh. Es ist nicht hilfreich für mich, Standardisierungen zu verbreiten, ohne es zu erklären. –

+0

@Tomalak, ohh du hast Recht! Ich habe jetzt alles ausgearbeitet. –

+0

@Tomalak Beachten Sie, dass es * nicht * ausreicht, um zu zeigen, dass beide Zweige in denselben Typ konvertiert werden können. Zum Beispiel sind sowohl "ios * x = &cin;" als auch "ios * y = &cout;" gültig, aber immer noch "myVar? & cin: & cout' ist ungültig. Deshalb habe ich den Entwurf zitiert. Also wollte ich * mehr * Erklärung geben, indem ich die Spezifikation zitiere, anstatt weniger, indem ich Einzelheiten weglasse. Wenn der Fragesteller etwas nicht versteht, kann er fragen und ich kann es klären. Wenn ich nur etwas übergeordnetes sage, muss er es glauben, bis er einen ähnlichen Fall trifft, in dem die allgemeine Erklärung nicht passt. Das wäre nicht gut. –

2

Beide Snippets kompilieren gut mit GCC 4.5.2.

Vielleicht hat Ihr Compiler keine (oder teilweise/gebrochene) Unterstützung für C++ 0x Features wie Lambda?

0

Es kann nicht kompiliert werden. Es funktioniert gut. Sie haben wahrscheinlich C++ 0x in Ihrem Compiler nicht aktiviert.

Edit: eine Fehlermeldung auf die ursprüngliche Frage wurde nun hinzugefügt! Es scheint, dass Sie C++ 0x Unterstützung haben, aber dass es in Ihrem Compiler nicht abgeschlossen ist. Das ist nicht überraschend.

Der Code ist immer noch gültig C++ 0x, aber ich empfehle nur mit C++ 0x Funktionen, wenn Sie wirklich müssen, bis es standardisiert ist und es volle Unterstützung über eine Reihe von Toolchains gibt. Sie haben eine brauchbare C++ 03-Alternative, die Sie in Ihrer Antwort angegeben haben, und ich schlage vor, sie vorläufig zu verwenden.

Mögliche alternative Erklärung:

Beachten Sie auch, dass Sie wahrscheinlich nicht schreiben, was man eigentlich schreiben soll. []()->void{} ist ein Lambda. []()->void{}() führt das Lambda aus und wertet das Ergebnis aus. Abhängig davon, was Sie mit diesem Ergebnis tun, könnte Ihr Problem sein, dass das Ergebnis des Aufrufs Ihres Lambda void ist, und Sie können nicht viel mit void tun.

+0

Sein Compiler hat keine Option, um es auszuschalten. Die Fehlermeldung erwähnt nicht fehlerhafte Syntax, es erwähnt speziell Lambdas. – Puppy

+0

@DeadMG: So scheint es jetzt. Er hatte seine Fehlermeldung nicht gepostet, als ich diese Antwort schrieb. Bearbeitet, um zu aktualisieren. –

+1

@Downvoter: Bitte kommentieren und erklären. –

9

Der Rückgabetyp des?: -Operators muss aus seinen zwei Operanden abgeleitet werden, und die Regeln zur Bestimmung dieses Typs sind ziemlich komplex. Lambdas befriedigen sie nicht, weil sie nicht ineinander umgewandelt werden können. Wenn also der Compiler versucht herauszufinden, was das Ergebnis davon ist?: Is, dann kann es kein Ergebnis geben, weil diese beiden Lambda nicht ineinander umwandelbar sind.

Wenn Sie jedoch versuchen, die Funktionen zu berechnen, dann Sie tatsächlich sie genannt, aber du hast nicht Anruf die Lambda-Ausdrücke. Wenn Sie also die Funktionen aufrufen, haben sie beide void, also ist der Rückgabetyp von:: ungültig.

Diese

void left() 
{} 
void right() 
{} 

int myVar = 0; 
myVar ? left() : right(); 

ist die extra

int myVar = 0; 
myVar ? [](){}() : [](){}(); 

Hinweis Äquivalent() auf der End-I die Lambda genannt tatsächlich.

Was Sie ursprünglich zu

compiler_deduced_type var; 
if (myVar) 
    var = [](){}; 
else 
    var = [](){}; 

Aber- kein Typ Äquivalent hatte besteht, dass beide lambdas sein kann. Der Compiler ist in seinem Recht, beide Lambdas zu verschiedenen Typen zu machen.

EDIT:

Ich erinnerte mich an etwas. Im letzten Standardentwurf können Lambdas ohne Captures implizit in Funktionszeiger derselben Signatur konvertiert werden. Das heißt, im obigen Code könnte compiler_deduced_type void (*)() sein. Ich weiß jedoch, dass MSVC dieses Verhalten nicht enthält, weil das zu dem Zeitpunkt nicht definiert wurde, als sie Lambdas implementierten. Dies ist wahrscheinlich, warum GCC es erlaubt und MSVC nicht - GCC Lambda-Unterstützung ist wesentlich neuer als MSVC.

+0

@DeadMG also warum kompiliert es mit GCC? –

+1

@There: Wie zur Hölle sollte ich wissen? Frage stellt das nicht. – Puppy

+1

@DeadMG Nun, ich frage dich nur so die Frage fragt __that__. Also vielleicht von anderer Seite: Wer hat nach dem Standard VS oder GCC Recht? –

3

Weil jedes Lambda ein einzigartiger Typ ist. Es ist im Grunde syntaktischer Zucker für einen Funktor, und zwei separat implementierte Funktoren sind nicht vom selben Typ, selbst wenn sie identischen Code enthalten.

Der Standard gibt an, dass Lambdas in Funktionszeiger umgewandelt werden können, wenn sie nichts erfassen. Diese Regel wurde jedoch hinzugefügt, nachdem MSVCs Lambda-Unterstützung implementiert wurde.

Mit dieser Regel können jedoch zwei Lambdas in den gleichen Typ konvertiert werden, und so glaube ich, dass Ihr Code mit einem kompatiblen Compiler gültig wäre.

Verwandte Themen