当概念用于源类型时,调用移动赋值而不是复制

Move assignemnt is called instead of copy, when concepts are used for source type

在下面的代码中,当我将源从 CAssign 作为赋值运算符的源更改为 AnyThing auto 时,将调用移动构造函数而不是复制。

我猜这与常量有关,但我可能错了。是什么原因导致的以及如何实现我想要的?

#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;
}

编辑: 这是我所做的改变:

CAssign& operator=(const AnyThing auto& source)
//...
CAssign& operator=(AnyThing auto&& source)

用 Clang 和 GCC 都试过了。调用移动而不是复制。

CAssign& operator=(CAssign&& source)

引用参数是右值引用。仅当给定的参数是右值(不在 a1 = a2; 中)时才会调用它。这是一个移动赋值运算符及其正常行为方式。


CAssign& operator=(Anything auto&& source)

或者只是

CAssign& operator=(auto&& source)

这是不是右值引用参数。 auto作为模板参数的short-hand,如果函数的模板参数T直接用作函数参数的T&&,那么它是转发参考.

转发引用可以匹配右值或左值。如果它匹配一个右值,函数参数将是一个右值引用。如果它匹配一个左值,它将是一个左值引用。

在你的例子中a1 = a2; right-hand 边是一个左值,所以它会调用特化

CAssign& operator=<CAssign&>(CAssign& source)

auto版本定义的函数模板。此重载优于您定义的复制赋值重载,因为它不需要参数中的 const 转换。如果您将 a2 的类型设置为 const,则复制赋值运算符将是首选,因为它不是函数模板。

特别是这意味着这样的 operator= 重载 不是 移动赋值运算符(或复制赋值运算符)。


您不应定义采用任何类型的模板 operator= 重载,或者如果您这样做,请添加类型不匹配的约束 CAssign.