含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>)时,编译器会优先选择自动生成的非模板默认赋值运算符,而非你的模板版本。因为在重载决议中,非模板函数的优先级高于模板函数,哪怕模板函数的条件完全满足。
解决方式有两种:
删除默认赋值运算符,强制编译器使用模板版本:
在ReferenceWrapper类中添加一行代码,禁用默认生成的赋值运算符:ReferenceWrapper& operator=(const ReferenceWrapper&) = delete;这样编译器无法生成默认版本,只能实例化你的模板赋值运算符,无论同类型还是派生类赋值都会触发预期的打印。
单独提供同类型的非模板赋值运算符:
在类中新增一个非模板版本的赋值运算符,覆盖同类型场景:ReferenceWrapper& operator=(const ReferenceWrapper& Other) { std::cout << "Cast template assignment!\n"; Buffer = Other.Buffer; return *this; }这样同类型赋值会直接调用这个非模板版本,派生类赋值则匹配模板版本,两者都能输出预期信息。
内容的提问来源于stack exchange,提问作者galinette
相关产品推荐
相关产品推荐

