为什么只允许在一个 ref 限定符上重载

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

本文关键字:ref 一个 重载 为什么      更新时间:2023-10-16

显然,不允许在 ref 限定符上重载——如果您删除 &&&(只是标记,而不是它们的函数(,则此代码将无法编译:

#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"
}

换句话说,如果您有两个相同名称和类型的函数,则在定义任何一个函数时都必须定义这两个函数。我认为这是故意的,但原因是什么?例如,为什么不允许为右值调用&&版本(如果已定义(,并且"主要"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
}

重载解析一直以这种方式工作;按引用传递不优于按值传递(反之亦然(。

(ref 限定函数的重载解析就像它是一个普通函数一样,其参数是 *this 的隐式第一个参数;限定 lvalue-ref 就像第一个参数S &const &就像S const &等(

我猜你是说g(s)应该打电话给g(S&)而不是模棱两可。

我不知道确切的基本原理,但是重载解决方案已经足够复杂了,因为它没有添加更多特殊情况(尤其是那些可能默默编译为不是编码人员意图的情况(。

正如您在问题中指出的那样,通过使用两个版本可以轻松避免该问题 S &S && .

您可以同时拥有两者,也可以其中之一。如果实现一个,则没有包含两者的特定要求。

问题是未标有限定符的成员方法(非静态成员(适合与左值和右值一起使用。一旦使用引用限定符重载方法,除非也标记其他方法,否则会遇到歧义问题

在重载解析期间,类 X 的非静态 cv 限定成员函数被视为一个函数,该函数采用 lvalue 类型的隐式参数引用到 cv 限定X(如果它没有 ref 限定符或具有 lvalue ref 限定符(。否则(如果它具有右值引用限定符(,则将其视为采用右值类型的隐式参数引用的函数,以引用 cv 限定的X

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

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

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

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


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

对于非静态成员函数,隐式对象参数的类型为

  • "对 cv X 的左值引用",用于声明没有 ref-qualifier 或 & ref-qualifier 的函数

  • "对 cv X 的右值引用",用于使用 &&ref-限定符声明的函数

让我们从定义一个没有任何 ref 限定符的基本非静态成员函数开始。

§13.3.1 [4]

对于非静态成员函数,隐式对象参数的类型为

— ">对 cv X 的左值引用",用于声明没有 ref-qualifier 或带有 & ref-qualifier 的函数

— "对 cv X 的右值引用",用于使用 &&ref-限定符声明的函数

但是等等,还有更多。

[5] 对于没有 ref 限定符声明的非静态成员函数,附加规则适用:

即使隐式对象参数不是常量限定的,右值也可以绑定到参数,作为 只要在所有其他方面,参数都可以转换为隐式对象参数的类型。

因此

  1. 您只能重载一种引用类型:左值、右值
  2. 不能重载一个或另一个,然后添加另一个不合格的 ref,因为不合格的那个被定义为绑定两种类型,因此存在歧义。