寻求对 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&&
所暗示的那样,这种理解是否正确?)已被记录为传递给 foo。
如果上面的理解是正确的,为什么返回值被指定为T&&
?
【问题讨论】:
T&&
并不总是意味着右值引用。我建议阅读 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 &
。由于参考折叠规则,正如您的问题的 cmets 中已经提到的那样,T &&
会给您T
,而T& &&
会给您T&
。简而言之,您观察 T&&
作为返回类型并不意味着函数返回右值引用。实际上,在模板函数中,&&
就像一个身份运算符。当与T&
结合时,它会给你T&
;否则,它会给你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::forward_list::empty 有 [[nodiscard]] 而 std::forward_list::max_size 没有? [复制]