寻求对 std::forward 的澄清

Posted

技术标签:

【中文标题】寻求对 std::forward 的澄清【英文标题】:Seeking clarification on std::forward 【发布时间】:2018-08-19 07:23:37 【问题描述】:

参考std::forward

对于所有重载,返回类型已指定为T&&(忽略constexpr)。

但在以下示例所附的描述中:

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

    // arg is always lvalue
    foo(std::forward<T>(arg)); // Forward as lvalue or as rvalue, depending on T

如果对 wrapper() 的调用传递了一个右值 std::string,则 T 为 推导出为 std::string(不是 std::string&、const std::string& 或 std::string&&) 和 std::forward 确保右值引用是 传递给 foo。

如果对 wrapper() 的调用传递了一个 const 左值 std::string,然后将 T 推导出为 const std::string&,并且 std::forward 确保将 const 左值引用传递给 foo。

如果对 wrapper() 的调用传递了非常量左值 std::string,则 T 被推导出为 std::string&,并且 std::forward 确保一个非常量 左值引用被传递给 foo。

在第一个之后的上述两个实例中,一个左值引用而不是一个右值引用(正如T&amp;&amp; 所暗示的那样,这种理解是否正确?)已被记录为传递给 foo。

如果上面的理解是正确的,为什么返回值被指定为T&amp;&amp;

【问题讨论】:

T&amp;&amp; 并不总是意味着右值引用。我建议阅读 Scott Meyers 的文章"Universal References in C++11"。 看看reference collapsing是如何工作的。 您认为返回值还应该指定为什么? 【参考方案1】:

有区别

void f1(int&& a) 

template<class T>
void f2(T&& a) 

第一个版本定义f1 来处理右值。另一方面,第二个版本是一个模板函数,接受一个通用(或在某些引用中,转发)引用作为其参数。

要了解std::forward 的机制,您应该使用不同的参数调用f2,如下所示:

#include <iostream>

template <class T> void f2(T &&a)  std::cout << __PRETTY_FUNCTION__ << '\n'; 

int main() 
  int a5;

  f2(5);
  f2(a);

  return 0;

当你编译代码时,比如g++,你会从你的程序中得到以下输出:

./a.out
void f2(T &&) [T = int]
void f2(T &&) [T = int &]

如您所见,在第一次调用中T 被推断为int,而在第二次调用中推断为int &amp;。由于参考折叠规则,正如您的问题的 cmets 中已经提到的那样,T &amp;&amp; 会给您T,而T&amp; &amp;&amp; 会给您T&amp;。简而言之,您观察 T&amp;&amp; 作为返回类型并不意味着函数返回右值引用。实际上,在模板函数中,&amp;&amp; 就像一个身份运算符。当与T&amp;结合时,它会给你T&amp;;否则,它会给你T

Arthur O'Dwyer 在 CppCon2016 上有一个really nice talk 讨论这个话题。或许你可以看看它来了解模板类型推导规则,这将有助于你理清std::forward的行为。

【讨论】:

以上是关于寻求对 std::forward 的澄清的主要内容,如果未能解决你的问题,请参考以下文章

std :: move和std :: forward之间有什么区别

寻求澄清有关弱类型语言的明显矛盾

寻求对内联命名空间的澄清

C++ STL应用与实现7: 如何使用std::forward_list 单链表 (since C++11)

理解std::move和std::forward

为啥 std::forward_list::empty 有 [[nodiscard]] 而 std::forward_list::max_size 没有? [复制]