1. 问题
关于右值和左值引用成员函数,通常在一个对象上调用成员函数,而不管对象是一个左值还是一个右值:
string s1 = "a value",s2 = "another";
auto n = (s1 + s2).find('a');
s1 + s2 = "wow";
2. 定义
上面的表达式对右值进行了赋值,为了阻止这种表达,在新标准中,可以强制左侧运算对象 (this) 指向的运算对象是一个左值,方法是在在参数列表后放置一个引用限定符:
class Foo {
public:
Foo& operator=(const Foo&)& {
return *this;
}
Foo& retFoo() {
; //返回一个引用,retFoo调用是一个左值
return *this;
}
Foo retVal() {
; //返回一个值,retVal调用是一个右值
return Foo();
}
};
那么
Foo i, j; //i,j是左值
i = j;
retFoo() = j; //正确,retFoo()返回一个左值
retVal() = j; //错误,retVal()返回一个右值
i = retVal(); //正确
j= retFoo(); //正确
2.1 可使用&&来强制指明"左侧运算对象是一个右值"
inline void StrVec::push_back(std::string &&s)
{
chk_n_alloc(); // reallocates the StrVec if necessary
alloc.construct(first_free++, std::move(s));
}
2.2 类似 const 限定符,引用限定符只能用于非 static 成员函数
2.3 必须同时出现在函数的声明和定义中。
3. const和引用限定符
一个函数可以同时使用 const 和引用限定符,在此情况下,引用限定符必须跟随在const 限定符之后:
class Foo{
public:
Foo someMes() & const; //错误,const限定符必须在前
Foo someMes() const &; //正确,const限定符必须在前
};
任何不会修改数据成员的函数都应该声明为const类型。
4. 重载
4.1 定义
引用限定符可用来区分重载版本,"&& 表示返回是一个右值"。并且可以综合引用限定符和const 来区分一个成员函数的重载版本:
class Foo{
public:
Foo sorted() &&; //可用于改变的右值
Foo sorted() const &; //可用于任何类型的Foo
private:
vector<int> data;
};
//本对象是右值,因此可以原址排序
Foo Foo::sorted() &&
{
sort(data.begin(),data.end());
return *this;
}
//本对象是const或是一个左值,哪种情况我们都不能对其进行原址排序
Foo Foo::sorted() const &
{
Foo ret*(*this); //拷贝一个副本
sort(ret.begin(),ret.end()); //排序副本
return ret; //返回副本
}
retVal.sorted(); //retVal()是一个右值,调用Foo::sorted() &&
retFoo.sorted(); //retFoo()是一个左值,调用Foo::sorted() const &
4.2 使用限制
- 当我们定义 const 成员函数时,可以定义两个版本,唯一的差别是一个版本有const限定,另一个没有。
- 引用限定函数则不一样,如果我们定义两个或者两个以上具有相同名字的和相同参数列表的成员函数,就必须对所有函数都加上引用限定符,或者所有都不加:
class Foo
{
Foo sorted() &&;
Foo sorted() const; //错误,必须加上引用限定符
using Comp = bool (const int&,const int&);
Foo sorted(Comp*); //正确,不同的参数列表
Foo sorted(Comp*); //正确,两个版本都没有使用引用限定符
};