2016-06-09 12 views
1

Ich habe eine ThreadPool Klasse mit einer enqueue Funktion:C++ template - variadische Vorlagen & vorbei konstante Referenz

class ThreadPool 
{ 
public: 
    //(Code removed here) 

    template <typename ... Args, typename Fun> 
    JobId enqueue(Fun func, Args ... args); 

    //(Code removed here) 
} 

Und ich benutze es auf dieser nicht statische Memberfunktion loadStuff auf Klasse Object:

class Object 
{ 
    //(Code removed here) 
    void init(const PrepareData & prepareData); 
    virtual bool loadStuff(const PrepareData & prepareData); 
    //(Code removed here) 
} 

von in QObject :: init Aufruf:

void QObject::init(const PrepareData &prepareData) 
{ 
    threadPool->enqueue(&loadStuff, this, prepareData); 
} 

Aber ich habe bemerkt, dass prepareData von copy übergeben wurde, was bedeutendes Gedächtnis verschlingt und das Programm signifikant verlangsamt (und nutzlos ist).

Also löschte ich die Kopie ctor und Zuweisungsoperator in PrepareData. Das Programm kompiliert nicht mehr, da die Variadic-Vorlage ihre Parameter nach Wert und nicht nach Referenz bezieht. So

ich erklärte enqueue die variadische Vorlage Argumente Referenz zu übergeben:

template <typename ... Args, typename Fun> 
JobId enqueue(Fun func, Args&... args); 

Nun ist der Copykonstruktor nicht mehr genannt wird, aber ich bekomme die folgende Fehlermeldung:

object.cpp:21: error: no matching function for call to

'ThreadPool::enqueue(bool (Object::*)(const PrepareData&), Object *, const PrepareData&)' threadPool->enqueue(&prepareType, this, loadStuff);

Also ich Ich bin ziemlich verloren, wie das geht. Ich könnte, anstatt eine const PrepareData & zu übergeben, eine const PrepareData * Kopie übergeben, aber ich würde gerne verstehen, warum es nicht mit einer const Referenz funktioniert.

+0

sagen 'Args && ...'. –

Antwort

1

Dieses:

template <typename ... Args, typename Fun> 
JobId enqueue(Fun func, Args ... args); 

kopiert alle args, weil sie alle von Wert übergeben. Es scheint etwas Verwirrung darüber zu geben, wie die Argumentübergabe funktioniert - es spielt keine Rolle, dass Sie enqueue mit einem Verweis auf const aufrufen, es ist wichtig, dass enqueue sein Argument nach Wert nimmt. init() wird durch Verweis übergeben, aber enqueue() ist nicht.

Was Sie wahrscheinlich stattdessen wollen, ist ein Referenz-Wrapper auf Ihre Daten zu übergeben (nach Wert):

threadPool->enqueue(&loadStuff, this, std::ref(prepareData)); 

Diese prepareData Kopieren vermeiden und loadStuff() richtig nennen. Dies stellt auch den Anrufer von enqueue() auf die Frage, welche Dinge kopiert werden sollten und auf welche Bezug genommen werden sollte.


Obwohl QObject Bedürfnisse, um sicherzustellen, dass prepareData lang genug dauert. Wir nehmen es unter Bezugnahme auf const, so scheint es nicht, als ob es eine Möglichkeit dazu hat. Also vielleicht ein alternativer Ansatz wäre haben init() seine Daten von Wert annehmen:

void QObject::init(PrepareData prepareData) 
{ 
    threadPool->enqueue(&loadStuff, this, std::move(prepareData)); 
} 
+0

Dinge, die an einen Thread-Pool versandt werden, sollten standardmäßig den Wert haben, um lebenslange Probleme zu vermeiden. Wenn das OP Referenz-Semantik haben möchte, könnte es einen 'reference_wrapper' verwenden. –

+0

@ T.C. Guter Punkt, dachte nicht darüber nach, was 'enqueue()' tat. – Barry

+0

@ T.C. : Ich verstehe Ihre Bedenken, aber hier sind die PrepareData eine Reihe großer const-Datensätze, die eigentlich nicht kopiert werden sollten.Ich stelle sicher, dass es dort bleibt, bis alle Thread-Pool-Funktionen bleiben, in der Tat threadPool und prepareData sind zwei Mitglieder einer größeren Klasse, die dies gewährleistet. – galinette