Cpp11 左值右值

相关概念

  • 左值:等号左边,可被取地址,有标识符。
  • 右值:等号右边,不可被取地址,没有标识符。
  • 纯右值:运算表达式产生的临时变量、不和对象关联的原始字面量、非引用返回的临时变量、lambda表达式等都是纯右值。
  • 将亡值:即将要被销毁的值。例如返回类型为对象的右值引用的函数调用或重载运算符表达式(std::move(x)
  • 左值引用:对左值的引用。( Int &)
  • 右值引用:最右值的引用(int &&)
  • 移动语义:移交资源的所有权,移交后原对象不再拥有资源(std::move
  • 完美转发:可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参。
  • 返回值优化:当函数需要返回一个对象实例时候,就会创建一个临时对象并通过复制构造函数将目标对象复制到临时对象,这里有复制构造函数和析构函数会被多余的调用到,有代价,而通过返回值优化,C++标准允许省略调用这些复制构造函数。

Cpp11新增加右值引用。

右值引用,顾名思义,对右值的引用。

int &&var = 42; //字面常量是右值。

int a = 4;
int &&c = std::move(a); // ok move 将左值变为右值。
const int &var = 42; //ok

移动语义

移交资源的所有权,减少调用拷贝构造函数带来的开销,提高程序性能。

所有的STL都实现了移动语义

std::vector<int> vecints = {1,2,3,4,5,6,7,8};
auto vecmove = std::move(vecints);


//vecints.size() is zero
for (auto item:vecints)  // 'vecints' used after it was moved
{
  cout<< item<<endl;  // 
}

移动语义仅针对于那些实现了移动构造函数的类的对象,对于那种基本类型int、float等没有任何优化作用,还是会拷贝,因为它们实现没有对应的移动构造函数。

完美转发

完美转发指可以写一个接受任意实参的函数模板,并转发到其它函数,目标函数会收到与转发函数完全相同的实参,转发函数实参是左值那目标函数实参也是左值,转发函数实参是右值那目标函数实参也是右值。

include <iostream>
#include <memory>
#include <utility>

struct A {
    A(int &&n) { std::cout << "rvalue overload, n=" << n << "\n"; }
    A(int &n) { std::cout << "lvalue overload, n=" << n << "\n"; }
};

class B {
public:
    template<class T1, class T2, class T3>
    B(T1 &&t1, T2 &&t2, T3 &&t3) : a1_{std::forward<T1>(t1)},
                                   a2_{std::forward<T2>(t2)},
                                   a3_{std::forward<T3>(t3)} {
    }

private:
    A a1_, a2_, a3_;
};

template<class T, class U>
std::unique_ptr<T> make_unique1(U &&u) {
    return std::unique_ptr<T>(new T(std::forward<U>(u)));
}

// ... 为不定长参数列表
template<class T, class... U>
std::unique_ptr<T> make_unique2(U &&... u) {
    return std::unique_ptr<T>(new T(std::forward<U>(u)...));
}

int main() {
    auto p1 = make_unique1<A>(2);// 右值
    int i = 1;
    auto p2 = make_unique1<A>(i);// 左值

    std::cout << "B\n";
    auto t = make_unique2<B>(2, i, 3);
}