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

类模板存储任意函数对象的疑问:是否需Func参数及std::function存储合法性

嘿,我来帮你拆解这两个C++技术问题,都是关于函数存储和模板设计的常见痛点,咱们一个个来聊清楚:

问题1:未知函数签名下,将任意函数对象存储到类的成员容器

如果只是需要存储任意函数(暂时不考虑调用),最简单的方案是用标准库的std::any作为容器的元素类型。std::any可以容纳任意可拷贝构造的类型,包括普通函数指针、lambda表达式、std::function等,完全不需要知道函数的签名:

#include <any>
#include <vector>
#include <functional>

class YourClass {
private:
    std::vector<std::any> storedFuncs;
public:
    template<typename Func>
    void addFunc(Func&& func) {
        storedFuncs.emplace_back(std::forward<Func>(func));
    }
};

// 使用示例
void foo() {}
int bar(int x) { return x*2; }

YourClass obj;
obj.addFunc(foo);
obj.addFunc(bar);
obj.addFunc([](){ /* lambda */ });
obj.addFunc(std::function<void(std::string)>([](std::string s){}));

如果之后需要调用这些函数,单纯用std::any就不够了——因为调用时必须知道函数的签名。这时候可以用类型擦除技术,定义一个抽象基类,再用模板子类包装不同签名的函数:

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

// 抽象基类,定义调用接口(这里以无参无返回为例,你可以根据需求扩展)
class AbstractFunction {
public:
    virtual ~AbstractFunction() = default;
    virtual void invoke() = 0;
};

// 模板子类,包装任意签名的函数(绑定成统一的无参无返回形式)
template<typename Func, typename... Args>
class FunctionWrapper : public AbstractFunction {
private:
    std::function<void()> boundFunc;
public:
    FunctionWrapper(Func func, Args&&... args) 
        : boundFunc(std::bind(std::forward<Func>(func), std::forward<Args>(args)...)) {}
    
    void invoke() override {
        boundFunc();
    }
};

// 辅助函数,简化包装过程
template<typename Func, typename... Args>
std::unique_ptr<AbstractFunction> wrapFunction(Func&& func, Args&&... args) {
    return std::make_unique<FunctionWrapper<Func, Args...>>(
        std::forward<Func>(func), std::forward<Args>(args)...
    );
}

class YourClass {
private:
    std::vector<std::unique_ptr<AbstractFunction>> storedFuncs;
public:
    template<typename Func, typename... Args>
    void addFunc(Func&& func, Args&&... args) {
        storedFuncs.push_back(wrapFunction(std::forward<Func>(func), std::forward<Args>(args)...));
    }

    // 调用所有存储的函数
    void invokeAll() {
        for(auto& func : storedFuncs) {
            func->invoke();
        }
    }
};

这种方式可以把任意签名的函数绑定成统一的调用接口,既能存储又能安全调用。

问题2:ThreadManager类模板的Func参数必要性与std::function存储

针对你的类模板,我来分析两个核心点:

1. 是否需要将Func作为模板参数?

这完全取决于你的设计目标:

  • 如果你的ThreadManager只需要管理单一签名的函数(比如所有线程都执行void(Data&)类型的函数),那把Func作为模板参数是合理的——它能保证类型安全,避免混入不匹配的函数类型,编译期就能发现错误。
  • 如果你的ThreadManager需要支持任意签名的函数,那Func作为模板参数就不合适了——因为模板参数是编译期确定的,一个ThreadManager实例只能处理一种Func类型。这时候应该改用问题1中的类型擦除方案(比如std::vector<std::any>或抽象基类容器),去掉Func模板参数,让类更灵活。

2. 能否不指定模板参数列表直接存入std::function?

如果保留Func模板参数,默认情况下编译器无法自动推导Func类型,除非你提供模板推导指引或支持推导的构造函数/成员函数:

比如给ThreadManager添加一个能推导模板参数的构造函数:

template<class Data, class Func> 
class ThreadManager final { 
private:
    std::vector<std::shared_ptr<Data>> storedDataPtrs;
    std::vector<std::shared_ptr<std::thread>> storedThreadPtrs;
    std::vector<Func> storedFuncs; // 假设存储Func的容器
public:
    // 构造函数,用于推导模板参数
    ThreadManager(std::vector<Func> funcs) : storedFuncs(std::move(funcs)) {}
};

// 模板推导指引(C++17及以上支持)
template<typename Data, typename Func>
ThreadManager(std::vector<std::shared_ptr<Data>>, std::vector<Func>) -> ThreadManager<Data, Func>;

这样你就可以不用显式指定模板参数,直接传入std::function:

// 编译器会自动推导Data为MyData,Func为std::function<void()>
ThreadManager tm{
    std::vector<std::shared_ptr<MyData>>{},
    {std::function<void()>([](){})}
};

但如果去掉Func模板参数,改用类型擦除容器,那创建ThreadManager时只需要指定Data参数,直接就能存任意std::function,完全不需要额外的模板参数:

template<class Data> 
class ThreadManager final { 
private:
    std::vector<std::shared_ptr<Data>> storedDataPtrs;
    std::vector<std::shared_ptr<std::thread>> storedThreadPtrs;
    std::vector<std::any> storedFuncs; // 用std::any存储任意函数
public:
    template<typename Func>
    void addFunc(Func&& func) {
        storedFuncs.emplace_back(std::forward<Func>(func));
    }
};

// 使用示例,只需要指定Data类型
ThreadManager<MyData> tm;
tm.addFunc(std::function<void()>([](){}));
tm.addFunc(std::function<int(MyData&)>([](MyData& d){ return 0; }));

内容的提问来源于stack exchange,提问作者Francis Cugler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:35:14