在我的下面的代码中,我有一个接受“通用引用”(F&&
)的函数。该函数还有一个内部类,在其构造函数中接受F&&
的对象。那时F&&
仍然是一个普遍的参考吗?即是F
仍然被认为是推断类型?
换句话说,我应该在构造函数初始化列表中使用std::forward<F>
还是std::move
?
#include "tbb/task.h"
#include <iostream>
#include <future>
template<class F>
auto Async(F&& f) -> std::future<decltype(f())>
{
typedef decltype(f()) result_type;
struct Task : tbb::task
{
Task(F&& f) : f_(std::forward<F>(f)) {} // is forward correct here?
virtual tbb::task* execute()
{
f_();
return nullptr;
}
std::packaged_task<result_type()> f_;
};
auto task = new (tbb::task::allocate_root()) Task(std::forward<F>(f));
tbb::task::enqueue(*task);
return task->f_.get_future();
}
int main()
{
Async([]{ std::cout << "Hi" << std::endl; }).get();
}
答案 0 :(得分:6)
那时
F&&
仍然是一个普遍的参考吗?即是F
仍然被认为是推断类型?
这种混淆是我不喜欢通用引用一词的原因...... there's no such thing。
我更喜欢用左值引用和右值引用以及参考折叠规则和模板参数推导来理解代码。
当使用类型为L
的左值调用函数时,参数F
将推导为L&
,参考折叠规则F&&
仅为{{ 1}}。在L&
构造函数中没有任何更改,Task
仍为F&&
,因此构造函数采用绑定到传递给L&
的左值的左值引用,因此您不需要想要移动它,Async
是合适的,因为它保留了值类别,将左值转发为左值。 (从左值移动会使forward
的调用者感到惊讶,他不会期望左值可以无声地移动。)
当使用类型为Async
的右值调用函数时,参数R
将推导为F
,因此R
为F&&
。在R&&
构造函数中没有任何更改,Task
仍为F&&
,因此构造函数采用绑定到传递给R&&
的右值的右值引用,因此您可以移动它,但Async
也是合适的,因为它保留了值类别,将右值转发为右值。
上周在CppCon上,Herb Sutter宣布“通用参考”的首选术语现在是转发参考,因为它更好地描述了它们的用途。
答案 1 :(得分:1)
ctor 不是通用引用,而是bog标准rvalue-reference或lvalue-reference。你的构造有问题,你不知道哪个,只是它反映了Async
(这可能已经足够了)!
为了成为一个通用引用,必须为该调用推断出类型,而不是之前的某个相关调用。
std::forward
我仍然适合那里,因为外部函数参数确实应该传递给具有保留的移动/复制语义的创建对象。