c++ - 如何制作一个采用引用而不是副本的可变参数模板方法?
问题描述
我们有一个模板方法,它根据可用的系统线程数,使用线程池一般地并行执行一些成员函数。
此方法必须采用的两个参数是固定的,第一个是线程索引,第二个是线程数。
因此,这些方法可以执行以下操作:
for(i = threadIndex; i < workContainer.size(); i += numThreads)
{
// So this thread will only do every numThreads piece of the work from the workContainer, with no overlap between threads
}
这很好,这就是该方法的样子:
template <class S, class Args>
void parallelWork(S* self, void (S::*workfcn)(const unsigned, const unsigned, Args*), Args* args)
{
auto work = [&](const unsigned threadIndex, const unsigned nthreads, S* self, Args* args)
{
std::string* rc = nullptr;
try
{
(self->*workfcn)(threadIndex, nthreads, args);
}
catch (std::exception& err)
{
rc = new std::string(err.what());
}
return rc;
};
ulong nthreads = size();
std::vector<std::string*> rc(nthreads);
if (1 == nthreads)
{
rc[0] = work(0, 1, self, args);
}
else
{
std::vector<std::future<std::string*>> frc(nthreads);
for (unsigned i = 0; nthreads > i; ++i)
frc[i] = enqueue(work, i, nthreads, self, args);
// Wait for all
for (unsigned i = 0; nthreads > i; ++i)
rc[i] = frc[i].get();
}
for (unsigned i = 0; nthreads > i; ++i)
{
if (rc[i])
{
HELAS_ERROR(std::string("Thread ") + std::to_string(i) + ": " + *rc[i]); delete rc[i];
}
}
}
然而,这是有限的。当前的解决方案要求我们有效地将所有参数(其中一些是引用)放入一个结构中,然后将指向该结构的指针传递给该parallelWork()
方法。
如果我们可以直接传递方法应该采用的参数,那不是很好吗?IE,而不是这样做:
struct ThreadArgs
{
const std::vector<int>& inArg1;
double inArg2;
SomeClass* inarg3;
std::vector<std::vector<double>> outArg1; // outer vector is per-thread
}
void MyClass::doFooInParallel()
{
std::vector<int> inArg1;
double inArg2;
SomeClass* cp = getSomeClass();
std::vector<std::vector<double>> outArg1(numThreads);
ThreadArgs args = ThreadArgs(inArg1, inArg2, cp, outArg1);
parallelWork(this, &MyClass::foo, &args);
}
我们可以这样做:
void MyClass::doFooInParallel()
{
std::vector<int> inArg1;
double inArg2;
SomeClass* cp = getSomeClass();
std::vector<std::vector<double>> outArg1(numThreads);
parallelWork(this, &MyClass::foo, inArg1, inArg2, cp, outArg1);
}
似乎可变参数模板将是解决方案,将方法的开头更改为:
template <class S, class... Args>
void parallelWork(S* self,void (S::*workfcn)(const unsigned, const unsigned, Args... args),Args... args)
{
auto work = [&](const unsigned threadIndex, const unsigned nthreads,S* self, Args... args)
这确实符合我们当前对该方法的使用。但是,有一个问题 - 这些参数是按值传递而不是按引用传递。
对于输入,这可能还不错,有些可能会被复制省略消除。但是对于作为方法输出的传递变量(例如大小为 nthreads 的向量,每个线程的输出),这显然是行不通的——这意味着并行执行工作会导致实际上什么也没做。
我试着让他们像这样引用:
template <class S, class... Args>
void parallelWork(S* self,void (S::*workfcn)(const unsigned, const unsigned, Args&&... args),Args&&... args)
{
auto work = [&](const unsigned threadIndex, const unsigned nthreads,S* self, Args&&... args)
但是,当我们尝试使用它时,它会失败。
could not match type-parameter 0-1&& against (the actual parameter we tried to pass)
(单参考也失败了,但我测试以防万一)
有什么办法可以解决这个问题?是否可以在 C++11 中使用,或者仅具有后来 C++ 标准的功能?
会std::forward
或std::ref
或会std::cref
以某种方式帮助吗?
这实际上更像是一个概念性问题,所以请原谅我所有这些代码都无法运行,要获得所有这些的可运行示例将是一项巨大的工作,我认为这个问题得到了很好的解释足以涵盖它,但如果需要澄清,请告诉我。
解决方案
&&
不是引用,而是 r 值(可以是引用或值)。你应该使用Args& ...
. 我认为问题在于您正在使用template <class S, class... Args>
应该使用template <class S, typename... Args>
的地方,因为您的论点似乎并不总是类。
如果你想使用转发(std::vector.emplace_back
例如),你可以使用Args && ...
然后std::forward
你的参数给函数。在您的情况下,您应该注意 usingstd::forward
仍将通过值而不是通过引用传递您的参数。要实现通过 r 值传递参数,请std::move
改用!
推荐阅读
- json - 如何从 ionic 3 角度的 json 数组中获取元素?
- cmake - 如何使用 RaspberryPi 的 bitbake 编译 Alexa-SDK 的 SampleApp
- elasticsearch - Elasticsearch Completion Suggester 不返回匹配输入的搜索文档
- prometheus-alertmanager - 如何更改 prometheus 警报管理器端口地址
- angular - ionic 3中的角度CORS问题和服务器端javaEE6的glassfish问题
- apache - 如何获得 Solr 中搜索最多的单词?
- laravel - Laravel nova - 基于另一个字段的自定义字段设置值并且仍然可以编辑
- alexa - 如果我使用自己的端点,如何将 ask-sdk 与节点 js 一起使用?
- r - 一张图表中的多个水平条形图
- python - 是否可以让客户端与服务器端的 recaptcha 交互?