c++ std::forward

std::forward通常是用于完美转发的,它会将输入的参数原封不动地传递到下一个函数中,这个“原封不动”指的是,如果输入的参数是左值,那么传递给下一个函数的参数的也是左值;如果输入的参数是右值,那么传递给下一个函数的参数的也是右值。一个经典的完美转发的场景是:

template <class... Args>
void forward(Args&&... args) {
    f(std::forward<Args>(args)...);
}

说明样例:

// forward example
#include <utility>      // std::forward
#include <iostream>     // std::cout

// function with lvalue and rvalue reference overloads:
void overloaded (const int& x) {std::cout << "[lvalue]";}
void overloaded (int&& x) {std::cout << "[rvalue]";}

// function template taking rvalue reference to deduced type:
template <class T> void fn (T&& x) {
  overloaded (x);                   // always an lvalue
  overloaded (std::forward<T>(x));  // rvalue if argument is rvalue
}

int main () {
  int a;

  std::cout << "calling fn with lvalue: ";
  fn (a);
  std::cout << '\n';

  std::cout << "calling fn with rvalue: ";
  fn (0);
  std::cout << '\n';

  return 0;
}

答案:

calling fn with lvalue: [lvalue][lvalue]
calling fn with rvalue: [lvalue][rvalue]

需要注意的有2点:

1、输入参数的类型是Args&&... , &&的作用是引用折叠,其规则是:

&& && -> &&
& && -> &
& & -> &
&& & -> &

由于我们需要保留输入参数的右值属性,因此Args后面需要跟上&&;

2、std::forward的模板参数必须是< Args>,而不能是< Args...>,这是由于我们不能对Args进行解包之后传递给std::forward,而解包的过程必须在调用std::forward之后.

那么std::forward为什么可以达到这一一个作用呢,我们看一下其实现代码:

template<class T>
constexpr T&& forward(std::remove_reference_t<T>& arg) noexcept{
    // forward an lvalue as either an lvalue or an rvalue
    return (static_cast<T&&>(arg));
}

template<class T>
constexpr T&& forward(std::remove_reference_t<T>&& arg) noexcept{
    // forward an rvalue as an rvalue
    return (static_cast<T&&>(arg));
}

std::remove_reference_t是一个模板类的类型别名,用于去掉T的引用属性(但不会去掉const属性,const属性可以用std::remove_const_t来去掉);

如果forward接受的参数为左值的话,它将其转化成右值返回,这样既可以传给左值,又可以传给右值;如果传递的参数是个右值的话,它将其保留右值属性返回,这样只可以返回给右值。

文章目录