万能引用和完美转发

万能引用

万能引用是 C++11 引入的一种特殊引用类型,其语法形式是 T&&,但只有在模板参数推导时,才能成为万能引用。

显式类型不会触发万能引用。

特点:

  • 左值可以匹配 T&,右值匹配 T&&
  • 类型推导时,T 可能是 U&(左值)或 U(右值)
1
2
3
4
5
6
7
8
9
template <typename T>
void func(T&& arg);

// 传递左值
int x = 42;
func(x); // T -> int&,arg 变为 int& && -> int& (万能引用匹配左值)

// 传递右值
func(42); // T -> int,arg 变为 int&& (万能引用匹配右值)

完美转发

完美转发是指保持参数原始的左值/右值属性,将其传递给另一个函数。它主要依赖 std::forward

1
2
3
4
template <typename T>
void forwarder(T&& arg) { // arg 是万能引用
process(std::forward<T>(arg)); // 完美转发
}

remove_reference

适用于非引用类型,如 intdoublestd::string 等。

type 直接定义为 _Tp,即保持原类型不变。

1
2
3
template<typename _Tp>
struct remove_reference
{ typedef _Tp type; };

适用于左值引用 T&,它将 T& 去掉引用,变为 T

1
2
3
template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp type; };

适用于右值引用 T&&,它将 T&& 去掉引用,变为 T

1
2
3
template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp type; };

forward 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 左值版本
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{
return static_cast<_Tp&&>(__t);
}

// 右值版本
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
static_assert(!std::is_lvalue_reference<_Tp>::value,
"template argument substituting _Tp is an lvalue reference type");

return static_cast<_Tp&&>(__t);
}

接受左值版本:

  • typename std::remove_reference<_Tp>::type& __t 表示把接受到的参数的引用去除得到原始类型 T,原始类型 T 再加上后面的 &,成为左值引用 T&,故而接受左值,称为左值版本
  • 强转为_Tp&&(右值引用),并返回

由于 _TP 是 T&,那么替换过来 T& && --> T&,确保传进来是左值,传出去还是左值。

 

接受右值版本:

  • typename std::remove_reference<_Tp>::type&& __t 表示把接受到的参数的引用去除得到原始类型 T,原始类型 T 再加上后面的 &&,成为右值引用 T&&,故而接受右值,称为右值版本
  • 断言判断防止传递左值,只接受右值
  • 强转为_Tp&&(右值引用),并返回

由于 _TP 是 T&&,那么替换过来 T&& && --> T&&,确保传进来是右值,传出去还是右值。

move 源码

1
2
3
4
5
6
template<typename _Tp>	
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{
return static_cast<typename std::remove_reference<_Tp>::type&&>(__t);
}
  • _Tp 是泛型参数,可以接受左值或右值
  • 先用 std::remove_reference 去掉 _Tp 的引用,再加上 &&,使其变成右值引用
  • 使用 static_cast 强制转换 __t 为右值引用

std::move 总是转换为右值,不会区分原本是左值还是右值。