我将添加一些关于@TartanLlama 回答的详细信息为什么您的代码无法编译(即使没有明确的模板参数),以及为什么(在我自己看来)您的代码是 危险的。
下面我将只使用一个简单的类型T而不是你的参数包Args...,因为它解释起来更简单,不改变含义。
关于转发引用的一点提醒...
首先,让我们举一个比你的例子更简单的例子:
template <typename T>
void f (T&&);
现在,让我们从各种来源实例化f,假设具有以下变量:
std::string s;
const std::string cs;
...然后:
f(s); // instanciate f<std::string&>
f(cs); // instanciate f<const std::string&>
f(std::string()); // instanciate f<std::string&&>
您应该想知道:为什么第一个实例是 f<std::string&> 而不是 f<std::string>?,但标准告诉你(§14.8.2.1#3 [temp.deduct.调用]):
如果 P 是转发引用并且参数是
lvalue,类型“对A的左值引用”用于代替A进行类型推导。
回到我们最初的 sn-p!
现在,让我们的例子复杂一点:
template <typename T>
struct A {};
template <typename T>
void f (A<T>, T&&);
还有一个实例化:
std::string s;
A<std::string> as;
f(as, s);
上面和你的例子是等价的,会编译失败,但是为什么...?好吧,如上所述,当你有一个 lvalue 时,T&& 的推导类型是 T&,而不是 T,因此 A<T> 的类型推导失败,因为编译器是期待A<std::string&>,而您正在提供A<std::string>。
所以现在我们知道我们必须执行以下操作:
A<std::string&> ars;
A<std::string const&> acrs;
f(ars, s); // good
f(acrs, cs); // good
为什么危险?
好的,现在应该没问题了:
A<std::string&&> arrs;
f(arrs, std::string());
但它不是...因为当T 被推导出为右值引用时,T 只是T,所以编译器期待A<std::string>。
所以问题来了:你要给一个方法一个rvalue,这个方法将把它转发给一个期望lvalue的函数。这没有错,但可能不是你所期望的。
如何处理?
第一种可能是强制第一个参数的类型不管T的推导类型,例如:
template <typename T>
void f (A<typename std::remove_reference<T>::type>, T&&);
但请注意:
- 你必须添加更多的东西来处理
const。
- 当第一个参数的类型固定(至少在您的情况下)时,您可能想知道
T&& 的用处。
第二种可能(警告:我不知道这是不是标准的!)是将第一个参数移到最后,然后从t推断类型:
template <typename T>
void f (T &&t, A<decltype(std::forward<T>(t))>);
现在,T 的推断类型与 A 的预期类型完全匹配。
不幸的是,我不知道如何使用可变参数模板进行上述操作...