2016-05-14 15 views
4

Ich habe ein sehr seltsames Problem. Um die Dinge einfach zu halten, kann sagen, dass ich eine Funktion haben will, die als ArgumentLambda als Vorlage Funktion

2 Funktionen mit der gleichen Erklärung nimmt
template<typename Func> 
void foo(Func a, Func b) 
{ 
    std::cout << "good"; 
} 

zu versuchen, die Dinge heraus, dass ich putchar aus cstdio nahm, und erstellt eine identische Funktion, um die putchar zu entsprechen.

Nun möchte das Lambda nicht kompilieren (der Schlüssel ist, ich möchte Lambda Capture verwenden).

Also lassen Sie Vorlage Spezialisierung hinzufügen, weil der Programmierer weiser als die Maschine ist, richtig? :)

template<typename Func> 
void foo(Func a, Func b) 
{ 
    std::cout << "good"; 
} 

template<> 
void foo(int(*)(int), int(*)(int)) 
{ 
    std::cout << "good"; 
} 

Kein Glück, der gleiche Fehler - warum? Aber aus irgendeinem Grund, wenn ich die Vorlage Spezialisierung auf Kommentar:

//template<> 
void foo(int(*)(int), int(*)(int)) 
{ 
    std::cout << "good"; 
} 

Der Code kompiliert. Ich möchte natürlich nicht foo für jede Reihe von Funktionen Argumente überladen - das ist, was Vorlagen sind. Jeder Schritt wurde sowohl mit msvC++ als auch mit g ++ getestet. Was mache ich falsch?

Antwort

2

Jede Lambda ist eine andere Art, so dass Sie zwei verschiedene Template-Parameter benötigen, sie haben

template<typename FuncA, typename FuncB> 
void foo(FuncA a, FuncB b) 

Typen zu bekommen zerfallen nicht, wenn Template-Typen herzuleiten (KOMMENTAR ZUR KORREKTUR SEE). Ein Lambda bleibt also ein Lambda und zerfällt nicht zu einem Funktionszeiger. Derselbe Grund, aus dem ein String-Literal als char[N] statt als const char * abgeleitet wird.

Mit Ihrem zweiten Beispiel, das Spezialisierung verwendet, möchte es Ihre Spezialisierung nicht verwenden, da das Lambda kein Funktionszeiger ist. Sie können das Lambda in einen Funktionszeiger umwandeln und es funktionieren lassen: https://godbolt.org/g/ISgPci Das Trick können Sie tun, hier ist + my_lambda, weil + für Zeiger so definiert ist, wird es zwingen, das non-capturing Lambda, ein Funktionszeiger zu werden.

+0

* "Typen zerfallen nicht, wenn Template-Typen herzuleiten" * natürlich sie tun. * "Aus demselben Grund, in dem ein String-Literal als char [N] anstelle von const char *" * abgeleitet wird, wird ein roher String-Literal als const char * –

+0

@PiotrSkotnicki abgeleitet, wann kommt er dann als Char-Array? Ich habe gerade darüber in einer anderen Frage gelesen. – xaxxon

+0

Wenn ein Funktionsparameter vom Referenztyp ist –

2

Zwei Möglichkeiten.

: Einfach + vor dem Lambda:

foo(putchar, +myPutcharLambda); 

das funktioniert, weil unären + einen ganze Zahl artige Wert, wie beispielsweise einen Zeiger erwartet. Daher wandelt sich das Lambda in einen Funktionszeiger um.

Letztendlich hat ein (nicht erfassendes) Lambda nicht denselben Typ wie ein Funktionszeiger, obwohl es in einen Funktionszeiger konvertiert werden kann.

Wie soll ein Compiler wissen, welche Konvertierungen zwei Objekte desselben Typs erzeugen dürfen?

: Es gibt eine weitere Möglichkeit, sich die Tatsache zunutze macht, dass die ?: bereit ist, einige Umwandlungen zu tun, converting one type to another in some circumstances.

template<typename Func1, typename Func2> 
void foo2(Func1 a, Func2 b) 
{ 
    using common_type = decltype(true?a:b); // or 'false', it doesn't matter 
    foo<common_type>(a,b); 
} 
+1

Option 1 ist besonders ungezogen. :) – erip

1

Ein Lambda hat seine eigene Art, die mit einem Funktionszeiger zerfallen kann, aber nicht im Fall einer Template-Funktion übereinstimmen, wird es für die eigentliche Funktion, wie Sie wegen der impliziten Umwandlung gefunden.

Im Falle des Abgleichs mit einer Vorlage müssen Sie foo mit dem gewünschten Typ eindeutig disambiguieren und explizit instanziieren oder das Lambda in einen Funktionszeiger konvertieren.

foo<decltype(putchar)>(putchar, myPutcharLambda); 

oder

foo(putchar, +myPutcharLambda); 
Verwandte Themen