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

时间:2016-02-14 08:43:41

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

显然,不允许在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"
}

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

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

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

3 个答案:

答案 0 :(得分:9)

以下情况没有任何不同:

struct S {};

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

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

过载分辨率一直都是这样的;通过引用传递不是优先传递值(反之亦然)。

(ref-qualified函数的重载解析就好像它是一个带有隐式第一个参数的正常函数,其参数为*this; lvalue-ref qualified就像第一个参数S &,{{ 1}}就像const &等。)

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

我不知道确切的基本原理,但是重载决策很复杂,因为它没有添加更多特殊情况(特别是那些可能无声编译而不是编码器所期望的情况)。

正如您在问题中所述,使用g(S&)S &这两个版本可以轻松避免此问题。

答案 1 :(得分:4)

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

问题是未使用限定符标记的成员方法(非静态成员)适合与左值和右值一起使用。 一旦使用ref-qualifier重载方法,除非你也标记其他方法,否则会遇到模糊问题

  

在重载解析期间,类X的非静态cv限定成员函数被视为一个函数,如果没有引用,则将带有lvalue类型的隐式参数引用到cv-qualified X -qualifiers或者如果它具有左值ref-qualifier。否则(如果它有rvalue ref-qualifier),它被视为一个函数,它采用类型为rvalue的隐式参数引用cv-qualified X

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

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

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

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

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

  

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

     
      
  • 对于没有引用限定符或使用&amp;而声明的函数的“对cv X的左值引用” REF-限定符

  •   对于用&amp;&amp ;;声明的函数,
  • “对cv X的rvalue引用” REF-限定符

  •   

答案 2 :(得分:1)

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

  

§13.3.1[4]

     

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

     对于声明没有引用限定符或使用&amp; ref-qualifier

的函数,

- “左值引用cv X ”      

- 使用&amp;&amp;&ref-qualifier

声明的函数的“rvue对cv X的引用”

但等等,还有更多。

  

[5] 对于未使用ref-qualifier声明的非静态成员函数,适用其他规则:

     

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

因此

  1. 只能重载一种参考类型:lvaluervalue
  2. 不能重载一个或另一个,然后添加另一个不是ref限定的,因为那个不是ref限定的那个被定义为绑定这两个类型 ,因此含糊不清。