C++结构体中存储不同返回类型lambda的高效实现方案咨询
你当前定义两个std::function成员的方案是可行的,但确实存在你担心的冗余内存占用、维护成本高的问题,不属于最优实现:64位系统下每个空std::function也会占用24字节左右的内存,额外的type字段需要手动维护和实际调用函数的对应关系,容易出现类型不匹配的bug,运行时判断分支也会增加维护成本。
以下是不同场景下的更优实现方案:
方案1:C++17及以上使用std::variant(最优推荐)
std::variant是C++17引入的类型安全联合体,可以存储指定多种类型中的任意一种,完美匹配你只需要存其中一类lambda的需求,不需要手动维护类型标记,内存占用仅为最大成员类型的大小加个位数的类型标签,远低于两个std::function加type字段的开销。
#include <string> #include <functional> #include <variant> #include <complex> // 先定义两种操作的类型别名 using DoubleOp = std::function<double(double, double)>; using ComplexOp = std::function<std::complex<double>(double, double)>; struct Func { std::string symbol; std::variant<DoubleOp, ComplexOp> operate; };
使用示例:
// 构造返回double的实例 Func add_double { .symbol = "+", .operate = [](double a, double b) { return a + b; } }; // 构造返回复数的实例 Func add_complex { .symbol = "+", .operate = [](double a, double b) { return std::complex<double>{a, b}; } }; // 调用方式1:先判断类型再调用 if (std::holds_alternative<DoubleOp>(add_double.operate)) { double res = std::get<DoubleOp>(add_double.operate)(1.0, 2.0); } // 调用方式2:用std::visit统一处理不同返回类型 std::visit([](auto&& op) { auto res = op(1.0, 2.0); // 这里可针对res的类型做对应处理 }, add_double.operate);
方案2:C++11/14 多态接口实现
如果你的编译环境不支持C++17,可以用多态基类封装不同类型的操作,结构体里存基类智能指针即可:
#include <string> #include <memory> #include <complex> #include <functional> #include <stdexcept> struct OpBase { virtual ~OpBase() = default; virtual bool is_complex() const = 0; virtual double call_double(double a, double b) const { throw std::bad_function_call(); } virtual std::complex<double> call_complex(double a, double b) const { throw std::bad_function_call(); } }; struct DoubleOp : OpBase { std::function<double(double, double)> op; DoubleOp(std::function<double(double, double)> o) : op(std::move(o)) {} bool is_complex() const override { return false; } double call_double(double a, double b) const override { return op(a, b); } }; struct ComplexOp : OpBase { std::function<std::complex<double>(double, double)> op; ComplexOp(std::function<std::complex<double>(double, double)> o) : op(std::move(o)) {} bool is_complex() const override { return true; } std::complex<double> call_complex(double a, double b) const override { return op(a, b); } }; struct Func { std::string symbol; std::unique_ptr<OpBase> operate; };
使用示例:
Func add_double { .symbol = "+", .operate = std::make_unique<DoubleOp>([](double a, double b) { return a + b; }) }; // 调用 if (!add_double.operate->is_complex()) { double res = add_double.operate->call_double(1.0, 2.0); }
该方案兼容低版本C++,但存在虚函数调用、堆内存分配的额外开销,效率低于std::variant方案。
方案3:模板方案(适合不需要混合存储的场景)
如果你不需要把返回double和返回复数的Func实例放到同一个容器里,用编译期模板方案没有任何额外开销,是效率最高的实现:
#include <string> #include <functional> #include <complex> template <typename ReturnType> struct Func { std::string symbol; std::function<ReturnType(double, double)> operate; }; // 使用 Func<double> add_double {"+", [](double a, double b) { return a + b; }}; Func<std::complex<double>> add_complex {"+", [](double a, double b) { return std::complex<double>{a, b}; }};
该方案的缺点是Func<double>和Func<std::complex<double>>属于完全不同的类型,不能混合存储到同一个容器中。
内容的提问来源于stack exchange,提问作者rjc810
相关产品推荐
相关产品推荐

