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

如何泛化C++模板类pack支持任意数量可调用对象及类型名调用?

问题描述

给定以下简化版C++模板类pack,其构造函数接收两个可调用对象并将它们作为成员存储:

#include <iostream>

struct callable1{
    void operator()(){
        std::cout << "callable 1\n";
    }
};
struct callable2{
    void operator()(){
        std::cout << "callable 2\n";
    }
};

template<typename T1, typename T2>
class pack{
  public:
    pack(T1 t1, T2 t2): f1{t1}, f2{t2} {}
    T1 f1;
    T2 f2;
};

int main(int, char**) {
    pack<callable1, callable2> p{callable1{}, callable2{}};
    p.f1();
    p.f2();
    return 0;
}

运行后输出:

callable 1
callable 2

需要解决两个核心需求:

  • 泛化pack类,使其能接收**任意数量(至少一个)**的可调用对象,且传入两个对象时行为与原代码一致(禁止使用宏)。
  • 调用成员函数时,无需通过p.f1()、p.f2()的方式,而是直接使用可调用对象的类型名调用,例如p.callable1()、p.callable2()。
解决方案

我们可以利用可变参数模板和基类继承展开的技巧实现需求。核心思路是为每个可调用类型创建辅助基类,持有对应实例并提供调用入口;再让pack继承所有辅助基类,通过模板函数实现类型名调用的转发逻辑。

完整实现代码

#include <iostream>
#include <utility>

// 辅助基类:持有单个可调用对象,提供函数符调用入口
template<typename Callable>
struct CallableWrapper {
    explicit CallableWrapper(Callable c) : callable(std::move(c)) {}

    // 内部调用逻辑
    auto invoke() -> decltype(auto) {
        return callable();
    }

private:
    Callable callable;
};

// 可变参数模板的pack类,继承所有CallableWrapper基类
template<typename... Callables>
class pack : public CallableWrapper<Callables>... {
public:
    // 确保至少传入一个可调用对象
    static_assert(sizeof...(Callables) > 0, "pack must contain at least one callable");

    // 可变参数构造函数,初始化所有基类实例
    explicit pack(Callables... callables) 
        : CallableWrapper<Callables>(std::move(callables))... {}

    // 模板转发函数:通过类型名匹配对应的CallableWrapper基类
    template<typename Callable>
    auto Callable() -> decltype(auto) {
        return static_cast<CallableWrapper<Callable>&>(*this).invoke();
    }
};

// 测试用可调用类型
struct callable1{
    void operator()(){
        std::cout << "callable 1\n";
    }
};
struct callable2{
    void operator()(){
        std::cout << "callable 2\n";
    }
};
struct callable3{
    void operator()(){
        std::cout << "callable 3\n";
    }
};

int main() {
    // 测试3个可调用对象的场景
    pack<callable1, callable2, callable3> p{callable1{}, callable2{}, callable3{}};
    p.callable1();
    p.callable2();
    p.callable3();

    // 测试2个可调用对象的场景,与原代码行为兼容
    pack<callable1, callable2> p2{callable1{}, callable2{}};
    p2.callable1();
    p2.callable2();

    return 0;
}

关键逻辑解释

  1. CallableWrapper辅助类:每个可调用类型对应一个独立的CallableWrapper实例,负责持有对象并封装调用逻辑,避免pack类内部成员命名冲突。
  2. 可变参数继承:pack通过public CallableWrapper<Callables>...语法,将所有辅助基类的成员纳入自身作用域,支持任意数量的可调用对象扩展。
  3. 类型名调用实现:pack中的模板成员函数Callable()通过static_cast定位到对应类型的CallableWrapper基类,调用其invoke()方法,实现p.callable1()这类直观调用。
  4. 非空参数校验:通过static_assert确保模板参数包至少包含一个可调用对象,符合需求中的“至少一个”限制。

运行输出

callable 1
callable 2
callable 3
callable 1
callable 2

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 16:22:07