Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why specifying a lvalue reference for *this on a member function is different from not specifying anything?

Tags:

c++

c++11

Consider this code:

#include <iostream>
using namespace std;

struct A {
    void f() { cout << "A::f" << endl; }
    void f() const { cout << "A::f const" << endl; }
};

struct B {
    void f() & { cout << "B::f &" << endl; }
    void f() const & { cout << "B::f const &" << endl; }
};

A getA() { return A{}; }
B getB() { return B{}; }

int main() {
    getA().f();
    getB().f();
}

which prints

A::f
B::f const &

For B, the const overload gets selected, not the non-const one. I guess this means that specifying a lvalue ref qualifier for *this is different from not specifying anything at all. Why is that? Does the "implicit this argument" change type and the const overload now becomes a better candidate in overload resolution?

like image 258
peppe Avatar asked May 22 '15 09:05

peppe


2 Answers

In this case:

struct A {
    void f() { cout << "A::f" << endl; }
    void f() const { cout << "A::f const" << endl; }
};

getA().f();

Both overloads are viable for f, but the non-const one is preferred because it requires no conversion.

But in this case:

struct B {
    void f() & { cout << "B::f &" << endl; }
    void f() const & { cout << "B::f const &" << endl; }
};

getB().f();

The first overload requires this to be an lvalue. But in getB().f(), the result of getB() is a prvalue, which can't bind to a non-const lvalue. So this overload is not viable and not selected.

The second overload however requires this to be a const lvalue, which a prvalue can bind to: this overload is viable and selected by the compiler.

like image 109
user703016 Avatar answered Nov 08 '22 13:11

user703016


For reference, the sentence that totally misled me is in [over.match.funcs], §13.3.1/4 of N3337:

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

(emphasis mine).

So I was getting kind of crazy about why there was a difference in the output. Without or with a & ref qualifier should be the same according to here, right?

Well, the reason is an additional rule sneaked in afterwards in §13.3.1/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.

Which basically triggers the non-const rvalue to non-const lvalue conversion and makes all the difference in the example above. D'uh.

like image 22
peppe Avatar answered Nov 08 '22 12:11

peppe