You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将不同模板参数的std::function存入数组?

能不能把不同签名的std::function存入数组?

首先直接给结论:可以,但不能直接用普通数组存不同模板参数的std::function——因为std::function<int(int)>和std::function<int()>是完全独立的实例化类型,C++数组要求所有元素类型必须一致,所以直接定义Sometype funArray[2]是行不通的,除非你用类型擦除的手段。

先聊聊你尝试的void*转换方式为什么不靠谱:
你把std::function对象的地址转成void*再强制转回来,虽然编译能过,但这属于未定义行为。不同签名的std::function内部布局可能完全不同,强制类型转换会破坏类型安全,你那次运行成功只是巧合,换个编译器、换个环境大概率会崩溃或者输出错误结果。更别说你例子里调用(*fun)(a, b)——std::function<int(int)>只接受一个int参数,传两个本身就是签名不匹配,不管转不转void*都是错误的。

接下来给你两种靠谱的解决方案:

方案一:C++17及以上推荐用std::variant

std::variant是C++17引入的类型安全的联合类型,可以存多种不同类型的对象,完美适配你的场景:

#include <functional>
#include <iostream>
#include <variant>
#include <array>

int main() {
    // 定义两个不同签名的std::function
    std::function<int(int)> funA = [](int x) { return x + 5; };
    std::function<int()> funB = []() { return 100; };

    // 声明variant类型,包含我们需要的两种std::function
    using FuncVariant = std::variant<std::function<int(int)>, std::function<int()>>;
    // 用array存储,当然也可以用vector
    std::array<FuncVariant, 2> funArray = {funA, funB};

    // 使用std::visit来调用每个元素,需要处理每种类型的情况
    std::visit([](auto&& func) {
        using FuncType = std::decay_t<decltype(func)>;
        // 用constexpr分支判断当前func的类型
        if constexpr (std::is_same_v<FuncType, std::function<int(int)>>) {
            std::cout << "调用funA:" << func(3) << std::endl; // 输出8
        } else if constexpr (std::is_same_v<FuncType, std::function<int()>>) {
            std::cout << "调用funB:" << func() << std::endl; // 输出100
        }
    }, funArray[0]);

    std::visit([](auto&& func) {
        using FuncType = std::decay_t<decltype(func)>;
        if constexpr (std::is_same_v<FuncType, std::function<int(int)>>) {
            std::cout << func(3) << std::endl;
        } else if constexpr (std::is_same_v<FuncType, std::function<int()>>) {
            std::cout << "调用funB:" << func() << std::endl; // 输出100
        }
    }, funArray[1]);

    return 0;
}

这种方式完全类型安全,不需要手动管理内存,是现在最推荐的做法。

方案二:用多态基类(兼容C++11及以上)

如果你还在使用C++11/14,可以用基类+子类模板的方式做类型擦除:

#include <functional>
#include <iostream>
#include <vector>
#include <memory>

// 定义抽象基类
class FunctionBase {
public:
    virtual ~FunctionBase() = default; // 必须有虚析构,否则子类析构会出问题
};

// 模板子类,包装具体的std::function
template<typename FuncType>
class FunctionWrapper : public FunctionBase {
public:
    explicit FunctionWrapper(FuncType func) : func_(std::move(func)) {}

    // 获取内部的std::function
    FuncType& get() { return func_; }
    const FuncType& get() const { return func_; }

private:
    FuncType func_;
};

int main() {
    std::function<int(int)> funA = [](int x) { return x * 2; };
    std::function<int()> funB = []() { return 42; };

    // 用智能指针数组/vector存储基类指针
    std::vector<std::unique_ptr<FunctionBase>> funArray;
    funArray.emplace_back(std::make_unique<FunctionWrapper<decltype(funA)>>(funA));
    funArray.emplace_back(std::make_unique<FunctionWrapper<decltype(funB)>>(funB));

    // 使用时需要dynamic_cast转成具体的子类类型
    if (auto wrapper = dynamic_cast<FunctionWrapper<decltype(funA)>*>(funArray[0].get())) {
        std::cout << "调用funA:" << wrapper->get()(3) << std::endl; // 输出6
    }

    if (auto wrapper = dynamic_cast<FunctionWrapper<decltype(funB)>*>(funArray[1].get())) {
        std::cout << "调用funB:" << wrapper->get()() << std::endl; // 输出42
    }

    return 0;
}

这种方式需要你在调用时知道每个元素的具体签名,否则dynamic_cast会返回nullptr,需要做空指针检查。

最后再强调一遍:绝对不要用void*强制转换的方式,这种做法完全依赖未定义行为,没有任何可靠性可言,遇到问题只是时间问题。

内容的提问来源于stack exchange,提问作者0811张庆昊

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:30:04