将多态函子存入std::function后,未知具体类型时如何提取并调用Base成员?
问题分析与解决方案
首先得明确为什么你的代码里f.target<Base>()会返回nullptr:std::function::target<T>()的设计逻辑是仅当内部存储的对象的精确类型就是T时,才返回非空指针。它做的是精确类型匹配,而不是多态类型转换——哪怕Derived继承自Base,std::function内部存储的是Derived对象,target<Base>()也无法识别出这个继承关系,自然返回空。
你想要的是“既能享受std::function的灵活可调用性,又能在未知具体类型时访问基类接口”,在C++14里有几种可行的实现方式:
方案一:自定义多态函数包装器
我们可以自己实现一个包装类,既保留std::function的类型擦除能力,又专门添加获取基类指针的接口:
#include <functional> #include <memory> #include <iostream> struct Base { virtual int operator()(int foo) const = 0; virtual void setBar(int bar) = 0; virtual ~Base() = default; }; struct Derived : Base { int operator()(int foo) const override { return foo * 2; } void setBar(int bar) override { bar_ = bar; } private: int bar_ = 0; }; class PolymorphicFunction { public: // 接受任意符合Base接口的可调用对象 template <typename Func> PolymorphicFunction(Func f) : impl_(std::make_shared<Impl<Func>>(std::move(f))) {} // 重载调用运算符,和std::function用法一致 int operator()(int foo) const { return (*impl_)(foo); } // 直接获取基类指针,无需知道具体派生类型 Base* getBase() { return impl_->getBase(); } const Base* getBase() const { return impl_->getBase(); } private: // 内部抽象基类,统一接口 struct ImplBase { virtual int operator()(int foo) const = 0; virtual Base* getBase() = 0; virtual const Base* getBase() const = 0; virtual ~ImplBase() = default; }; // 模板实现类,包装具体的函子类型 template <typename Func> struct Impl : ImplBase { Impl(Func f) : func_(std::move(f)) {} int operator()(int foo) const override { return func_(foo); } Base* getBase() override { return &func_; } const Base* getBase() const override { return &func_; } Func func_; }; std::shared_ptr<ImplBase> impl_; }; PolymorphicFunction getFunction() { return Derived(); } int main() { auto f = getFunction(); if (Base* b = f.getBase()) { b->setBar(5); // 直接调用Base接口,完全不需要知道Derived类型 } std::cout << f(7) << std::endl; // 输出14 return 0; }
这个包装类的用法和std::function几乎一致,但额外提供了getBase()方法,让你能直接访问基类的所有接口。
方案二:绑定std::function与基类智能指针
如果不想自定义包装类,也可以用std::pair把std::function和基类的智能指针绑定在一起,让客户端返回这个组合体:
#include <functional> #include <memory> #include <iostream> #include <utility> struct Base { virtual int operator()(int foo) const = 0; virtual void setBar(int bar) = 0; virtual ~Base() = default; }; struct Derived : Base { int operator()(int foo) const override { return foo * 3; } void setBar(int bar) override { bar_ = bar; } private: int bar_ = 0; }; // 用pair封装调用能力和基类指针 using PolymorphicFuncPair = std::pair<std::function<int(int)>, std::shared_ptr<Base>>; PolymorphicFuncPair getFunction() { auto derived_ptr = std::make_shared<Derived>(); // lambda捕获智能指针,提供调用能力 return { [derived_ptr](int foo) { return (*derived_ptr)(foo); }, derived_ptr }; } int main() { // C++14可以用first/second访问,C++17支持结构化绑定 auto func_pair = getFunction(); func_pair.second->setBar(5); std::cout << func_pair.first(7) << std::endl; // 输出21 return 0; }
这个方案更轻量,不需要自定义类,缺点是客户端需要返回这个pair类型,而不是直接返回函子。
为什么原方案行不通?
再强调下:std::function的类型擦除是为了统一可调用对象的调用接口,它不会存储任何多态相关的信息——内部只保存了一个“调用器”,知道怎么调用存储的对象,但不知道这个对象的继承关系。所以target<T>只能匹配精确类型,无法做向上转型。
内容的提问来源于stack exchange,提问作者Yuriy
相关产品推荐
相关产品推荐

