Home > front end >  Move assignemnt is called instead of copy, when concepts are used for source type
Move assignemnt is called instead of copy, when concepts are used for source type

Time:02-02

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.

  •  Tags:  
  • Related