问题
I have a ThreadPool
class with an enqueue
function:
class ThreadPool
{
public:
//(Code removed here)
template <typename ... Args, typename Fun>
JobId enqueue(Fun func, Args ... args);
//(Code removed here)
}
And I use it on these non static member function loadStuff
on class Object
:
class Object
{
//(Code removed here)
void init(const PrepareData & prepareData);
virtual bool loadStuff(const PrepareData & prepareData);
//(Code removed here)
}
by calling in QObject::init :
void QObject::init(const PrepareData &prepareData)
{
threadPool->enqueue(&loadStuff, this, prepareData);
}
But I noticed that prepareData was passed by copy, which eats significant memory and slows down significantly the program (and is useless).
So I deleted the copy ctor and assignment operator in PrepareData. The program does not compile anymore, as the variadic template takes its parameters by value and not by reference.
So I declared enqueue to pass the variadic template arguments by reference:
template <typename ... Args, typename Fun>
JobId enqueue(Fun func, Args&... args);
Now the copy constructor is not called anymore, but I get the following error:
object.cpp:21: error: no matching function for call to
'ThreadPool::enqueue(bool (Object::*)(const PrepareData&), Object *, const PrepareData&)' threadPool->enqueue(&prepareType, this, loadStuff);
So I'm quite lost on how to do this. I could, instead of passing a const PrepareData &
, pass a const PrepareData *
by copy, but I would like to understand why it does not work with a const reference.
回答1:
This:
template <typename ... Args, typename Fun>
JobId enqueue(Fun func, Args ... args);
copies all the args
because they're all passed by value. There seems to be some confusion with how argument passing works - it doesn't matter that you're calling enqueue
with a reference to const, it matters that enqueue
takes its argument by value. init()
is pass by reference, but enqueue()
is not.
What you probably want instead is to pass a reference wrapper to your data (by value):
threadPool->enqueue(&loadStuff, this, std::ref(prepareData));
This will avoid copying prepareData
and call loadStuff()
correctly. This also puts the onus on the caller of enqueue()
to know which things should be copied and which should be referred to.
Although, QObject
needs to ensure that prepareData
lasts long enough. We take it by reference to const
, so it doesn't seem like it has any way of doing that. So perhaps an alternative approach would be have init()
take its data by value:
void QObject::init(PrepareData prepareData)
{
threadPool->enqueue(&loadStuff, this, std::move(prepareData));
}
来源:https://stackoverflow.com/questions/37730989/c-template-variadic-templates-pass-by-const-reference