2016-06-09 126 views
1

我有一个ThreadPool类的enqueue功能:C++模板 - 可变参数模板和路过常引用

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

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

    //(Code removed here) 
} 

我用它在这些非静态成员函数loadStuffObject类:

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

通过调用QObject :: init:

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

但我注意到prepareData是通过复制传递的,这会消耗大量的内存并显着减慢程序的速度(并且是无用的)。

所以我删除了PrepareData中的copy ctor和赋值操作符。该程序不再编译,因为可变参数模板通过值而不是参考来获取参数。

因此,我宣布排队按引用传递的可变参数模板参数:

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

现在拷贝构造函数不再被调用,但我得到以下错误:

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

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

所以我如何做到这一点,我相当遗憾。我可以,而不是通过const PrepareData &,通过const PrepareData *副本,但我想了解为什么它不适用于const引用。

+0

说'参数数量&& ...'。 –

回答

1

此:

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

复制所有args,因为他们都按值传递。对于参数传递如何工作似乎存在一些混淆 - 无论您是否在调用const时引用enqueue都没关系,重要的是enqueue通过值取其参数。 init()是通过参考,但enqueue()不是。

你可能就要什么是(按价值计算)传递一个参考包装到您的数据:

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

这将避免复制prepareData和正确调用loadStuff()。这也使enqueue()的调用者有责任知道应该复制哪些东西以及应该引用哪些东西。


虽然QObject需要确保prepareData持续足够长的。我们通过参考const,所以它似乎没有任何方式做到这一点。所以,或许是另一种方法是有init()按值取其数据:

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

发送到线程池*的东西默认情况下应为*值,以避免生命期问题。如果OP需要引用语义,他们可以使用'reference_wrapper'。 –

+0

@ T.C。好点,没有考虑'enqueue()'在做什么。 – Barry

+0

@ T.C。 :我理解你的担心,但是这里的PrepareData是一堆大的const数据集,应该不会被复制。我确保它保持在那里,直到所有的线程池函数保持为止,实际上threadPool和prepareData是一个更大类的两个成员,它确保了这一点。 – galinette