何时需要 ref 限定成员函数

Joh*_*mes 3 c++ ref-qualifier

据我了解,引用限定的成员函数用于区分隐式操作this作为左值与右值。如果我们定义一个没有 ref 限定的成员函数,那么左值和右值都会调用同一个成员函数。例如

class Obj {
public:
  Obj(int x) : x_{x} {}
  int& getVal() {return x_;}
};
Run Code Online (Sandbox Code Playgroud)

在这里,做类似的事情Obj o{2}; o.getVal();Obj{2}.getVal()调用相同的getVal()函数。

但是,我不确定我是否理解这个用例。我似乎看不出我们什么时候可能想要对待与右值不同的左值。我在想也许右值可能被视为临时变量,所以也许我们不想返回对成员变量的引用,而左值可能没问题,因为它们实际上存储在内存地址中并且可以更好地管理。例如,

class Obj {
public:
  Obj(int x) : x_{x} {}
  int& getVal() & {return x_;}
  int getVal() && {return x_;}
};
Run Code Online (Sandbox Code Playgroud)

我相信会返回对左值成员的引用x_,并会返回其右值的副本,Obj因为Obj调用该函数后可能会被删除,并且我们希望保留该返回成员的副本。然而,我想我们也希望有它的右值版本,以防我们要在另一个函数中使用右值引用并且我们想要以某种方式修改该成员。

到目前为止,我对 ref 限定成员函数的解释是否正确?

use*_*522 5

如果一致,那么非非const静态成员函数几乎总是被&- 限定。否则,成员函数和自由函数之间会出现不一致的行为。

假设你有

struct A {
    int i;
    void inc() { i++; }
    void inc2() & { i++; }
};

void inc(A& a) { a.i++; }

int main() {
    inc(A{});   // error
    A{}.inc();  // no error
    A{}.inc2(); // error
}
Run Code Online (Sandbox Code Playgroud)

有了这个你不能做inc(A{}),但你可以做A{}.inc()。你不能做的inc(A{})是语言的有意设计决定,因为你给一个期望修改其参数的函数一个临时值,这可能应该是与调用者相关的副作用,但是当传递一个临时值时该临时文件立即被再次销毁,而调用者无法检查其修改。然而,如果您使用成员函数,则允许您执行完全相同的操作,这有点不一致。

当然,您从不严格需要引用限定符,但有一些用例。

它们可用于禁止我上面提到的内容,以避免向对象参数提供临时值的用户错误。或者有时可能需要要求成员函数只能在右值上调用,在这种情况下,该函数必须是&&- 限定的,例如,因为该函数将从对象中提取资源并将其保留在未指定的状态。

有时,如果在右值上调用成员函数,并且该成员函数将被const限定,则它可能能够窃取类的资源,就像您拥有自由函数的const&重载一样&&。例如std::string::substr,当在非右值上调用时,应该允许它窃取内存资源const,但仍然应该能够对const左值进行操作。

在某些情况下,您希望成员函数将值类别转发给其他函数或作为返回值,在这种情况下,您需要&- 和&&-overload(+ 可能const/volatile变体)来区分赋予成员函数的值类别。请std::optional::value参阅示例。

在 C++23 中,有显式的对象参数,因此可以对最后一个用例使用普通的转发引用。使用显式对象参数,还可以简单地编写一个非静态成员函数,其外观和行为与自由函数完全相同:

struct A {
    int i;
    void inc(this A& a) { a.i++; } // essentially equivalent to void inc() & { i++; }
};

int main() {
    A a{};
    a.inc();    // no error
    A{}.inc();  // error
}
Run Code Online (Sandbox Code Playgroud)