Search code examples
c++lvaluervalueoverload-resolution

confusion overloading memberfunctions on rvalues and lvalues


I have the following setup:

struct foo
{
    void bar( ) &  { std::cout << "lvalue\n"; }
    void bar( ) && { std::cout << "rvalue\n"; }

    ~foo( ) { bar( ); }
};


int main( int arg, char **argv )
{
    foo{ }.bar();
}

which leads to the output of

rvalue
lvalue

I do not understand the output of the dtor which is always lvalue no matter how hard I try. While I agree that taking the address similar to

(*this).bar( ); // sure enough this will print lvalue

calls the lvalue-overload I do not understand why I can never get an output of rvaluefor the dtor.

I find this strange since the object is an rvalue first and somehow binds to an lvalue before being destructed. What's the matter here?


Solution

  • this is a prvalue, but *this is a lvalue, naturally it will call lvalue ref-qualifiers:

    § 5.3.1 Unary operators [expr.unary.op]
    

    The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.

    From cppreference, this pointer:

    When a non-static class member is used in any of the contexts where the this keyword is allowed (non-static member function bodies, member initializer lists, default member initializers), the implicit this-> is automatically added before the name, resulting in a member access expression (which, if the member is a virtual member function, results in a virtual function call).

    Note: calling this-> is equivalent to calling (*this).

    #include <iostream>
    
    struct foo
    {
        void bar( ) &  { std::cout << "lvalue\n"; x(); }
        void bar( ) && { std::cout << "rvalue\n"; x(); }
    
        void x( ) & { std::cout << "x lvalue\n"; }
        void x( ) && { std::cout << "x rvalue\n"; }
    
        ~foo( ) { bar( ); }
    };
    
    
    int main( int arg, char **argv )
    {
        foo{ }.bar();
    }
    

    Prints:

    rvalue
    x lvalue
    lvalue
    x lvalue
    

    If, for some reason, you really want to call a rvalue function, you could cast the this pointer to a rvalue reference:

    ~foo( ) { static_cast<foo&&>(*this).bar( ); }
    

    Or using std::move:

    ~foo( ) { std::move(*this).bar( ); }