In the below code I call step as a member function and as a global function on a temporary value. The member function is allowed, and works, whereas the global function is disallowed due to invalid initialisation of non-const reference of type ‘kludge&’ from an rvalue of type ‘kludge’.
I'm trying to understand, from a language perspective, why one behaviour is allowed and the other is not. Technically both calls and functions seem like they'd be compiled identically, or at least could be.
#include <iostream>
struct kludge {
int a;
kludge() {
a = 1;
}
kludge & step() {
a++;
std::cout << a << ",";
return *this;
}
};
kludge get() {
kludge t;
return t;
}
kludge & step( kludge & t ) {
t.a++;
std::cout << t.a << ",";
return t;
}
int main() {
get().step();
step( get() );
}
You cannot bind rvalues to non-const lvalue references1. That applies to step(get()) as the parameter of step, which is a non-const lvalue reference, cannot be bound to the prvalue (pure rvalue) get().
However, member functions can per se be called on object arguments of every value category, be it lvalue or rvalue - [over.match.funcs]/4 and /5:
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[..]
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. [ Note: The fact that such an argument is an rvalue does not affect the ranking of implicit conversion sequences (13.3.3.2). — end note ]
But if you use so-called ref-qualifiers, you can restrict the value categories that are valid for a particular member function. That is, if you write:
kludge & step() & { /* .. */ }
The call get().step() will be ill-formed too.
A reference to type “cv1
T1” is initialized by an expression of type “cv2T2” as follows:
- If the reference is an lvalue reference and the initializer expression
- is an lvalue [..]
- has a class type (i.e.,
T2is a class type), whereT1is not reference-related toT2, and can be implicitly converted to an lvalue of type “cv3T3,” [..]
- Otherwise, the reference shall be an lvalue reference to a non-volatile
consttype (i.e., cv1 shall beconst), or the reference shall be an rvalue reference.
Temporary cannot bind to non-const reference
step( get() );
// ~~~~~ Creates a temporary object (r-value)
// But step( ) excepts a non-const reference
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With