右值引用
通过使用&&来绑定右值引用,右值引用只能绑定到一个将要销毁的对象,所以可以自由的将右值引用的资源移动到另一个对象中
左值通常代表对象的身份,右值代表对象的值
不能将右值引用绑定到另一个右值引用类型的变量上。因为变量实际上属于左值

标准库move函数
可以通过调用move获得绑定到左值的右值引用,函数定义在utility中

int &&rr3 = std::move(rr1);//可以使用

当使用了move,相当于我们对编译器承诺除了对rr1赋值或者销毁时,我们将不再使用它

移动构造函数和移动赋值运算符

StrVec::StrVec(StrVec &&s) noexcept : elements(s.elements), first_free(s.first_free), cap(s.cap)
{
    s.elements = s.first_free = s.cap = nullptr;
}

noexcept通知标准库不抛出任何异常,如果不使用noexcept控制异常,一旦在移动过程中抛出了异常,编译器可能就会认为不安全,使用noexcept可以显式的告诉编译器此函数是安全的

移动赋值运算符

StrVec &StrVec::operator=(StrVec &&rhs) noexcept
{
    if (this != &rhs)
    {
        free();
        elements = rhs.elements;
        first_free = rhs.first_free;
        cap = rhs.cap;
        rhs.elements = rhs.first_free = rhs.cap = nullptr;
    }//使用if进行自赋值处理
    return *this;

合成的移动操作
如果一个类定义了自己的拷贝构造函数,就不会合成移动构造函数,编译器会使用拷贝构造来匹配移动构造
只有当类没有定义任何版本的拷贝控制成员,且非静态数据成员都可以移动时,编译器才会合成

移动和拷贝的匹配优先级
按照普通的函数匹配机制,拷贝构造一般形参为const,所以可以用于一切能转换成StrVec的参数,而移动构造只能适用于传入右值的情况,拷贝赋值运算符和移动复制运算符也是如此,如果没有移动构造,则全部使用拷贝构造

更新三五法则
一般来说,如果一个类定义了任何一个拷贝操作,它就应该定义所有五个操作(拷贝构造,移动构造,移动赋值运算符,拷贝构造运算符,析构函数)

移动迭代器
移动迭代器可以改变给定迭代器的解引用行为来发挥移动而不拷贝的效果,一般迭代器返回指向元素的左值,而移动迭代器返回右值引用。
通过调用标准库的make_move_iterator函数将一个普通迭代器转换为移动迭代器。此函数接受一个迭代器参数,返回一个移动迭代器,从而可以让算法不拷贝只移动。但是并不能确保每个算法都可以使用移动迭代器。

右值和左值引用成员函数
标准库允许向右值赋值,例如:

string s1 = "a value", s2 = "another";
auto n = (s1 + s2).find('a');;
s1 + s2 = "wow!";//此处对两个string的连接结果进行了赋值

可以通过在参数列表加入引用限定符来避免这种情况(与定义const成员函数相同)

class Foo
{
public:
    Foo &operator=(const Foo&) &;//只能向可修改的左值赋值
};
Foo &Foo::operator=(const Foo &rhs) &
{
    return *this;
}

引用限定符只能用于非静态函数,必须同时出现在函数声明和定义中。对于&限定的函数只能用于左值,&&限定的函数只能用于右值。函数可以同时限定const和引用限定,const写在前面。

重载和引用函数
引用限定符可以用来区分重载版本。
当使用类似于排序的手段时,右值对象没有其他的用户,可以进行原址排序,左值不能改变对象,就要先进行拷贝再排序。
定义两个或两个以上相同名字参数的函数,要么都加引用限定,要么都不加。