2015-10-16 55 views
5

当我有看起来像这样的代码:完美转发和模板

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; 
} 

输出是:
“无效F(T & & arg):可以修改\ n“;
“void f(T & & 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 &)。该版本不能修改参数。
“In f(MemoryBlock & &)。此版本可以修改参数。

它在我看来,只有通过将功能从模板更改为非模板,才能完全改变右值引用的演绎规则。
如果有人会向我解释这个问题,那真是太棒了。

+1

你通过误导以为模板参数'最好的选择T'映射到'MemoryBlock'。 – Walter

+0

有趣的是,你在标题中有“完美转发”,但你根本没有做任何转发。 – Mehrdad

回答

4

当您使用T&&时,这不是右值引用,而是通用参考参数。他们以相同的方式宣布,但他们的行为有所不同。

当您删除模板参数时,您不再处于可推论的上下文中,它实际上是右值引用:当​​然,它们仅绑定到右值。

在可推论的上下文中(即发生类型推演时),T&&可以是左值引用左值引用。通用引用可以绑定到几乎所有组合(const,const volatile等),并在您的情况下:const T&

现在你的思路是对rvalues和左值是有效的和重载的,但是会发生什么是在推导模板参数时通用引用超载更好地匹配。因此,它将通过const T&过载来选择。

通常,您只想保留通用参考函数并将其与std::forward<T>()配对以完善参数(s)。这消除了您的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

+2

@Artur另请参阅[参考折叠规则](https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers)。 – vsoftco

1

T&&可以被称为universal/forwarding参考。
参考塌陷规则:

  1. 甲& &成为A &
  2. 甲& & &成为A &
  3. 甲& & &成为A &
  4. 甲& & & &成为A & &

template<typename T> void foo(T&&);

在此,以下适用:

  1. 当FOO上称为类型A的左值,则T解析为&和 因此通过上述参考折叠规则,参数类型 实际上变成A &。
  2. 当在类型A的右值上调用foo时,T解析为A,并且 因此参数类型变为A & &。

在你的情况:

template<class T> void f_(T&& arg); 
f_(block); //case 1 
f_(MemoryBlock()); //case 2 

在情况1:
T = MemoryBlock中&则T & &换成T & & & ==>给出Ť&
在情况2:
T = MemoryBlock然后T & &变成T & & ==>给牛逼& &

对于这两种情况下

template<class T> void f_(T&& arg) 

是编译器,因此,反而采取其

template<class T> 
void f_(const T& arg)