CRTP中间类无法访问祖父类成员的原因探究
#include <iostream> #include <string> template<typename T> class animal { public: std::string print() { return static_cast<T*>(this)->impl_print(); }; protected: inline const static std::string verb = "says "; }; template<typename T> class pet : public animal<pet<T>> { public: std::string impl_print() { return verb + static_cast<T*>(this)->impl_print(); }; inline const static std::string excite = "!"; }; class cat : public pet<cat> { public: std::string impl_print() { return "meow" + excite; }; }; class dog : public pet<dog> { public: std::string impl_print() { return "WOOF" + excite; }; }; int main() { cat a; dog c; std::cout<<a.print()<<std::endl; std::cout<<c.print()<<std::endl; return 0; }
上述代码在GCC13编译时出现“'verb' was not declared in this scope”错误。为何CRTP继承链末端的类可访问父类成员,而中间类却无法访问自身父类(祖父类)的成员?
问题原因
这是模板继承中的依赖名称查找规则导致的:
- 中间类
pet<T>继承的基类是animal<pet<T>>,这个基类的类型依赖于模板参数T(属于「依赖基类」)。在模板类的成员函数中,编译器在未完成模板实例化前,不会主动去查找依赖基类的成员,所以直接写verb会被判定为未声明的符号。 - 末端的
cat/dog继承的是pet<cat>/pet<dog>——这是模板实例化后的具体类,不属于依赖模板参数的基类。编译器处理这类「非依赖基类」时,会直接查找其成员,因此excite可以正常访问。
修复方法
有两种可行的修复方式:
- 用
this->明确指向基类成员:
std::string impl_print() { return this->verb + static_cast<T*>(this)->impl_print(); };
- 通过基类名限定成员:
std::string impl_print() { return animal<pet<T>>::verb + static_cast<T*>(this)->impl_print(); };
内容的提问来源于stack exchange,提问作者Treeman
相关产品推荐
相关产品推荐

