You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

含requires与concept的模板复制赋值运算符失效问题

问题分析:模板赋值运算符的匹配问题

预期两次打印Cast template assignment!,但仅第一次赋值时输出该信息。第二次赋值调用了默认赋值运算符,怀疑是requires使用错误导致同类型的赋值无法匹配模板运算符。

#include <iostream>
#include <concepts>

struct Base { };
struct Derived : public Base { int A = 0; };

template<typename T>
class ReferenceWrapper
{
public:
    ReferenceWrapper() { }
    ~ReferenceWrapper() { Buffer = nullptr; }

    //Assignment allows upcasting
    template<typename U> requires(std::derived_from<U, T> || std::same_as<U, T>)
    ReferenceWrapper& operator=(const ReferenceWrapper<U>& Other)
    {
        std::cout << "Cast template assignment!\n";
        Buffer = Other.Buffer;
        return *this;
    }

    const T* Buffer = nullptr;
};


int main()
{
    std::cout << "Hello World!\n";

    ReferenceWrapper<Base> A;
    A = ReferenceWrapper<Derived>();  //Will call the template and print a message
    A = ReferenceWrapper<Base>();     //Default assignment, but why?
}

问题原因与解决方法

问题不在requires的使用,而是模板函数与默认生成函数的匹配优先级:
当进行同类型赋值(ReferenceWrapper<Base>赋值给ReferenceWrapper<Base>)时,编译器会优先选择自动生成的非模板默认赋值运算符,而非你的模板版本。因为在重载决议中,非模板函数的优先级高于模板函数,哪怕模板函数的条件完全满足。

解决方式有两种:

  1. 删除默认赋值运算符,强制编译器使用模板版本:
    在ReferenceWrapper类中添加一行代码,禁用默认生成的赋值运算符:

    ReferenceWrapper& operator=(const ReferenceWrapper&) = delete;
    

    这样编译器无法生成默认版本,只能实例化你的模板赋值运算符,无论同类型还是派生类赋值都会触发预期的打印。

  2. 单独提供同类型的非模板赋值运算符:
    在类中新增一个非模板版本的赋值运算符,覆盖同类型场景:

    ReferenceWrapper& operator=(const ReferenceWrapper& Other)
    {
        std::cout << "Cast template assignment!\n";
        Buffer = Other.Buffer;
        return *this;
    }
    

    这样同类型赋值会直接调用这个非模板版本,派生类赋值则匹配模板版本,两者都能输出预期信息。

内容的提问来源于stack exchange,提问作者galinette

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:24:51