C++问题:CRTP父类中的赋值运算符无法正常工作
CRTP父类赋值运算符无法生效的问题解决办法
这个问题本质是C++的**名字隐藏(name hiding)**规则在作祟。当你的子类Bar定义了自己的拷贝/移动赋值运算符(哪怕你没显式写普通赋值,只要有拷贝/移动赋值,编译器的名字查找逻辑就会优先在子类作用域里找operator=),父类IFoo里的operator=(const char*)就会被隐藏——编译器不会再去父类作用域里查找同名的operator=了,自然就不会生效。
最简单的解决办法:用using引入父类的赋值运算符
直接在子类中添加using声明,把父类的operator=带入子类作用域,这样编译器就能找到它了:
struct Bar : public IFoo<Bar> { using IFoo<Bar>::IFoo; using IFoo<Bar>::operator=; // 关键:引入父类的赋值运算符 Bar(const Bar & b) {} Bar(const Bar && b) {} // 不需要再自己实现const char*版本的operator=了 void TestInternal() {} };
这样一来,当你写Bar b; b = "test";时,编译器就能匹配到父类的operator=(const char*)了。
另一种灵活方案:把父类的赋值运算符改成模板(可选)
如果不想每个子类都写using,可以把父类的赋值运算符改成模板形式——模板函数不会被非模板的同名函数隐藏,这样即使子类有自己的赋值运算符,父类的模板版本依然能被匹配到:
#include <type_traits> template <class T> struct IFoo { IFoo() {} IFoo(const char * x) {} // 模板版本的赋值运算符,仅当U可转换为const char*时生效 template <typename U> std::enable_if_t<std::is_convertible_v<U, const char*>, T&> operator=(U&& x) { // 这里可以添加实际的赋值逻辑,比如给子类的成员变量赋值 return *static_cast<T*>(this); } void Test() { static_cast<T *>(this)->TestInternal(); } }; struct Bar : public IFoo<Bar> { using IFoo<Bar>::IFoo; Bar(const Bar & b) {} Bar(const Bar && b) {} void TestInternal() {} };
这个方案适合需要支持多种可转换类型赋值的场景,如果你只需要const char*的话,第一种using的方式更直接高效。
为什么子类自己写就生效?
因为你在子类Bar中定义的operator=(const char*)是直接存在于子类作用域里的,编译器查找operator=时直接就能找到它,不会触发名字隐藏的问题——而父类的版本因为被子类的拷贝/移动赋值运算符隐藏了,所以根本不会被考虑。
内容的提问来源于stack exchange,提问作者Martin Perry
相关产品推荐
相关产品推荐

