I wrote this simple test to overload based on whether this
is an rvalue or an lvalue. The original version had additional cases, but I reduced it down to 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?
In the "deduced this" case, you always return an A
object by value, so:
aa.prop(33).prop(4)
assigns 33
to aa.pp
, and then the prop(4)
part assigns 4
to pp
of the copy of aa
you just returned, thus leaving aa.pp
as 33
.
What you need to do is return an A&
or A&&
reference, 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.