当我有一个看起来像这样的代码时:
template<class T>
void f_(const T& arg)
{
cout << "void f(const T& arg): Cannot modify\n";
}
template<class T>
void f_(T&& arg)
{
cout << "void f(T&& arg): Can modify\n";
}
主要是我称之为:
int main()
{
MemoryBlock block;
f_(block);
f_(MemoryBlock());
return 0;
}
输出结果为:
“void f(T&amp;&amp; arg):可以修改\ n”;
“void f(T&amp;&amp; arg):可以修改\ n”;
但是当我将此代码更改为非泛型时,而不是函数模板,我将使用常规函数,
void f(const MemoryBlock&)
{
cout << "In f(const MemoryBlock&). This version cannot modify the parameter.\n";
}
void f(MemoryBlock&&)
{
cout << "In f(MemoryBlock&&). This version can modify the parameter.\n";
}
输出更“直观”:
“在f(const MemoryBlock&amp;)中。此版本无法修改参数。”;
“在f(MemoryBlock&amp;&amp;)中。此版本可以修改参数。”;
在我看来,只有通过将功能从模板更改为非模板,才能完全改变右值参考的演绎规则。
如果有人向我解释,那真是太有意义了。
答案 0 :(得分:4)
当您使用T&&
时,这不是右值参考,那是一个通用参考参数。他们以相同的方式宣布,但他们的行为方式不同。
当您删除模板参数时,您不再处于可推导的上下文中,并且它实际上是一个右值引用:当然,它们仅绑定到右值。
在可推导的上下文中(即在进行类型推导时),T&&
可以是右值引用或左值引用。通用引用可以绑定到几乎所有组合(const
,const volatile
等),在您的情况下:const T&
。
现在你的思路是为rvalues和lvalues提供高效和重载,但是在推导模板参数时,通用引用重载是更好的匹配。因此,它将在const T&
重载之上被选中。
通常,您只想保留通用引用函数并将其与std::forward<T>()
配对以完善转发参数。这样就不需要const T&
重载,因为通用参考版本将接管。
请注意,仅仅因为您在可导出的背景下看到&&
,并不意味着它是一个普遍的参考; &&
需要附加到推断的类型上,所以这里有一个实际上是右值引用的例子:
template<class T>
void f_(std::vector<T>&& arg) // this is an rvalue reference; not universal
{
cout << "void f(T&& arg): Can modify\n";
}
以下是关于此事的精彩演讲:https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11
答案 1 :(得分:1)
T&&
可以称为universal/forwarding
引用
参考折叠规则:
template<typename T> void foo(T&&);
以下适用:
在你的情况下:
template<class T> void f_(T&& arg);
f_(block); //case 1
f_(MemoryBlock()); //case 2
案例1:
T = MemoryBlock&amp; T&amp;&amp;成为T&amp; &安培;&安培; ==&GT;给T&amp;
在案例2中:
T = MemoryBlock然后T&amp;&amp;成为T&amp;&amp; ==&GT;给T&amp;&amp;
对于这两种情况
template<class T> void f_(T&& arg)
是编译器的最佳选择,因此取而代之的是
template<class T>
void f_(const T& arg)