【问题标题】:Why is overloading on just one ref-qualifier not allowed?为什么不允许仅对一个 ref-qualifier 进行重载?
【发布时间】:2016-02-14 08:43:40
【问题描述】:

显然,不允许在 ref-qualifiers 上重载 - 如果您删除 &&&(只是标记,而不是它们的函数),此代码将无法编译: p>

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

换句话说,如果您有两个具有相同名称和类型的函数,则必须定义两者中的任何一个。我认为这是故意的,但原因是什么?为什么不允许,比如说,如果定义了右值,则调用 &amp;&amp; 版本,并在以下变体中的所有其他内容上调用“主要”f()(反之亦然 - 尽管这会令人困惑):

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

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

【问题讨论】:

  • 根据其他重载更改 void f() {...} 的含义听起来令人困惑。
  • 在实践中从未见过&amp;/&amp;&amp; 限定符。
  • @Lingxi,一种用途是将陷阱变成编译器错误。例如,人们可能会错误地使用const char* str = getStr().c_str();。您可以使用它来将其转换为编译器错误。不幸的是,目前还没有使它万无一失的方法(即,那里有一个错误,但仍然可以在foo(getStr().c_str()); 中工作)。我知道它正在研究标准提案。另请参阅this question 了解用途。
  • 您不必同时定义两者。只有一个很好。例如。 void f() &amp;&amp; 没有 f 的任何其他定义
  • 由于 [over.match.funcs] 13.3.1\5.3,没有 ref-qualifier 的成员函数就像 lvalue 上的双管霰弹枪 s 和 rvalue s 参数。因此,当您声明一个成员函数 with 任何双胞胎 ref-qulifier 时,必须删除该霰弹枪,因为引入了歧义。

标签: c++ c++11 overloading language-lawyer rvalue


【解决方案1】:

和下面的情况没有什么不同:

struct S {};

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

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

重载解决方案一直都是这样工作的;按引用传递并不比按值传递更可取(反之亦然)。

(ref-qualified 函数的重载解析就好像它是一个带有隐式第一个参数的普通函数,其参数是*this;lvalue-ref 限定就像第一个参数S &amp;const &amp; 就像@ 987654325@等)

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

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

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

【讨论】:

  • 感谢提议的编辑。如果我在有人拒绝之前看到它,我会接受 :)
  • 可以自己加吗?我认为这是对您的好答案的宝贵补充。
【解决方案2】:

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

要注意的是,没有用限定符标记的成员方法(非静态成员)适用于左值和右值。 一旦你用 ref-qualifier 重载了一个方法,除非你也标记其他方法,否则你会遇到歧义问题

在重载决议期间,类 X 的非静态 cv 限定成员函数被视为一个函数,如果它没有 ref-qualifiers 或如果它具有左值引用限定符。否则(如果它具有右值引用限定符),它被视为一个函数,该函数采用右值引用类型的隐式参数引用 cv 限定 X

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

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

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

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


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

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

  • “对 cv X 的左值引用”适用于未使用 ref-qualifier 或 & ref-qualifier 声明的函数

  • 使用 && ref-qualifier 声明的函数的“对 cv X 的右值引用”

【讨论】:

  • 很好的解释。但是为什么这个例子有歧义呢?
  • @YamMarcovic。非限定成员对参数进行了适当的隐式限定,因此,在存在显式限定成员的情况下,它们是“等效的”......我添加了来自 cppreference 站点的引用。
  • @YamMarcovic 要回答这个问题,您可能对 13.3.3.2\3.2.3 感兴趣; 13.3.3\1.
  • @EugeneZavidovsky :) 评论后我打开了标准并进行了探索。因此,我的回答。
【解决方案3】:

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

§13.3.1 [4]

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

—“对 cv X 的左值引用”用于声明没有引用限定符或使用 & 引用限定符

的函数

—“对 cv X 的右值引用”,用于使用 && ref-qualifier 声明的函数

但是等等,还有更多。

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

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

因此

  1. 可以重载一种引用类型:lvaluervalue
  2. 不能重载一个或另一个,然后再添加另一个不是 ref 限定的,因为不是 ref 限定的那个被定义为绑定两种类型 ,因此产生歧义。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-09
    • 2013-06-12
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多