C++模板类型参数自动推导问题:如何实现vSend()无需显式指定类型
解决模板参数推导问题:简化消息发送调用的技巧
这个问题其实挺典型的——你想把显式指定模板参数的调用oMsg.vSend<tMsgVolumeChanged>();简化成更清爽的oMsg.vSend();,但编译器报错推不出模板参数T,这不是你犯了基础错误,而是C++模板参数推导的规则限制导致的:
为什么原来的代码会报错?
你的vSend()是一个无参的模板函数,编译器推导模板参数T需要有参数来源(比如函数参数、返回值推导等),但这里vSend()没有任何参数,也没有返回值关联T,所以编译器完全不知道该把T推导成什么类型,只能要求你显式指定。
巧妙的解决方案:CRTP(奇异递归模板模式)
要让编译器自动知道消息类型,最常用的技巧就是用CRTP让基类“绑定”到派生类的类型上,这样基类不用依赖模板参数推导,就能直接拿到派生类的类型。
修改后的代码如下:
// 基类改成模板类,接收派生类作为模板参数 template <typename Derived> struct tMsgBase { tMsgBase() {} virtual ~tMsgBase() {} // 不再需要模板参数,直接用Derived类型 void vSend() { poGetMessageDispatcher()->boDispatch<Derived>( *static_cast<Derived*>(this) ); } }; // 派生类继承时,把自己作为基类的模板参数传入 struct tMsgVolumeChanged : tMsgBase<tMsgVolumeChanged> { };
现在你就可以直接调用oMsg.vSend();了:
- 基类
tMsgBase<tMsgVolumeChanged>在编译时就已经知道Derived是tMsgVolumeChanged,所以boDispatch的模板参数直接确定; static_cast<Derived*>(this)也更安全,因为继承关系是明确的,不会有原来那种跨类型强制转换的风险;- 同时保留了原来的多态性(基类的虚析构函数依然有效,派生类可以被
tMsgBase<Derived>*指针指向)。
有没有其他备选方案?
如果不想用CRTP,你可能会想到用decltype(*this)尝试推导,但在基类中this的类型是tMsgBase*,decltype(*this)只能得到tMsgBase&,无法直接获取派生类类型。如果用dynamic_cast虽然能拿到派生类,但会引入运行时开销,远不如CRTP的编译期方案高效。
所以CRTP是解决这类“基类需要知道派生类类型”问题的最优选择,也是C++里的经典技巧之一。
内容的提问来源于stack exchange,提问作者kanoop
相关产品推荐
相关产品推荐

