CRTP模式下实例化类型字符串打印异常问题排查
问题排查与修复
核心问题分析
你的代码存在两个致命问题,都会触发未定义行为(表现为崩溃、输出乱码或异常):
1. 悬垂右值引用
在ServiceType1和ServiceType2中,service_type_被声明为std::string&&(右值引用),但构造函数里绑定的是临时std::string对象:
ServiceType1() :service_type_("Service Type 1") {}
字符串字面量会被隐式构造为临时std::string,这个临时对象在构造函数执行完毕后就会被销毁,导致service_type_变成悬垂引用。后续调用service_type_impl()返回该引用时,访问的是已销毁的内存,必然触发异常。
2. CRTP中的对象切片
Server类里的成员service_是Service<T>类型,而非实际的派生类T:
Service<T> service_;
CRTP模式下,Service<T>是T的基类,存储基类对象会发生对象切片——基类对象无法容纳派生类的成员变量。调用service_.service_type()时,static_cast<T*>(this)会把基类指针强制转换为派生类指针,这是非法操作,因为this指向的是基类对象,不是派生类对象,会导致内存访问错误。
修复后的代码
#include <iostream> #include <string> template <typename servicetype> class Service { public: std::string& service_type() { return static_cast<servicetype*>(this)->service_type_impl(); } }; class ServiceType1 : public Service<ServiceType1> { public: ServiceType1() : service_type_("Service Type 1") {} std::string& service_type_impl() { return service_type_; } private: std::string service_type_; // 改为普通std::string对象,消除悬垂引用 }; class ServiceType2 : public Service<ServiceType2> { public: ServiceType2() : service_type_("Service Type 2") {} std::string& service_type_impl() { return service_type_; } private: std::string service_type_; // 同理,改为普通对象 }; template <typename T> class Server { public: void print() { std::cout << service_.service_type() << std::endl; } T service_; // 存储完整派生类对象,避免对象切片 }; int main() { Server<ServiceType1> service_type1; Server<ServiceType2> service_type2; service_type1.print(); service_type2.print(); return 0; }
修复说明
- 将
service_type_的类型从std::string&&改为std::string,直接存储字符串对象,彻底解决悬垂引用问题。 - 把
Server中的service_类型从Service<T>改为T,存储完整的派生类实例,确保CRTP的类型转换合法,消除对象切片风险。
内容的提问来源于stack exchange,提问作者eniac
相关产品推荐
相关产品推荐

