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

时光怂恿深爱的人放手 提交于 2021-02-07 11:30:43

问题


Apparently, overloading on ref-qualifiers is not allowed – this code won't compile if you remove either & or && (just the tokens, not their functions):

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

In other words, if you have two functions of the same name and type, you have to define both if you define either. I assume this is deliberate, but what's the reason? Why not allow, say, calling the && version for rvalues if it's defined, and the "primary" f() on everything else in the following variation (and vice-versa – although that would be confusing):

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

In other words, let them act similar to template specializations with respect to the primary template.


回答1:


It's not any different to the following situation:

struct S {};

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

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

Overload resolution has always worked this way; passing by reference is not preferred to passing by value (or vice versa).

(Overload resolution for ref-qualified functions works as if it were a normal function with an implicit first parameter whose argument is *this; lvalue-ref qualified is like a first parameter S &, const & is like S const & etc.)

I guess you are saying that g(s) should call g(S&) instead of being ambiguous.

I don't know the exact rationale, but overload resolution is complicated enough as it is without adding more special cases (especially ones that may silently compile to not what the coder intended).

As you note in your question, the problem can be easily avoided by using the two versions S & and S &&.




回答2:


You can have both, or either. There is no specific requirement to include both if you implement the one.

The catch is that a member method (non-static member) that is not marked with a qualifier, is suitable for use with both lvalues and rvalues. Once you overload a method with a ref-qualifier, unless you mark the others as well, you run into ambiguity issues.

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.

So basically, if you have one method that is qualified (e.g. for an lvalue &) and one that is not qualified, the rules are such that they are effectively both qualified and hence ambiguous.

Similar rationale is applied to the const qualifier. You can implement a method and have one "version" for a const object, and one for a non-const object. The standard library containers are good examples of this, in particular the begin(), end() and other iterator related methods.

One particular use case is when the logic applied to the method is different between when the object is a temporary (or expiring) object and when it is not. You may wish to optimise away certain calls and data processing internally if you know the lifetime is about to end.

Another is to limit the use of a method to lvalues. A certain piece of application or object logic may not make sense or be useful if the entity is about to expire or is a temporary.


The wording in the standard (taken from the draft N4567) from §13.4.1/4 is:

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




回答3:


Let's start with what it means to define a basic non-static member function without any ref qualifiers.

§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

But wait, there's more.

[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.

Therefore

  1. You can overload for just one ref type: lvalue, rvalue
  2. You can't overload one or the other and then also add in another that's not ref qualified, because the one that's not ref qualified is defined to bind both types, and hence the ambiguity.


来源:https://stackoverflow.com/questions/35389911/why-is-overloading-on-just-one-ref-qualifier-not-allowed

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!