类仅含模板化移动赋值运算符时为何支持拷贝赋值操作?
问题核心原因解释
这是C++标准的明确规定导致的差异,具体可拆解为三个核心要点:
- 只有非模板的特殊成员函数才会触发默认函数删除规则
C++标准中对移动赋值运算符的定义是:非模板、参数为当前类类型的右值引用(或带const/volatile修饰的右值引用)的成员赋值运算符。
类A中的赋值运算符是模板函数,不属于标准定义的移动赋值运算符范畴,因此不会触发「用户声明移动操作则删除默认拷贝操作」的规则,编译器会正常为类A隐式生成默认的拷贝赋值运算符。你执行a2 = a1时,模板也会实例化为A& operator=<A&>(A& theOther)匹配左值赋值场景,因此不会报错。 - 类B的报错完全符合标准预期
类B中声明的是非模板的移动赋值运算符,符合标准对移动赋值运算符的定义,因此编译器会将隐式生成的拷贝赋值运算符标记为删除。b2 = b1中b1是左值,无法匹配移动赋值的右值引用参数,只能调用被删除的拷贝赋值运算符,因此触发编译报错。 - 模板化赋值运算符的合规性与隐患
- 语法层面这种写法是完全合规的,但工程层面不推荐无约束使用。
- 这类模板化赋值运算符存在不少隐性问题:比如它会参与所有赋值场景的重载决议,可能在你预期调用拷贝/移动赋值的时候,实例化出更匹配的模板版本;如果传入非A类型的参数,还可能因为访问私有成员、类型不匹配等问题产生意料之外的编译错误。如果确实需要使用这类模板赋值,建议配合
std::enable_if添加类型约束,限制模板参数只能为当前类类型。
附复现代码与报错信息
#include <iostream> #include <string> class A { public: template <class Type> Type& operator=(Type&& theOther) { text = std::forward<Type>(theOther).text; return *this; } private: std::string text; }; class B { public: B& operator=(B&& theOther) { text = std::forward<B>(theOther).text; return *this; } private: std::string text; }; int main() { A a1; A a2; a2 = a1; B b1; B b2; b2 = b1; return 0; }
编译报错信息:
..\src\Main.cpp: In function 'int main()': ..\src\Main.cpp:41:7: error: use of deleted function 'B& B::operator=(const B&)' 41 | b2 = b1; | ^~ ..\src\Main.cpp:19:7: note: 'B& B::operator=(const B&)' is implicitly declared as deleted because 'B' declares a move constructor or move assignment operator 19 | class B | ^ mingw32-make: *** [Makefile:419: Main.o] Error 1
内容的提问来源于stack exchange,提问作者Benjamin Bihler
相关产品推荐
相关产品推荐

