I wrote this simple test to overload based on whether this is an rvalue or an lvalue. The original version had addition cases, but I reduced it down the one that’s puzzling me.
#include <iostream>
struct A {
int pp=0;
A(int p) : pp(p) {}
A &prop(int nn) & {pp=nn; return *this;}
A &&prop(int nn) && {pp=nn; return std::move(*this);}
};
struct B {
B(const A &aa) {std::cout << "COPY: " << aa.pp << std::endl;}
B(A &&aa) {std::cout << "MOVE: " << aa.pp << std::endl;}
};
int main() {
A aa(99);
B b4(aa.prop(33).prop(4)); // 4 COPY
std::cout << "4 == " << aa.pp << std::endl;
return 0;
}
The output is what I expected
COPY: 4
4 == 4
So far, so good. Then I modified A to use a method that deduces this.
struct A {
int pp=0;
A(int p) : pp(p) {}
template <typename self> A prop(this self &&me, int nn) {
me.pp=nn;
return me;
}
};
I expected to get the same result, but I don’t:
MOVE: 4
4 == 33
-
Why does the second version call the move method?
-
Even though it calls the move method, I would still expect to see nn set to 4 and not 33. What’s going on?
>Solution :
In the deduced this case you always return a A object so
aa.prop(33).prop(4)
assigns 33 to aa.pp and the prop(4) part sets the value of pp of the copy of aa you just returned to 4, thus leaving aa.pp as 33
What you need to do is return a A& or A&& depending on how this gets deduced. To do this you can use decltype(auto) to deduce the value category of the returned object and return the correct type.