为什么不允许仅在一个 ref-qualifier 上重载?

Why is overloading on just one ref-qualifier not allowed?

显然,不允许重载 ref-qualifiers – 如果删除 &&&(只是令牌,而不是它们的功能):

#include <iostream>

struct S {
    void f() &  { std::cout << "Lvalue" << std::endl; }
    void f() && { std::cout << "Rvalue" << std::endl; }
};

int main()
{
    S s;
    s.f();   // prints "Lvalue"
    S().f(); // prints "Rvalue"
}

换句话说,如果您有两个同名同类型的函数,如果您定义其中一个,则必须同时定义这两个函数。我认为这是故意的,但是原因是什么?为什么不允许调用 && 版本的右值(如果它已定义),以及 "primary" f() 在以下变体中的所有其他内容(反之亦然 - 尽管这会令人困惑):

struct S {
    void f()    { std::cout << "Lvalue" << std::endl; }
    void f() && { std::cout << "Rvalue" << std::endl; }
};

换句话说,让它们的行为类似于关于主模板的模板特化。

跟下面的情况没什么区别:

struct S {};

void g(S s);
void g(S& s);

int main()
{
    S s;
    g(s);     // ambiguous
}

过载解决方案一直都是这样工作的;按引用传递不如按值传递(反之亦然)。

(引用限定函数的重载解析就像一个普通函数一样,带有一个隐式第一个参数,其参数是 *this;左值引用限定就像第一个参数 S &const & 就像 S const & 等)

我猜你是说g(s)应该调用g(S&)而不是模棱两可。

我不知道确切的原理,但重载决议已经足够复杂了,因为它没有添加更多的特殊情况(尤其是那些可能会静默编译为不是编码器预期的情况)。

正如您在问题中所指出的,使用 S &S && 这两个版本可以轻松避免该问题。

您可以同时拥有两者,也可以两者兼有。 There is no specific requirement to include both if you implement the one.

要注意的是,未标记限定符的成员方法(非静态成员)适用于左值和右值。 一旦你用引用限定符重载一个方法,除非你也标记其他方法,否则你 运行 会陷入歧义问题

During overload resolution, non-static cv-qualified member function of class X is treated as a function that takes an implicit parameter of type lvalue reference to cv-qualified X if it has no ref-qualifiers or if it has the lvalue ref-qualifier. Otherwise (if it has rvalue ref-qualifier), it is treated as a function taking an implicit parameter of type rvalue reference to cv-qualified X.

所以基本上,如果您有一种合格的方法(例如,对于左值 &)和一种不合格的方法,则规则是这样的,它们实际上都是合格的,因此是模棱两可的。

类似的原理适用于 const 限定符。您可以实现一个方法,一个 "version" 用于 const 对象,一个用于非 const 对象。标准库容器就是很好的例子,特别是 begin()end() 和其他与迭代器相关的方法。

一个特定的用例是当对象是临时(或过期)对象和不是临时(或过期)对象时应用于方法的逻辑不同。如果您知道生命周期即将结束,您可能希望在内部优化掉某些调用和数据处理。

另一个是将方法的使用限制为左值。如果实体即将过期或者是临时的,则某个应用程序或对象逻辑可能没有意义或没有用。


来自 §13.4.1/4 的标准(取自 N4567 草案)中的措辞是:

For non-static member functions, the type of the implicit object parameter is

  • “lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier

  • “rvalue reference to cv X” for functions declared with the && ref-qualifier

让我们从定义不带任何 ref 限定符的基本非静态成员函数意味着什么开始。

§13.3.1 [4]

For non-static member functions, the type of the implicit object parameter is

— "lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier

— "rvalue reference to cv X” for functions declared with the && ref-qualifier

等等,还有更多。

[5] For non-static member functions declared without a ref-qualifier, an additional rule applies:

even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as in all other respects the argument can be converted to the type of the implicit object parameter.

因此

  1. 可以 仅对一种引用类型进行重载:lvalue, rvalue
  2. 不能重载一个或另一个,然后再添加另一个不符合 ref 限定的,因为不符合 ref 限定的定义为绑定两种类型,因此存在歧义。