如何从std::vector<std::shared_ptr<void>>获取Lambda?定义多返回值Lambda向量
问题:如何定义可存储不同返回值Lambda的std::vector?
问题代码(无法正常运行)
以下代码尝试将不同返回值类型的std::function存储到std::vector<std::shared_ptr<void>>中,但最后一行的调用会编译失败——因为std::shared_ptr<void>无法直接解引用并调用其中的函数:
#include <iostream> #include <memory> #include <functional> #include <vector> int main() { printf("Hello World\n"); std::function<int()> dummy1 = []() { return -3; }; std::function<unsigned()> dummy2 = []() { return 33333; }; auto test1 = std::make_shared<std::function<int()>>(dummy1); auto test2 = std::make_shared<std::function<unsigned()>>(dummy2); std::cout << "Final 1: " << (*test1)() << "\n"; std::cout << "Final 2: " << (*test2)() << "\n"; std::vector<std::shared_ptr<void>> ptr; ptr.push_back(test1); ptr.push_back(test2); // 编译失败:无法对void类型解引用并调用函数 // std::cout << "Final 3: " << (*ptr[0])() << "\n"; return 0; }
需求背景
计划实现一个用于监控的中央数据结构,用来查询不同模块中返回类型各异的各类获取器(getter)。需要定期调用所有获取器来查询数据,且获取器支持编译时配置。为保持通用实现并仅依赖config.h文件,需尽可能通用地构建该向量(不过我认为避免使用std::any不太妥当)。
解决方案
针对监控场景的需求,有两种实用的实现方式:
方案1:抽象基类+模板派生类(动态多态)
通过定义统一的抽象接口,将不同返回值的获取器包装成派生类,向量存储基类的智能指针,调用时通过多态接口执行。
#include <iostream> #include <memory> #include <functional> #include <vector> // 统一的监控获取器接口 class MonitorGetter { public: virtual ~MonitorGetter() = default; // 可根据需求扩展,比如传入监控数据收集器的引用 virtual void execute() const = 0; }; // 模板包装类,适配不同返回值的获取器 template<typename ReturnType> class GetterWrapper : public MonitorGetter { private: std::function<ReturnType()> m_getter; public: GetterWrapper(std::function<ReturnType()> getter) : m_getter(std::move(getter)) {} void execute() const override { // 这里统一处理监控数据的输出/收集逻辑 std::cout << "监控数据: " << m_getter() << "\n"; } }; int main() { std::function<int()> dummy1 = []() { return -3; }; std::function<unsigned()> dummy2 = []() { return 33333; }; // 存储统一接口的智能指针 std::vector<std::shared_ptr<MonitorGetter>> getters; getters.emplace_back(std::make_shared<GetterWrapper<int>>(dummy1)); getters.emplace_back(std::make_shared<GetterWrapper<unsigned>>(dummy2)); // 定期调用所有获取器 for (const auto& getter : getters) { getter->execute(); } return 0; }
优点:无需提前枚举所有返回类型,新增类型只需添加对应包装类,扩展性强;缺点:存在轻微的动态多态运行时开销。
方案2:std::variant(静态分发)
如果所有获取器的返回类型在编译时就能确定,可使用std::variant存储不同类型的std::function,通过std::visit执行调用。
#include <iostream> #include <variant> #include <functional> #include <vector> // 枚举所有可能的获取器类型(编译时确定) using GetterVariant = std::variant<std::function<int()>, std::function<unsigned()>>; // 统一执行逻辑 void execute_getter(const GetterVariant& var) { std::visit([](auto&& getter) { std::cout << "监控数据: " << getter() << "\n"; }, var); } int main() { std::vector<GetterVariant> getters; getters.emplace_back([]() { return -3; }); getters.emplace_back([]() { return 33333; }); // 定期调用所有获取器 for (const auto& getter : getters) { execute_getter(getter); } return 0; }
优点:无动态多态开销,实现更简洁;缺点:需要提前枚举所有可能的返回类型,适合编译时配置的场景。
关于std::any的说明
std::any也可用来存储不同类型的获取器,但调用时必须明确转换回原类型才能安全调用,需要额外的类型标记逻辑,在监控这种需要统一执行的场景下,不如上述两种方案直观易用,因此确实不推荐优先使用。
内容的提问来源于stack exchange,提问作者MKFein
相关产品推荐
相关产品推荐

