C++ template - variadic templates & pass by const reference

允我心安 提交于 2019-12-24 17:25:31

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!