Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can a member function be called on a temporary but a global function cannot?

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() );
}
like image 736
edA-qa mort-ora-y Avatar asked Dec 28 '14 16:12

edA-qa mort-ora-y


2 Answers

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.


1)
This is a well-known fact, but here is [dcl.init.ref]/5, heavily shortened:

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • If the reference is an lvalue reference and the initializer expression
    • is an lvalue [..]
    • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be implicitly converted to an lvalue of type “cv3 T3,” [..]

  • Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
like image 120
Columbo Avatar answered Oct 03 '22 02:10

Columbo


Temporary cannot bind to non-const reference

step( get() );
//    ~~~~~   Creates a temporary object (r-value)
// But step( ) excepts a non-const reference
like image 23
P0W Avatar answered Oct 03 '22 01:10

P0W