成员函数声明末尾的& 是一个ref 限定符。它适用于调用成员函数的对象值,并约束该值的值类别:
- 可以对任何值调用不带 ref 限定符的函数。
- 带有
& 限定符的函数只能在左值上调用。
- 带有
&& 限定符的函数只能在右值上调用。
ref 限定符影响重载决议,例如不匹配的实例值的重载是不可行的。
标准库并没有太多使用 ref 限定符(我只能想到std::optional),所以我们自己编一个例子:
struct X {
explicit X(int n) : s_(n, 'a') {}
std::string s_;
const char* f1() const { return s_.c_str(); }
const char* f2() const & { return s_.c_str(); }
const char* f3() const && { return s_.c_str(); }
};
现在考虑以下调用:
int main() {
X x(10);
x.f1(); // OK
X(20).f1(); // OK
x.f2(); // OK
X(20).f2(); // ill-formed
x.f3(); // ill-formed
X(20).f3(); // OK
}
该示例还说明了为什么此功能可能有用:当成员函数返回对对象自身某些内部部分的引用时,重要的是该内部引用不会超过对象的生命周期。如果您的成员函数不合格,那么您很容易引入终身错误。例如:
const char* s = std::string("abcde").c_str(); // dangling pointer!
改进此类“内部状态访问”API 的一种方法是为不同的 ref 限定重载创建不同的返回值(类别)。回到std::optional,参与访问基本上归结为这组重载:
struct MyOptional {
T value_; // assume engaged!
T& get() & { return value_; }
T&& get() && { return std::move(value_); }
};
这意味着MyOptional::get 在可选的左值上调用时返回一个左值,在右值上调用时返回一个右值(实际上是一个 xvalue)。这意味着,给定MyOptional x;,绑定T& r = x.get(); 是允许的,但T& r = MyOptional().get(); 是不允许的,同样T&& r = x.get(); 是不允许的,但T&& r = std::move(x).get() 是允许的。