I've used this on "builder" type objects where I build up by calling methods on some kind of factory thing (that maybe builds up vectors, unique_ptrs etc) and then to "build" I make it an `&&` r-value function that then can move all the configured pieces into a single, final object. Then one has to call `std::move(builder).build();` which makes it clear(ish) that the builder is now "finished"
I've played with similar concepts. The trick I used is that the "builder" is only available through a factory method and it's return in an "RvalueOnly" wrapper object. All the builder calls are forwards via an overloaded "operator->" that is an r-value function, so you're never allowed to get an lvalue version of the builder. This does make it so you have to chain all the calls together which you may not want, but it doesn't require a std::move, which feels unintuitive to me. Feel free to disagree though.
If I'm not mistaken, for one-off building you can achieve the same effect by constructing the builder in place, e.g. Builder{}.x(1).y(2).build(). (build() is called with an r-value reference of the builder object).
I think builder objects make sense if they are doing non-trivial work as they are build in steps (including checking arguments). Otherwise a skeleton object with just parameters is enough {.x = 1, .y = 2}. i could be missing something though. in any case, moving “at the end” is a great idea.
I've used this on a tensor class for the assignment operator. The lvalue assignment is just the default of assigning a different tensor object to the value, but assignment to an rvalue meant broadcast assignment into the tensor, allowing a slice operator to return a temporary tensor that shared the original memory and then broadcast assignment into that temporary slice.
I used reference qualified functions extensively to implement a builder DP. It allowed me to get rid of the explicit .build() at the end. Made everything much much more readable.
I think the main problem to be solved here is that getters often return an l-value reference to the member for performance reasons or in the case of a non-const member to have a non-const l-value reference to be able to change the member (one special form of getters is the index operator). The problem now occurs if you do this on a temporary object. Overloading the member function for r-values helps to prevent this. (I believe that by now a reference to a member extends the lifetime of the temporary object. Then, much of what I said is not an issue anymore.)
Всегда рассказываю об этом студентам, не понимаю, почему об этом так мало говорят! Наверное, так же редко, как о указателях на член класса, типа `int Car::* pSpeed = &Car::speed;`
As a C++ dev you must know that explicitness is the most important thing there is. Your comment seems pretty comprehensible after translation, so why don't you just use any translator available to you (if you're too lazy to translate your comment yourself) to provide an explicit translation of your thoughts instead of relying on implicit auto-translated option?
No, std::move is kind of a misnomer. It doesn't actually move anything, it just does a static cast to T&&. So if you std::move a const object it will just return the object as const T&& but the internal state will not have changed at all by std::move
@@evo6789 And, pretty importantly, if you have a copy constructor as well as a move constructor (that takes a non-const rvalue) the copy constructor will be valid (rvalue -> lvalue) while the move constructor won't (because the const can't be removed). So you end up with a copy.
@@evo6789 You're right. So it would be impossible to do `std::move(obj).set_value(...)`, but `std::move(obj).get_value()` is fine because it calls the `get_value() const &&` overload.