一、通用引用

  • 类型声明形式为 type&&
  • 其中 type 类型是要进行推导的,如果类型推导没有发生,那么 type&& 代表一个右值引用
  • 如果一个对象被声明为 auto&&,这个形参或者对象就是一个通用引用
  • 通用引用,如果它被右值初始化,就会对应地成为右值引用;如果它被左值初始化,就会成为左值引用
  • 如果在一个类模板里面看见了一个函数形参类型为 T&&,也不一定就是通用引用,可能并没有发生类型推导
void f(Widget&& param);             //右值引用
Widget&& var1 = Widget();           //右值引用
auto&& var2 = var1;                 //通用引用(不是右值引用)

template<typename T>
void f(std::vector<T>&& param);     //右值引用

template<typename T>
void f(T&& param);                  //通用引用(不是右值引用)

template <typename T>
void f(const T&& param);            //param是一个右值引用,因为添加了 const 限定符

// 函数模板  一般可能是通用引用
template <typename T> 
void print_reference_type(T &&i) {      // T&& 或是 auto&&
    if (std::is_lvalue_reference<decltype(i)>::value) {
        std::cout << "lvalue: " << i << std::endl;
    } else if (std::is_rvalue_reference<decltype(i)>::value) {
        std::cout << "rvalue: " << i << std::endl;
    } else {
        std::cout << "unknown value: " << i << std::endl;
    }
}

// 因为 push_back 在有一个特定的 vector 实例之前不可能存在,
// 而实例化 vector 时的类型已经决定了 push_back 的声明
// 所以在这里并没有发生类型推导
template<class T, class Allocator = allocator<T>>   //来自C++标准
class vector
{
public:
    void push_back(T&& x);
    
}

一个记录任意函数调用的时间开销的函数模板

auto timeFuncInvocation =
    [](auto&& func, auto&&... params)           // C++14 
                                                // auto&&椒一个通用引用
                                                // params是0个或者多个通用引用
                                                //(即它是个通用引用parameter pack),
                                                //它可以绑定到任意数目、任意类型的对象上
    {
        start timer;
        std::forward<decltype(func)>(func)(     //对params调用func
            std::forward<delctype(params)>(params)...
        );
        stop timer and record elapsed time;
    };

二、右值引用和移动操作

  • 右值引用也是引用,所以和左值引用一样,不能返回局部变量
class B
{
    friend  std::ostream& operator <<(std::ostream& os, B& b);
public:
    B() :s(10), ptr(new int[s])
    {
       std::cout << "default constructor" << std::endl;
       for (int i = 0; i < s; i++)
       {
          ptr[i] = i;
       }
    }
    B(const B& b) :s(b.s)
    {
       std::cout << "B&b" << std::endl;
       ptr = new int[s];
       for (int i = 0; i < s; i++)
          ptr[i] = b.ptr[i];
              
    }
    B(B&& b) :s(b.s), ptr(b.ptr)
    {
        std::cout << "B&& b" << std::endl;
        b.ptr = nullptr;
    }
    ~B()
    {
        std::cout << "~B()" << std::endl;
        if (ptr)
          delete[] ptr;
    }
private:
    int s;
    int* ptr{nullptr};
};
std::ostream& operator <<(std::ostream& os,B& b)
{
    os << "[ ";
    for (int i = 0; i < b.s; i++)
       os << b.ptr[i] << " ";
    os << "]" << std::endl;
    return os;
}
#if 0
B&& f()     // 警告,返回了局部变量的引用
{
    B tmp;
    return std::move(tmp);
}

B& f2()     // 同上
{
    B t;
    return t;
}
#endif
B f()       // 调用移动构造
{
    B tmp;
    return std::move(tmp);
}

B f2()     // 编译器会做优化
{
    B t;
    return t;
}

int main()
{
    B b0 = f2();     // default constructor
                     // ~B()
                     // 这里编译器做了优化,减少了对象的拷贝

    B b1(f());      // default constructor
                    // B&& b
                    // ~B()
                    // ~B()
                    // 这里调用了移动构造,所以直接返回局部对象的方式不一定比返回右值引用的方式慢

    return 0 ;
}