Reputation: 703
I'm confused by the following code:
struct test {
void f() & {
std::cout << "&" << std::endl;
}
void f() const& {
std::cout << "const&" << std::endl;
}
void f() && {
std::cout << "&&" << std::endl;
}
void f() const&& {
std::cout << "const&&" << std::endl;
}
void g() & {
std::cout << "& -> ";
f();
}
void g() const& {
std::cout << "const& -> " ;
f();
}
void g() && {
std::cout << "&& -> ";
f();
}
void g() const&& {
std::cout << "const&& -> ";
f();
}
test() {} //allow default const construction
};
int main(int, char**) {
test value;
const test constant;
value.g();
constant.g();
std::move(value).g();
std::move(constant).g();
}
When I compile with clang 3.5 I get this output:
& -> &
const& -> const&
&& -> &
const&& -> const&
Why is the r-value qualifier being dropped here? And is there any way to call f
from g
with the right qualifier?
Upvotes: 12
Views: 1212
Reputation: 119124
The call f()
is interpreted as (*this).f()
. The result of dereferencing a pointer is always an lvalue, so *this
is an lvalue and the lvalue-qualified function is called.
This behaviour even makes sense, at least to me. Most of the time the object referred to by an rvalue expression will either be destroyed at the end of the full-expression (a temporary) or at the end of the current scope (an automatic local variable that we choose to std::move
). But when you're inside a member function, neither of those is true, so the object should not treat itself as an rvalue, so to speak. This is also why it makes sense for the name of an rvalue reference function parameter to be an lvalue within the function.
If you want the rvalue-qualified f
to be called, you can do this:
std::move(*this).f();
Upvotes: 11
Reputation: 60979
ref-qualifiers affect the implicit object parameter, §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-qualifierwhere
X
is the class of which the function is a member and cv is the cv-qualification on the member function declaration.
Overload resolution is, roughly speaking, performed on the object argument to object parameter conversion just as any other argument->parameter conversion.
However, f()
is transformed into (*this).f()
(§9.3.1/3), and *this
is an lvalue. §5.3.1/1:
The unary
*
operator performs indirection: […] and the result is an lvalue referring to the object or function to which the expression points.
Hence the overloads of f
with the &
qualifier are preferred - in fact, the rvalue overloads are entirely ignored since initializers of rvalue object references must be rvalues (last bullet point in §8.5.3/5). Also, non-const references are preferred over const ones in overload resolution (§13.3.3.2/3, last bullet point concerning standard conversions).
Upvotes: 2
Reputation: 361352
It is because dereferencing1 this
always produces an lvalue, irrespective of the fact that it is pointing to a temporary object or not.
So when you write this:
f();
it actually means this:
this->f(); //or (*this).f()
//either way 'this' is getting dereferenced here
So the overload of f()
which is written for lvalue is invoked from g()
— and const-correctness is applied accordingly as well.
Hope that helps.
1. Note that this
is a prvalue; only after dereferencing it produces an lvalue.
Upvotes: 3
Reputation: 13698
Well, for f()
the right qualifier is always lvalue reference
since you use it on this
. All your f
calls are nothing else than this->f()
and this
is always lvalue
. It doesn't matter that for the outside world the object is rvalue
this
is lvalue for the object insides.
Upvotes: 1