【问题标题】:Why is std::forward needed when using forwarding references?为什么在使用转发引用时需要 std::forward?
【发布时间】:2020-12-28 06:05:11
【问题描述】:

转发引用定义如下: “转发引用是一种特殊的引用,它保留函数参数的值类别,从而可以通过 std::forward 转发它。” (https://en.cppreference.com/w/cpp/language/reference)

通过“保留函数参数的值类别”,我理解类型为转发引用的参数变量的名称与函数调用中的值类别相同。 例如:

template<typename T>
void wrapper(T&& forwarded_argument) {
    some_function(forwarded_argument);
}

wrapper(5);
wrapper(my_int);

这里,第一次调用中的表达式5是prvalue,所以据我理解,some_function(forwarded_argument);中的表达式forwarded_argument(只是变量的名称)也应该是prvalue。 在第二次调用中,名称my_int 是一个左值,所以我希望some_function(forwarded_argument); 中的表达式forwarded_argument 是一个左值。 这将使这行写成std::forward&lt;T&gt;(forwarded_argument) 不必要,因为在这两种情况下,根据我传递给wrapper 的内容,无论如何都会调用some_function 的正确重载。

我也认为我不完全理解 std::forward 的实际作用。这是我找到的一个简化实现 (The implementation of std::forward):

template <class T>
inline T&& forward(typename std::remove_reference<T>::type& t) noexcept {
    return static_cast<T&&>(t);
}

template <class T>
inline T&& forward(typename std::remove_reference<T>::type&& t) noexcept {
    static_assert(!std::is_lvalue_reference<T>::value, "Can not forward an rvalue as an lvalue.");
    return static_cast<T&&>(t);
}

正如 cppreference 所述 (https://en.cppreference.com/w/cpp/language/value_category),对对象类型(例如 static_cast&lt;char&amp;&amp;&gt;(x))的右值引用的强制转换表达式是一个 xvalue。我没有看到任何提及x 的值类别,所以我假设整个转换表达式的值类别无论如何都是相同的。函数调用表达式的值类别,其返回类型是对对象的右值引用,是一个 xvalue。由此看来,无论我将什么传递给std::forward,对std::forward 的调用的值类别最终都会是一个xvalue。

这里的错误在哪里?从通常的转发方式来看,问题的两个部分(关于转发引用和关于std::forward)的结论似乎都不正确。

【问题讨论】:

  • _forwarded_argument 始终是左值。见:Rvalues, lvalues and formal definitions
  • 那么定义中的“保留函数参数的值类别”部分到底是什么意思,如果不是这样呢?
  • 意思是:std::forward&lt;T&gt;(arg) 与参数具有相同的值类别。但是arg 本身没有。
  • wrapper 的调用中的&lt;int&gt; 不应该存在,这是我的疏忽,我将其编辑了。

标签: c++ templates c++17 c++20


【解决方案1】:

这里的错误在哪里?

来了

这里,第一次调用中的表达式5是prvalue,据我理解,some_function(forwarded_argument);中的表达式forwarded_argument(只是变量的名称)也应该是prvalue。

引用函数参数的id-expression(具体来说是一个普通的标识符)是一个左值。我们需要对其进行强制转换以获得函数参数的值类别。并且std::forward 根据推导的模板参数中的编码值类别提供正确的转换。

引用变量绑定到参数(无论值类别如何)。因此,它现在作为一个名称,经验法则是具有名称的事物是左值1


1 - 只是一个经验法则。这是大多数情况下的一般行为。并且足以了解转发的工作原理。

【讨论】:

    猜你喜欢
    • 2017-07-08
    • 1970-01-01
    • 2012-10-08
    • 2018-07-30
    • 2011-11-07
    • 1970-01-01
    • 2017-11-05
    • 2011-07-19
    • 2014-12-20
    相关资源
    最近更新 更多