【问题标题】:When not to use std::forward with r-values?何时不将 std::forward 与 r 值一起使用?
【发布时间】:2012-08-16 10:42:22
【问题描述】:

什么情况下不需要std::forward?它用于包装内部函数参数,即模板右值(即,它可以是左值或命名右值)。喜欢在:

template<class T>
void outer(T&& t) { 
    inner(std::forward<T>(t)); 
}

我猜一种情况是内部函数参数按值传递。还有其他情况吗?我在写std::begin(std::forward&lt;Ct&gt;(ct)) 时遇到了这个问题,其中 Ct 是模板右值引用。

编辑关于可能的重复

如果我没记错的话,这是第三次尝试关闭这个 4 年的老问题,因为一些不懂这个问题的新手重复了这个问题。

“使用 forward 的好处?”和“什么时候不使用带有 r 值的 std::forward?”是非常不同的问题。首先是为初学者介绍 r 值,其次是为高级 C++ 用户讨论完美转发。我是meta-template library 和 lambda 库的作者,他们不需要对基础知识进行详细描述。答案中的信息与其他问题非常不同。

【问题讨论】:

  • 你的问题是倒退的。 不需要 需要 std::forward 的情况有无数种。我认为您确实要求澄清何时准确使用 std::forward 。但是这个问题已经存在了。
  • @DDrmmr -- 这是不将std::forward 与 r 值一起使用的时候。我在标题中添加了说明。
  • @DDrmmr -- 只是想添加到我之前的评论中。这是关于完美转发,而不是关于 r 值基础。

标签: c++ c++11 parameter-passing perfect-forwarding


【解决方案1】:

使用 std::forward (在模板化函数的形参上,其类型为 T&& 形式,T 是模板的类型参数):

  • 当内部函数调用是最后一次使用外部函数中的参数时,并且
  • 内部函数的可能重载可能会将参数作为右值引用,也可能不会。

基本原理是,当一个对象可能通过右值引用作为参数传递时(这是您使用 std::forward 所允许的),它的信息内容可能会被破坏。因此,只有当您确定不再使用该信息内容时,您才想这样做。

例子:

#include <utility>

template <class T> void f(T &&t);
template <class T> void g(const T &t);

template <class T>
void outer(bool f_first, T &&t)
  {
    if (f_first)
      {
        f(t);
        g(t);
      }
    else
      {
        g(t);
        f(std::forward<T>(t));
      }
  }

#include <string>

void foo(std::string s)
  {
    outer(true, s);
    outer(true, s + "x");
    outer(false, s);
    outer(false, std::move(s));
  }

【讨论】:

    【解决方案2】:

    我正在回答我自己的问题,因为到目前为止我还没有得到满意的答案。 如果我会对此进行哪怕很小的改进/补充 - 我会选择您的答案作为已接受。

    一般std::forward如果能达到完美转发的效果,对内部功能是有利的。否则就是多余的了。

    仅当以下任何一项为真时,才使用 std::forward 包装内部函数 arg:

    • 内部函数参数是templated-rvalue-ref(现在称为“转发引用”);
    • 内部函数有多个重载,根据参数 r/l-valueness 进行区分;
    • 内部函数有多个重载,这些重载根据常量区分左值参数;

    【讨论】:

    • 这句话让人迷惑:“内部函数参数是templated-rvalue-ref”——当你有一个转发引用时,它是一个与“rvalue-ref”完全不同的概念,称其为rvalue-ref 是一个混乱的来源,尽管它们都使用&amp;&amp; 语法。也许正确的答案是,“当您将转发引用传递给另一个函数调用时使用std::forward,否则不要使用它”?
    • @ChrisBeck -- 术语“转发引用”于 2014 年引入。这是 2012 年的问题。表达式“模板化右值引用”定义为“转发引用”(根据 n4164)。我将尝试改写我的答案以使其更清楚。
    【解决方案3】:

    当模板参数类型包含值类别时,可以进行完美转发。 (如果这句话没有意义,请花一分钟时间familiarize yourself with the problem at hand。)

    给定:

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

    foo 的正文中,T 将采用UU&amp; 的形式。前者意味着我们传递了一个右值,后者意味着我们传递了一个左值。我们可以像这样转发这个事实:

    template <typename T>
    void foo(T&& x)
    {
        bar(std::forward<T>(x));
    }
    

    将左值传递给foobar 得到相同的左值。将右值传递给foobar 获取右值。

    如果你不能区分一个值类别,那么转发是没有用的。仅当您具有以与上述相同的方式推导出的模板参数时,它才有用。所以是的,它在这里没有用:

    template <typename T>
    void foo(const T& x)
    {
        // if this was called as foo(1), we're none the wiser
    }
    

    【讨论】:

    • @LeonidVolnitsky:我不明白。哪一部分不清楚?你完全了解什么是转发吗?
    • 我明白什么是完美转发。您的两个答案(这个和链接的)都非常清楚并且很好地解释了转发的基本知识(并且都得到了我的 +1)。但不幸的是,他们没有回答我的问题。某些类型的内部函数不关心价值参数的类型。对于他们来说,使用std::forward 不会买任何东西。在我的问题中,我试图找到所有这些类型。
    • @LeonidVolnitsky:抱歉,我无法添加更多内容,因为我没有看到您要问的内容。您似乎明白转发什么时候得不到任何东西,所以只需看看您将要调用的函数,看看它是否可以工作。
    猜你喜欢
    • 2021-08-29
    • 2013-06-30
    • 2018-05-30
    • 1970-01-01
    • 2016-07-08
    • 2015-05-03
    • 2013-11-01
    • 2017-01-18
    • 1970-01-01
    相关资源
    最近更新 更多