std::forward与std::move详解

在阅读代码时遇到了std::forward与std::move,对这两个方法含义及使用场景有点不太清楚,在此写文章进行记录加深印象。

std::forward

概念

保持传递值的类别不变,顾名思义,即传入一个左值,那么经过forward传递后还是一个左值;传入一个右值,经过forward转换后还是一个右值。它保持了原始实参的值类别,而不是强制进行转换,如果原始实参是左值,它就会转发为左值;如果原始实参为右值,它就会转发为右值。

详解

下面通过两个例子来说明std::forward是如何实现完美转发实参类型的,首先是转发实参类型为左值:

void foo(Widget& w);  //左值引用

template

void wrapper(T&& param)  //此处T&&是一个万能引用

{

    return foo(std::forward(param));

}

Widget w;

wrapper(w);

 上面的例子是一个利用std::forward实现的转换左值引用的例子,下面将详细的类型推导过程整理如下:

  1. wrapper的类型推导:w是一个左值,作为实参传入wrapper后,经过万能引用后推导为Widget&
  2. std::forward的类型推导:

// forward 的定义
template
T&& forward(typename std::remove_reference::type& param);     // 版本1

template
T&& forward(typename std::remove_reference::type&& param);    // 版本2
 

通过上面可以得到std::forward存在两种重载形式,具体的推导过程如下

  • 由前面的步骤可知T被推导为Widget&,那么param的类型变为Widget& &&,经过折叠后变为Widget&,那么param的类型则为Widget&.
  • 通过上面std::forward的定义可知,std::forward首先需要调用std::remove_reference,而T的类型为Widget&,那么通过调用std::remove_reference后,std::forward会生成两种重载的形式,如下所示:

Widget& forward(Widget& param);  //版本1

Widget& forward(Widget&& param);  //版本2

版本1和版本2的含义是在具体的类型下,这里forward的T为Widget&,那么无论是版本1还是版本2则都会返回Widget&(左值引用);同样如果传入的类型T为Widget,那么版本1和版本2都会返回Widget&&(右值引用).

  •  由于wrapper的参数param的类型为Widget&,那么传入调用std::forward后符合版本1的形式所以会采用第一种方法,至此std::forward返回了Widget&类型。
  • foo函数被传入了Widget&符合预期,编译成功。

下面对右值引用的完美调用类型推导进行展开,例子如下:

void foo(Widget&& w);  //左值引用

template

void wrapper(T&& param)  //此处T&&是一个万能引用

{

    return foo(std::forward(param));

}

Widget w;

wrapper(std::move(w));

上面的例子展示了利用std::forward实现右值引用转发的例子,下面对推导过程进行展开:

  1. 由前面的步骤可知T被推导为Widget,那么param的类型变为Widget&&,经过折叠后变为Widget&&,那么param的类型则为Widget&&.
  2. 通过上面std::forward的定义可知,当调用std::forward(param)时,std::forward首先需要调用std::remove_reference,而T的类型为Widget,那么调用std::remove_reference后,std::forward会生成两种重载的形式,如下所示:

Widget&& forward(Widget& param);  //版本1

Widget&& forward(Widget&& param);  //版本2

  •  由于wrapper的参数param的类型为Widget&&,那么传入调用std::forward后符合版本2的形式所以会采用第2种方法,至此std::forward返回了Widget&&类型。
  • foo函数被传入了Widget&&符合预期,编译成功。

使用注意事项

  • 必须是模版参数才可以,比如std::forward(xx)的形式,std::forward(parm)不合法

生效阶段

std::forward在编译阶段生效,在运行时没有开销。

std::move

概念

这是c++种的转移语义,用于将一个左值转换为一个右值引用,他的返回值固定是右值引用。

基本实现

template
typename std::remove_reference::type&& move(T&& t) noexcept {
    return static_cast::type&&>(t);
}

通过上面的代码可以看到,无论是传入的是左值还是右值,move方法的返回值一定是一个右值。

你可能感兴趣的:(c++,算法)