C++中子类调用父类重载方法:如何让父类foo触发子类doFoo且无开销?
解决方案:使用CRTP(奇异递归模板模式)实现静态多态
要让c.foo()调用Child::doFoo(),核心问题是父类Parent的foo()函数在编译时就静态绑定到了自身的doFoo(),而非子类的版本。由于C++不允许虚模板函数(template和virtual不能同时使用),我们可以用CRTP来实现无额外运行时开销的静态多态,具体修改如下:
#include <iostream> #include <utility> // 先声明子类,供父类模板使用 struct Child; template<typename Derived> struct Parent { template<class... Args> void doFoo(Args&&... args) { std::cout << "parent doFoo"; } template<class... Args> void foo(Args&&... args) { // 通过静态转换将this转为子类指针,调用子类的doFoo static_cast<Derived*>(this)->doFoo(std::forward<Args>(args)...); } }; struct Child : Parent<Child> { template<class... Args> void doFoo(Args&&... args) { std::cout << "child doFoo"; } }; int main() { Child c; c.foo(); // 输出:child doFoo return 0; }
为什么这个方法有效?
- 无额外开销:
static_cast<Derived*>(this)是编译时完成的转换,完全没有运行时成本(比如虚函数表的查找开销)。 - 精准调用子类实现:通过显式转换到子类指针,编译器会优先在子类作用域中查找
doFoo(),自然调用子类的重写版本。 - 保留模板灵活性:原有的模板参数包
Args&&...和完美转发std::forward<Args>(args)...都被完整保留,不会损失函数的通用性。
原代码失效的原因
原代码中,Parent::foo()里的doFoo(args...)会在Parent的作用域中查找函数名,直接绑定到Parent::doFoo()。由于doFoo不是虚函数,即使子类重写了同名函数,也不会触发动态绑定——C++的非虚函数调用是编译时确定的,只看当前代码所在的类作用域,和对象的实际类型无关。
内容的提问来源于stack exchange,提问作者Saturnu
相关产品推荐
相关产品推荐

