2017-04-24 2 views
2

ich eine Methode, wie dieses:Polymorphismus nicht in C++ arbeiten Lambda-Funktion

void syncOperation(ProgressCallback& progressCallback); 

wo ProgressCallback ist:

class ProgressCallback 
{ 
public: 
    virtual void onProgress(std::size_t currValue, std::size_t maxValue) {} 
    virtual void onDone() {} 
}; 

und ich möchte es machen async, so dass ich die folgende :

void asyncOperation(ProgressCallback& progressCallback) 
{ 
    auto impl = [this](ProgressCallback& progressCallback_) 
    { 
     syncOperation(progressCallback_); 
    }; 
    jobsPool.addJob(std::bind(impl, progressCallback)); 
} 

aber das Verhalten von progressCallback im zweiten Fall (asyncOperation(ProgressCallback&)) isn‘ t polymorph, ruft es immer Methoden der Basisklasse auf und das ist definitiv nicht das, was ich erwarte. Also meine Fragen sind: 1) warum passiert es und 2) wie man es repariert (ja, ich weiß, ich kann einfach aufhören, Lambdas in meinem Fall zu verwenden, aber vielleicht gibt es einen traditionellen Workaround)?

+3

Warum jonglierst du lambdas und 'std :: bind' anstatt einfach' [&] {syncOperation (progressCallback); } '? – Quentin

+0

@drolmal diese Frage ist wild nicht verwandt –

+0

Nir, die Post erklärt im Detail a) warum gibt es nicht Poly Lambdas und b) wie man es beheben ... – didiz

Antwort

2

Der Code, den Sie zeigen, hat keinen Platz für Polymorphismus, sondern lässt vermuten, dass asyncOperation einen Verweis auf etwas von ProgressCallback abgeleitet geben wird:

Sie erstellen eine Kopie, wenn Sie Ihren Bezug auf bind passieren. Das Objekt, das bind zurückgibt, speichert den Wert basierend auf den Typinformationen, über die es verfügt. Es weiß nur, dass es einen Verweis auf ProgressCallback hat, also was für einen Wert es speichert. Dies verursacht eine Schneidekopie.

Sie benötigen Wert in einem reference_wrapper wickeln:

jobsPool.addJob(bind(impl, ref(progressCallback)); 

Oder Sie könnten Lambda verwenden (besser):

jobsPool.addJob([impl,&progressCallback](){ impl(progressCallback); }); 

Oder Sie könnten nur die impl so erstellen, dass sie alles tut allein.

Was Sie jetzt tun, ist relativ gleichwertig:

jobsPool.addJob([impl,=progressCallback]() mutable { impl(progressCallback); }); 
+1

Danke! Das ist es was ich meinte. – slowcheetah

+0

Sie sollten hier die lebenslange Gefahr hervorheben. Persönlich würde ich entweder einen Zeiger haben, um die Lebensdauerprobleme expliziter zu machen, und möglicherweise einen Smartpointer mit geteilter Eigentümerschaft verwenden. – Yakk

+0

Ich sehe nicht, wie dieser Code Sinn macht, Sie rufen "impl" von innerhalb eines Lambda, der es nicht erfasst. –

1

Sie brauchen keine Lambda zu definieren, und dann binden verwenden oder zwei Lambda-Ausdrücke verwenden, können Sie diese in einem alles tun, gehen:

Wie bereits erwähnt, werden Sie im Original-Code gebissen, weil die Kopie schneiden, die auftritt. Aufgrund der Funktionsweise der Schablonenargumentableitung müssen zurückgestellte Funktionsaufrufe wie std::bind über Referenzargumente verfügen, die über Referenzwrapper übergeben werden. Dieses ziemlich merkwürdige Verhalten ist ein weiterer guter Grund, nicht einmal bind zu verwenden, sobald Sie Zugriff auf Lambdas haben. Beachten Sie jedoch, dass dieses Verhalten auch für std::thread gilt.

Beachten Sie, dass das Speichern eines Lambda, das etwas durch Referenz erfasst, ziemlich gefährlich ist. Sie könnten asyncOperation mit einem progressCallback-Objekt aufrufen, das dann aktiv ist, und es ist möglich, dass das Objekt bis zu dem Zeitpunkt zerstört wird, an dem Lambda ausgeführt wird, was zu undefiniertem Verhalten führt.

+0

Downvoter, vorsichtig erklären? –