C++11下无dynamic_cast实现异构函数顺序执行容器是否可行?
可以实现!这里有一个基于C++11的方案
完全可以在C++11中实现你的需求,不需要依赖dynamic_cast,核心思路是用类型擦除技术来统一不同函数的存储和调用接口。下面是具体的实现步骤和代码示例:
1. 先实现一个简易的类型擦除容器(替代C++17的std::any)
因为C++11没有标准的std::any,我们需要自己写一个轻量版本来持有任意类型的值:
#include <iostream> #include <vector> #include <memory> #include <functional> #include <typeindex> #include <stdexcept> #include <string> class Any { public: Any() = default; template<typename T> Any(const T& value) : m_content(new Holder<T>(value)), m_type(typeid(T)) {} Any(const Any& other) : m_content(other.m_content ? other.m_content->clone() : nullptr), m_type(other.m_type) {} Any& operator=(const Any& other) { if (this != &other) { m_content.reset(other.m_content ? other.m_content->clone() : nullptr); m_type = other.m_type; } return *this; } Any(Any&& other) noexcept : m_content(std::move(other.m_content)), m_type(other.m_type) {} Any& operator=(Any&& other) noexcept { if (this != &other) { m_content = std::move(other.m_content); m_type = other.m_type; } return *this; } bool has_value() const { return static_cast<bool>(m_content); } std::type_index type() const { return m_type; } template<typename T> T get() const { if (!m_content || m_type != typeid(T)) { throw std::bad_cast(); } return static_cast<Holder<T>*>(m_content.get())->value; } private: struct BaseHolder { virtual ~BaseHolder() = default; virtual BaseHolder* clone() const = 0; }; template<typename T> struct Holder : BaseHolder { T value; Holder(const T& v) : value(v) {} BaseHolder* clone() const override { return new Holder<T>(value); } }; std::unique_ptr<BaseHolder> m_content; std::type_index m_type; };
2. 定义统一的函数包装器接口
我们需要一个基类来统一所有函数的调用接口,然后用模板派生类来包装具体的函数:
class FunctionWrapper { public: virtual ~FunctionWrapper() = default; virtual Any invoke(const Any& input) const = 0; }; template<typename In, typename Out> class SpecificFunctionWrapper : public FunctionWrapper { public: SpecificFunctionWrapper(std::function<Out(In)> func) : m_func(std::move(func)) {} Any invoke(const Any& input) const override { // 检查输入类型是否匹配函数的参数类型 if (input.type() != typeid(In)) { throw std::invalid_argument("Input type mismatch for function"); } // 提取输入值,调用函数,包装结果为Any返回 In arg = input.get<In>(); Out result = m_func(arg); return Any(result); } private: std::function<Out(In)> m_func; };
3. 实现你的容器类
这个容器负责存储函数包装器,并提供链式调用的方法:
class Container { public: template<typename Func> void add(Func func) { // 自动推断函数的输入输出类型 using Traits = function_traits<Func>; using InType = typename Traits::arg_type; using OutType = typename Traits::return_type; m_wrappers.emplace_back( new SpecificFunctionWrapper<InType, OutType>(std::function<OutType(InType)>(func)) ); } Any execute(const Any& initial_input) const { Any current_value = initial_input; for (const auto& wrapper : m_wrappers) { current_value = wrapper->invoke(current_value); } return current_value; } private: // 辅助模板:提取函数的参数和返回类型 template<typename Func> struct function_traits; template<typename Ret, typename Arg> struct function_traits<Ret(Arg)> { using return_type = Ret; using arg_type = Arg; }; // 处理函数指针的特化 template<typename Ret, typename Arg> struct function_traits<Ret(*)(Arg)> : function_traits<Ret(Arg)> {}; std::vector<std::unique_ptr<FunctionWrapper>> m_wrappers; };
4. 测试代码
现在可以用你提到的函数来验证这个方案:
// 定义你提到的三个函数 int h(int x) { return x * 2; } std::string g(int x) { return "Number: " + std::to_string(x); } double f(const std::string& s) { // 简单示例:返回字符串长度的1.5倍 return s.size() * 1.5; } int main() { Container c; c.add(h); c.add(g); c.add(f); // 初始输入是int类型的4 Any initial(4); Any final_result = c.execute(initial); // 获取最终的double结果 std::cout << "Final result: " << final_result.get<double>() << std::endl; // 输出应该是:Final result: 13.5(因为"Number: 8"的长度是9,9*1.5=13.5) return 0; }
关键原理说明
- 类型擦除的Any类:通过基类
BaseHolder和模板派生类Holder<T>,把任意类型的值包装成统一的Any对象,用std::type_index记录实际类型,全程不需要dynamic_cast。 - 统一的函数包装器:
FunctionWrapper基类提供了统一的invoke接口,模板派生类SpecificFunctionWrapper负责处理具体的输入输出类型匹配,调用时通过type_index检查类型,用static_cast安全提取值(因为已经确认类型匹配)。 - 自动类型推断:
function_traits辅助模板自动提取函数的输入和返回类型,让add方法可以接受任意符合单参数、单返回值的函数。
内容的提问来源于stack exchange,提问作者svoltron
相关产品推荐
相关产品推荐

