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

如何简化仅用单个已知参数包的C++可变参数模板函数实现?

当然可以!直接把逻辑内联到目标函数里就行

完全能把标注QUESTION的impl函数逻辑移到make_by_class_index(也就是你说的#1)的函数体内,同时保留折叠表达式的简洁性,不用手动展开参数包。下面分两种C++版本给出实现方式:

方案1:C++20及以上(推荐,更简洁)

利用C++20支持的模板lambda,直接在函数内部处理参数包展开:

std::unique_ptr<Base> make_by_class_index(int class_index) {
  int i = 0;
  std::unique_ptr<Base> ret;

  // 用模板lambda接收参数包,内部直接复用折叠表达式逻辑
  [&]<class... Types>(std::type_identity<Types>...) {
    ([&] {
      if (i++ == class_index)
        ret = std::make_unique<Types>();
      return ret != nullptr;
    }() || ...);
  }(std::type_identity<ITest_DERIVED_CLASSES>{}...);

  return ret;
}

原理:通过std::type_identity把宏定义的参数包ITest_DERIVED_CLASSES转换成模板lambda的模板参数,内部用||的短路特性快速找到对应索引的类并创建实例,和原impl的逻辑完全一致。

方案2:C++17兼容版本

如果需要兼容C++17(不支持模板lambda),可以用std::tuple+std::apply间接展开参数包:

std::unique_ptr<Base> make_by_class_index(int class_index) {
  int i = 0;
  std::unique_ptr<Base> ret;

  // 把参数包打包成tuple,通过std::apply展开处理
  std::apply([&](auto... type_tags) {
    using namespace std;
    ([&] {
      using Type = decay_t<decltype(type_tags)>;
      if (i++ == class_index)
        ret = make_unique<Type>();
      return ret != nullptr;
    }() || ...);
  }, std::tuple<ITest_DERIVED_CLASSES>{});

  return ret;
}

原理:先把ITest_DERIVED_CLASSES打包成tuple实例,再用std::apply把tuple元素传递给lambda,触发折叠表达式展开,效果和原impl完全相同。

修改后的完整代码(C++20版本)

替换原impl和#1函数后,完整可运行代码如下:

#include <cassert>
#include <cstdio>
#include <memory>
#include <type_traits> // 需添加std::type_identity的头文件

struct Base {
  virtual ~Base() {}
  virtual const char *get_name() const = 0;
};

template <class Derived> struct Base_CRTP : public Base {
  const char *get_name() const final {
    return static_cast<const Derived *>(this)->name;
  }
};

struct A : Base_CRTP<A> {
  static constexpr const char *name = "A";
};
struct B : Base_CRTP<B> {
  static constexpr const char *name = "B";
};

#define ITest_DERIVED_CLASSES A, B

// #1 直接包含折叠表达式逻辑
std::unique_ptr<Base> make_by_class_index(int class_index) {
  int i = 0;
  std::unique_ptr<Base> ret;

  [&]<class... Types>(std::type_identity<Types>...) {
    ([&] {
      if (i++ == class_index)
        ret = std::make_unique<Types>();
      return ret != nullptr;
    }() || ...);
  }(std::type_identity<ITest_DERIVED_CLASSES>{}...);

  return ret;
}

template <class... Types> void print_pack_names() { (puts(Types::name), ...); }

int main() {

  print_pack_names<ITest_DERIVED_CLASSES>();

  puts("");

  auto p = make_by_class_index(0);
  assert(p != nullptr);
  printf("p name: %s\n", p->get_name());

  auto p2 = make_by_class_index(1);
  assert(p2 != nullptr);
  printf("p2 name: %s\n", p2->get_name());

  auto p3 = make_by_class_index(99);
  assert(p3 == nullptr);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 09:05:16