Search code examples
c++c++11thismove-semanticslvalue

Is std::move(*this) a good pattern?


In order to make this code with C++11 reference qualifiers work as expected I have to introduce a std::move(*this) that doesn't sound right.

#include<iostream>
struct A{
    void gun() const&{std::cout << "gun const&" << std::endl;}
    void gun() &&{std::cout << "gun&&" << std::endl;}
    void fun() const&{gun();}
    void fun() &&{std::move(*this).gun();} // <-- is this correct? or is there a better option
};

int main(){
    A a; a.fun(); // prints gun const&
    A().fun(); // prints gun&&
}

Something doesn't sound right about it. Is the std::move necessary? Is this a recommended use for it? For the moment if I don't use it I get gun const& in both cases which is not the expected result.

(It seems that *this is implicit and lvalue reference always, which makes sense but then the only way to escape to use move)

Tested with clang 3.4 and gcc 4.8.3.


EDIT: This is what I understand from @hvd answer:

  1. std::move(*this) is syntactically and conceptually correct

  2. However, if gun is not part of the desired interface, there no reason to overload the lv-ref and rv-ref version of it. And two functions with different names can do the same job. After all the ref-qualifiers matters at the interface level, which is generally only the public part.

struct A{
private:
    void gun() const{std::cout << "gun const&" << std::endl;}
    void gun_rv(){std::cout << "gun called from fun&&" << std::endl;}
public:
    void fun() const&{gun();}
    void fun() &&{gun_rv();} // no need for `std::move(*this)`.
};

But again, if gun is part of the (generic) interface then std::move(*this) is necessary, but only then. And also, even if gun is not part of the interface there readability advantages in not splitting the function gun as two differently named function and the cost of this is, well..., std::move(*this).

EDIT 2: In retrospect this is similar to the C++98 case of const and no-const overload of the same function. In some cases it makes sense to use const_cast (another form of cast) to not repeat code and have the two functions with the same name (https://stackoverflow.com/a/124209/225186). ... although in more complicated cases it makes sense to have auxiliary private functions that delegate the correct behavior for the interface function.


Solution

  • Yes, *this is always an lvalue, no matter how a member function is called, so if you want the compiler to treat it as an rvalue, you need to use std::move or equivalent. It has to be, considering this class:

    struct A {
      void gun() &; // leaves object usable
      void gun() &&; // makes object unusable
    
      void fun() && {
        gun();
        gun();
      }
    };
    

    Making *this an rvalue would suggest that fun's first call to gun can leave the object unusable. The second call would then fail, possibly badly. This is not something that should happen implicitly.

    This is the same reason why inside void f(T&& t), t is an lvalue. In that respect, *this is no different from any reference function parameter.