完美的转发和模板

时间:2015-10-16 14:01:50

标签: c++ rvalue-reference

当我有一个看起来像这样的代码时:

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;)中。此版本可以修改参数。”;

在我看来,只有通过将功能从模板更改为非模板,才能完全改变右值参考的演绎规则。
如果有人向我解释,那真是太有意义了。

2 个答案:

答案 0 :(得分:4)

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

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

在可推导的上下文中(即在进行类型推导时),T&&可以是右值引用左值引用。通用引用可以绑定到几乎所有组合(constconst 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引用 参考折叠规则:

  1. A和&安培;成为A&amp;
  2. A和&安培;&安培;成为A&amp;
  3. A&安培;&安培; &安培;成为A&amp;
  4. A&安培;&安培; &安培;&安培;成为A&amp;&amp;
  5. template<typename T> void foo(T&&);

    以下适用:

    1. 当在类型A的左值上调用foo时,则T解析为A&amp;和 因此,通过上面的参考折叠规则,参数类型 实际上变成了A&amp;。
    2. 当在类型A的右值上调用foo时,则T解析为A,和 因此论证类型变为A&amp;&amp;。
    3. 在你的情况下:

      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)