1. std::forawrd

std::forward<T>(arg) 能够实现完满转发,即如果 arg 是一个右值援用,则转发之后后果仍是右值援用;反之,如果 arg 是一个左值援用,则转发之后后果仍是左值援用.

#include <iostream>struct BigObject{    char data[1 << 10];};void g(BigObject& o){    std::cout << "lvalue reference\n";}void g(BigObject&& o){    std::cout << "rvalue reference\n";}template <typename T>void f(T&& arg){    g(std::forward<T>(arg));}int main(){    BigObject o;    f(o);    f(BigObject());}
lvalue referencervalue reference

2. 为什么须要完满转发?

  • 在函数模板编程中,常有一种场景是应用模板参数去调用另一个函数(如,上例中 f 去调用 g),这时候如果只提供值传递版本会显得效率太低。
  • 函数的参数个别会尽可能地设为援用类型,以防止对象拷贝带来的昂扬开销.
  • 为了使一个函数既能够承受左值,又能够承受右值,C++11 之前的解决方案是将参数类型设为 const Type&. 但这并不是很不便,如限度了参数是常量.
  • 如果函数 g 既提供了左值援用版本和右值援用版本,则最好的状况是函数 f 能够依据参数类型去调用相应版本的 g. 而完满转发正能够满足此要求.

3. 援用折叠规定

  • 右值援用和右值援用叠加将失去右值援用;
  • 右值援用和左值援用叠加将失去左值援用;
  • 左值援用和左值援用叠加将失去左值援用.
template <typename T>using TR = T&;                           // v 的类型TR<int> v;                 // int&TR<int>& v;                // int&TR<int>&& v;               // int&
template <typename T>using TRR = T&&;                            // v 的类型TRR<int> v;                 // int&&TRR<int>& v;                // int&TRR<int>&& v;               // int&&

4. 完满转发的原理

template< class T >T&& forward( typename std::remove_reference<T>::type& t ) noexcept;template< class T >T&& forward( typename std::remove_reference<T>::type&& t ) noexcept;

std::remove_reference<T> 的作用就是去掉 T 中的援用,它是通过模板特化来实现:

template< class T > struct remove_reference      {typedef T type;};template< class T > struct remove_reference<T&>  {typedef T type;};template< class T > struct remove_reference<T&&> {typedef T type;};

根据上述援用折叠规定,如果 Tint&,则 T&& 即为 int&;反之,如果 Tint&&,则 T&&int&&.

5. forwarding reference

上例函数 f 中的 T&& 实际上被称为 forwarding reference. 它是一种非凡类型的援用,它保留了函数参数的值类别(category),使得能够通过 std::forward 来转发它.

forwarding reference 包含以下两种:

  • 在函数模板中,没有被 constvolatile 润饰的、申明为右值援用的类型形参:

    template<class T>int f(T&& x) {                    // x is a forwarding reference  return g(std::forward<T>(x)); // and so can be forwarded}template<class T>int g(const T&& x); // x is not a forwarding reference: const T is not cv-unqualified
  • auto&&,但如果它跟着一个花括号括起的初始值列表,则它不是 forwarding reference:

    auto&& vec = foo();       // foo() may be lvalue or rvalue, vec is a forwarding referenceg(std::forward<decltype(vec)>(vec)); // forwards, preserving value categoryauto&& z = {1, 2, 3}; // *not* a forwarding reference (special case for initializer lists)for (auto&& x: f()) {// x is a forwarding reference; this is the safest way to use range for loops}