27

While working with ref-qualified function overloads, I'm getting different results from GCC (4.8.1) and Clang (2.9 and trunk). Consider the following code:

#include <iostream> #include <utility> struct foo { int& bar() & { std::cout << "non-const lvalue" << std::endl; return _bar; } //~ int&& bar() && //~ { //~ std::cout << "non-const rvalue" << std::endl; //~ return std::move(_bar); //~ } int const& bar() const & { std::cout << "const lvalue" << std::endl; return _bar; } int const&& bar() const && { std::cout << "const rvalue" << std::endl; return std::move(_bar); } int _bar; }; int main(int argc, char** argv) { foo().bar(); } 

Clang compiles it and outputs "const rvalue", while GCC thinks this is an ambiguous call with the two const-qualified functions both being best viable candidates. If I provide all 4 overloads, then both compilers output "non-const rvalue".

I would like to know which compiler --if any-- is doing the right thing, and what are the relevant standard pieces in play.

Note: The reason this actually matters is that the real code declares both const-qualified functions as constexpr. Of course, there is no output to std::cout and static_cast is used instead of std::move, so that they are valid constexpr definitions. And since in C++11 constexpr still implies const, the overload commented out in the sample code cannot be provided as it would redefine the const-qualified rvalue overload.

1 Answer 1

35

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.

Sign up to request clarification or add additional context in comments.

3 Comments

An rvalue reference is a better match in all cases. This is likely a bug with GGC's ref-qualifiers.
Also, this answer might be relevant. ;)
Thank you for your detailed answer. I wish I could upvote it more than once!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.