如何简化仅用单个已知参数包的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
相关产品推荐
相关产品推荐

