In the following code, when I change the source from CAssign
as source for assignment operators to AnyThing auto
, then the move constructor will be called instead of copy.
I'm guessing it has to do with constness but I might be wrong. What causes this and how to achieve what I want?
#include <iostream>
#include <concepts>
template<typename T>
concept AnyThing = true;
struct CAssign
{
CAssign() = default;
CAssign& operator=(const CAssign& source)
{
std::cout << "Copy called\n";
return *this;
}
CAssign& operator=(CAssign&& source)
{
std::cout << "Move called\n";
return *this;
}
};
int main() {
CAssign a1, a2;
a1 = a2;
}
EDIT: This is the change I do:
CAssign& operator=(const AnyThing auto& source)
//...
CAssign& operator=(AnyThing auto&& source)
Tried it with both Clang and GCC. Move is called instead of copy.
CodePudding user response:
In
CAssign& operator=(CAssign&& source)
the reference parameter is a rvalue reference. It will be called only if the argument given is a rvalue (which it isn't in a1 = a2;
). This is a move assignment operator and how it should normally behave.
With
CAssign& operator=(Anything auto&& source)
or just
CAssign& operator=(auto&& source)
this is not a rvalue reference parameter. auto
acts as a short-hand for a template parameter and if a template parameter T
of a function is used directly as T&&
for a function parameter, then it is a forwarding reference.
The forwarding reference can match either a rvalue or a lvalue. If it matches a rvalue, the function parameter will be a rvalue reference. If it matches a lvalue, it will be a lvalue reference.
In your case a1 = a2;
the right-hand side is a lvalue and so it will call the specialization
CAssign& operator=<CAssign&>(CAssign& source)
of the function template that the auto
version defines. This overload is preferred to the copy assignment overload you defined, because it doesn't require a const
conversion in the parameter. If you had made a2
's type const
, the copy assignment operator would be preferred, since it is not a function template.
In particular this means that such a operator=
overload is not a move assignment operator (or a copy assignment operator).
You shouldn't define a template operator=
overload that takes any type or if you do, add a constraint that the type doesn't match CAssign
.