2016-07-10 10 views
1

Der folgende Code nicht kompiliert:Lambda-Capture-Liste und definierte Funktion Zeigertyp

typedef void(*RunnableFun)(int); //pointer-to-function type 

void foo(RunnableFun f) { 
} 
void bar(const std::string& a) { 
    foo([&](int) -> void { std::cout << a; }); 
} 

und IntelliSense sagt mir

no suitable conversion function from "lambda []void (int)->void" to "RunnableFun" exists 

und der Compiler

beschwert
'void foo(RunnableFun)' : cannot convert argument 1 from 'bar::<lambda_796873cf40a6be4e411eb9df14f486bf>' to 'RunnableFun' 

aber die folgenden nicht kompiliert:

typedef void(*RunnableFun)(int); //pointer-to-function type 

void foo(RunnableFun f) { 

} 
void bar(const std::string&) { 
    // Notice the empty capture list 
    foo([](int) -> void { std::cout << "dummy"; }); 
} 

Wie kann ich die Unterschrift von foo() halten, sondern erreichen, was ich versuchte, in der ersten Codebeispiel?

S.S .: Ändern der foo-Signatur in void foo(std::function<void(int)> f) würde kompilieren, aber kann ich es tun, ohne es zu ändern?

Antwort

2

Ein Zeiger auf Funktion speichert nur einen Ausführungsort, es speichert keinen zusätzlichen Status. Die einzige Änderung zwischen den Ausführungen muss durch global Zustand bestimmt werden.

In Ihrem Fall möchten Sie das Lambda lokalen Zustand erfassen. So, wenn nach a="hello" aufgerufen, druckt es einen anderen Wert als wenn nach a="world" aufgerufen.

Die kurze Antwort ist "Schade, so traurig, aber nein".

Sie können ein wenig hacken. Sie könnten a in einem static std::string ga; speichern und innerhalb des Lambda zugreifen. Beachten Sie, dass dies hässlich ist, nicht reentrant, Ihren Code unnötig dem globalen Status aussetzt und generell eine schlechte Idee ist.

void bar(const std::string& a) { 
    static std::string ga; 
    ga = a; // note, separate line 
    foo([](int) -> void { std::cout << ga; }); 
} 

Allgemeines reiner Funktionszeiger Rückruf APIs ist ein Zeichen, ein Tor unwissend die API konstruiert: ein geeigneter C-style Rückruf ein void* nimmt, und ein geeigneter C++ ist ein Callback-std::function oder Schablone aufrufbare oder ähnliches. (Ignorant, weil sie nicht wissen, dass das void* Muster üblich ist: Dummkopf, denn selbst wenn Sie es nicht wussten, haben sie es nicht selbst ausprobiert, nachdem sie ihr System einige Male ausprobiert hatten und das große klaffende Loch bemerkt haben. die meisten Programmierer sind Dummköpfe, bis sie alle die Fehler selbst machen!

)

Wenn Sie haben tatsächlich ein void* oder gleichwertiges Argument Sie zurück aufgerufen, können Sie es aus Ihrer Frage ausgelassen, und die Antwort ist ganz anders. Speichern Sie einfach ein ptr-to-Lambda in diesem void* und speichern Sie ein statusloses Lambda, das das statusbehaftete Lambda aus void* umwandelt.

+0

"*, der das zustandsbehaftete Lambda aus dem' void * 'ausgibt und anruft" Ich bin neugierig, wie genau das passieren würde. Schließlich ist der Typ eines Lambda nicht bekannt, also müssten Sie 'declltype (some_variable)' verwenden, um es zu erhalten und den Cast durchzuführen. Woher nehmen Sie die Variable? Ich denke, es wäre besser, das Stateful Lambda in eine 'std :: function 'zu packen und * das * als' void *' in Frage zu stellen. Auf diese Weise wissen Sie genau, um welchen Typ es sich handelt. –

+0

@nicol 'auto f = [&] (int x) {std :: cout << a << '\ n';}; foo ([] (void * ptr, int x) {auto * pf = statischer_cast (ptr); (* pf) (x);}, &f); '? Oder wickle das Lambda-and-' void * 'Muster in eine Klasse mit einer Factory-Funktion, die den Typ von' F' ableitet? Nichts grundsätzlich schwer hier, und Sie müssen das Lambda irgendwo speichern, so hat 'declltype' ein natürliches Ziel (wo immer es ist). – Yakk

+0

Interessant Hack, obwohl nicht anwendbar für meine [Multithread-Anwendung] (http://stackoverflow.com/a/4590776/1531708). Als Antwort wegen seiner vielen Details – PhilLab

6

Sie können die meisten der standard library algorithm functions betrachten. Wenn sie ein "Prädikat" (Callable Object) nehmen, tun sie es als Template-Argumente.

So könnten Sie Ihre Funktion als Vorlage machen:

template<typename FunctionType> 
void foo(FunctionType f) { 
} 

Keine Änderung erforderlich ist.

Es ist entweder dies oder std::function verwenden, können Sie Ihr Problem nicht lösen, ohne die foo Funktion zu ändern.

Verwandte Themen