2

Beim Versuch, dies zu kompilieren, gibt es überraschend einen Fehler, weil der auto Parameter der Lambda-Funktion in std::string und den Compiler aufgelöst wurde weiß nicht, wie man std::string in int oder Widget beim Aufruf test umwandelt.Funktion überladen mit std :: function und generische lambdas: std :: string bevorzugt über int

Aber, frage ich mich, warum der Compiler die zweite invok Funktion anstelle des ersten gewählten hat, wenn der erste gelingen würde:

#include <string> 
#include <functional> 

struct Widget {}; 

bool test(int); 
bool test(Widget); 

void invok(std::function<bool(int)>);   // #1 
void invok(std::function<bool(std::string)>); // #2 

int main() 
{ 
    // error: unresolved overloaded function type 
    // invok(test);    

    // still error: no known conversion from std::string to 
    // int or Widget 
    invok([](auto&& x) {  
     return test(std::forward<decltype(x)>(x)); 
    }); 

} 

Das Beispiel von a C++ proposal kopiert wurde.

+3

Ah, SFINAE-unfreundliche generische Lambdas. –

+1

Ich stimme dieser Frage zu. – Barry

Antwort

3

Der Compiler hat # 2 nicht gewählt. Es versucht zu entscheiden, ob es # 2 wählen kann.

Um dies zu tun, fragt es "Kann dieses generische Lambda in std::function<bool(std::string)> umgewandelt werden"?

std::function Converting Constructor sagt "nur wenn es mit einem std::string rvalue aufrufbar ist und der Ergebnistyp in bool konvertierbar ist".

Compiler versucht, auto als std::string abzuleiten, ersetzen Sie in die Signatur des Funktionsaufrufoperators ... Erfolg! Hoppla, der Rückgabetyp ist auto, und es benötigt einen tatsächlichen Typ, um die Frage "ist konvertierbar" zu beantworten. Daher instanziiert es den Rumpf der Funktionsaufruf-Operatorvorlage, um den Rückgabetyp herauszufinden.

Autsch. Der Körper ist schließlich nicht gültig für std::string. Harte Fehler und Explosionen folgen.

3

Ihre Überladung bleibt mehrdeutig (Art von), aber auf dem Weg zur Feststellung, dass Ihr Code in einem harten Fehler beim Ausprobieren der beiden Möglichkeiten (Übergabe eines int oder einer Zeichenfolge) läuft.

Um die Ambiguität zu sehen, fügen Sie Ihrem Lambda ein ->bool hinzu, damit es den Körper nicht kompilieren muss, um den Rückgabewert zu bestimmen.

Der Körper eines Lambda befindet sich nicht in einem Bereich, in dem ein Substitutionsfehler nicht zu einem Fehler führt. Stattdessen erhalten Sie dort einen schweren Fehler.

Die einfache Lösung besteht darin, dass Ihr Lambda explizit einen int-Wert annimmt.

Wenn Sie eine generische Lösung:

#define RETURNS(...) \ 
    noexcept(noexcept(__VA_ARGS__)) \ 
    -> decltype(__VA_ARGS__) \ 
    { return __VA_ARGS__; } 

#define OVERLOADS_OF(...) \ 
    [](auto&&...args) \ 
    RETURNS(__VA_ARGS__(decltype(args)(args)...)) 

und dann

invok(OVERLOADS_OF(test)); 

hat (zumindest näher an) das Richtige.

Dieses Makro verschiebt den Fehler vom Körper des Lambda in einen nachfolgenden Rückgabetyp. Und der Fehler (dieser String kann nicht zum Test übergeben werden) tritt jetzt in einem Kontext auf, in dem Substitutionsfehler keinen Fehler verursacht (SFINAE). So funktioniert alles.

Die genauen Regeln für was ist SFINAE freundlich und was nicht benötigt, den Standard zu lesen. Die Faustregel lautet jedoch, dass es gut funktioniert, dass Compiler Fehler in Funktionskörpern nicht als Substitutionsfehler behandeln müssen und dass der Zugriff auf den Inhalt einer nicht definierten Klasse ein schwerer Fehler ist.Das erste, weil es ein vernünftiger Ort zu sein schien, eine Linie zu zeichnen und es für Compiler-Schreiber leichter zu machen; der zweite, weil die Alternative Wahnsinn oder ODR Bug Köder ist.

In der Praxis sind die Regeln der SFINAE-Regeln arkaner, und zuletzt habe ich in C++ 14 nachgeprüft, dass SFINAE während der partiellen Spezialisierung der Template-Klasse weggelassen wurde: Jeder Compiler hat es unterstützt. Aber vielleicht habe ich es falsch verstanden. In jedem Fall scheint die von mir verwendete Faustregel genauso nützlich zu sein wie der Standardtext. Beide werden nicht perfekt sein.

+0

Können Sie Ihre Antwort mit einem Hinweis auf einen "SFINAE-freundlichen Bereich" verbessern? –

+0

@ Peregring-lk Faustregel hinzugefügt. – Yakk

Verwandte Themen