Overload resolution with ref-qualifiers
Firstly, the implicit object parameter is treated as a normal parameter as per 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
where X is the class of which the function is a member and cv is the cv-qualification on the member
function declaration.
So what you are asking is equivalent to the following:
void bar(foo&);
void bar(foo&&);
void bar(const foo&);
void bar(const foo&&);
int main()
{
bar(foo());
}
The expression foo()
is a class prvalue.
Secondly, the non-const lvalue reference version is not viable, as a prvalue cannot bind to it.
This leaves us with three viable functions for overload resolution.
Each has a single implicit object parameter (const foo&
, foo&&
or const foo&&
), so we must rank these three to determine the best match.
In all three case it is a directly bound reference binding. This is described in declarators/initialization (8.5.3).
The ranking of the three possible bindings (const foo&
, foo&&
and const foo&&
) is described in 13.3.3.2.3:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
- S1 and S2 are reference bindings and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier [this exception doesn't apply here, they all have ref-qualifiers], and S1 binds an rvalue reference to an rvalue [a class prvalue is an rvalue] and S2 binds an lvalue reference.
This means that both foo&&
and const foo&&
are better then const foo&
.
- S1 and S2 are reference bindings, and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.
This means that foo&&
is better than const foo&&
.
So Clang is right, and it is a bug in GCC. The overload ranking for foo().bar()
is as follows:
struct foo
{
int&& bar() &&; // VIABLE - BEST (1)
int const&& bar() const &&; // VIABLE - (2)
int const& bar() const &; // VIABLE - WORST (3)
int& bar() &; // NOT VIABLE
int _bar;
};
The bug in GCC seems to apply purely to implicit object parameters (with ref-qualifiers
), for a normal parameter it seems to get the ranking correct, at least in 4.7.2.
Why is overloading on just one ref-qualifier not allowed?
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 &&
.
Overload resolution of template methods with ref-qualifiers
MSVC is wrong here.
The relevant rule is [over.load]/2.3:
Member function declarations with the same name and the same parameter-type-list as well as member function template declarations with the same name, the same parameter-type-list, and the same template parameter lists cannot be overloaded if any of them, but not all, have a ref-qualifier ([dcl.fct]).
Here the function templates have different template parameters (int I
and class Q
), so this rule does not apply, and there is no other rule stoping them from overloading.
call of overloaded with ref-qualifiers member function is ambiguous
g++ 4.8.2 has the same problem with the even simpler (Live at coliru):
struct A {
auto f() & {}
auto f() && {}
};
int main() {
A{}.f();
A a;
a.f();
}
despite the program obviously being correct. It appears to be a bug in the interaction between ref-qualifiers and return type deduction: presumably the deduction process is stripping the qualifiers from the implicit object argument before handing them off to overload resolution.
I have reported this as GCC bug 60943.
Why do ref-qualifier together with cv-qualifier on operator overloading allow rvalue assignment?
Because rvalues could be bound to lvalue-reference to const
. Just same as the following code:
foo& r1 = foo(); // invalid; rvalues can't be bound to lvalue-reference to non-const
const foo& r2 = foo(); // fine; rvalues can be bound to lvalue-reference to const
BTW: The overload qualified with rvalue-reference wins in overload resolution when calling on rvalues. That's why you mark it as delete
explicitly works as expected.
Overloading a parent member function without ref-qualifier with a child member function with ref-qualifier in C++
GCC is correct to accept this, but the situation changed recently. The current phrasing is that a using-declaration in a class ignores (base-class) declarations that would be ambiguous (in a sense that is more strict than for overload resolution, partly because there is no argument list yet) with other declarations in the class. void()
and void() &
members are ambiguous in this sense, so b.f
finds only B
’s f
and the call is valid.
In previous (as of this writing, that means “published”) versions of the standard, both functions would be made available because the &
distinguished them (in a sense that is even stricter), which would not only render the call ambiguous (as Clang says) but be ill-formed outright because the base- and derived-class functions were checked for overload compatibility which they lack.
rvalue ref-qualifiers for STL containers
There isn't a particular problem with adding ref-qualifiers to everything that returns references, however that basically doubles the number of members, which will generally have identical implementations apart from wrapping the return in std::move
.
class A
{
int a;
int& operator[](std::size_t pos) & { return a; }
int&& operator[](std::size_t pos) && { return std::move(a); }
};
The standard library has declined to provide these overloads, in the same way that it has declined to provide many volatile
overloads. In this case, you can just std::move
the &
value, where you need it.
If you are writing your own containers, then there is no safeness reason to avoid such overloads. It does increase the maintenance burden, so I would advise against it.
Related Topics
How to Return Array from C++ Function to Python Using Ctypes
Do Static Members of a Class Occupy Memory If No Object of That Class Is Created
C++: How to Get Fprintf Results as a Std::String W/O Sprintf
Comparing Std::Functions for Equality
Parameter Name Omitted, C++ VS C
Strange Ambiguous Call to Overloaded Function Error
Library Is Linked But Reference Is Undefined
How to Check If a Key Is Pressed on C++
C++ Template Metaprogramming - How to Output the Generated Code
How to Find a Particular Value in an Array and Return Its Index
Why Is Rvo Disallowed When Returning a Parameter
Do C++11 Regular Expressions Work with Utf-8 Strings
What Is the Purpose of Ref-Qualified Member Functions
Calculating and Printing Factorial at Compile Time in C++
C++11 Virtual Destructors and Auto Generation of Move Special Functions