36

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.

alfC
  • 14,261
  • 4
  • 67
  • 118

1 Answers1

24

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.

  • Yes, I understand that. So is `std::move(*this)` the way to use all `&&` functions in the call chain? (I clarified the real question above) – alfC Aug 15 '14 at 22:10
  • @alfC Yes, just like for other function parameters. The caller specified that the object can be safely moved from, but the called function itself also needs to do so, as the compiler cannot figure that out automatically. (Of course, since `this` is your own class type, you could also approach it differently: forward to a private destructive member function that can be called on lvalues. Since it's private, external users can't accidentally call it.) –  Aug 15 '14 at 22:13
  • I don't understand the other approach, using a private destructive (?) member. – alfC Aug 16 '14 at 00:56
  • Just for the sake of exposition, the second call in the example should probably be `std::move( * this ).gun();`. – Potatoswatter Aug 16 '14 at 02:15
  • @alfC Anything `void gun() &&` can do, `void gun()` can do. So if you'd like to call the `void gun() &&` version, what you *could* do instead, is implement `void gunRvalue()`, which is `private` and not generally safe to call on lvalues unless otherwise specified. You can call it from a `public` function like so: `void gun() && { gunRvalue(); }`, and anything else that you *want* to call the `void gun() &&` version, can just call `gunRvalue()` directly. –  Aug 16 '14 at 20:20
  • @Potatoswatter The example is about a hypothetical version of the language in which `gun()` implicitly resolves to the rvalue version. But you're right that in the C++ that we actually have, that's probably what you should do. –  Aug 16 '14 at 20:21
  • I summarized my interpretation of all this as an edit in question. I hope I am interpreting this correctly. – alfC Aug 17 '14 at 04:07
  • @alfC Yes, I think you did interpret that correctly, but I should add that although there is no *need* for rvalue overloads of private member functions, there may be circumstances in which such rvalue overloads make those functions or code more readable or easier to implement. In those cases, of course, do make use of them. –  Aug 17 '14 at 13:42