如何解包可变参数模板初始化对应成员?修复ECS组件添加代码
问题描述
我是可变参数模板和打包参数的新手,想要在程序中实现实体组件系统(ECS)。在尝试为实体添加组件时,发现自己的实现存在重大缺陷。
失败尝试代码:
struct A{ float x; float y; A(float _x, float _y):x(_x), y(_y){} }; struct B{ float x; float y; float z; B(float _x, float _y, float _z):x(_x), y(_y), z(_z){} }; struct Entity{ A* a = NULL; B* b = NULL; Entity(){} template<typename T, typename... args> void Add(args... _args){ if(typeid(T) == typeid(A)) a = new A(_args...); else if(typeid(T) == typeid(B)) b = new B(_args...); else throw("Invalid component added"); } };
调用代码:
Entity ent; ent.Add<A>(12.24f, 123.246f); ent.Add<B>(1.f, 1.2f, 1.23f);
修正方案及代码
原实现的核心问题是运行期分支导致的编译错误:if(typeid(T) == ...)是运行期判断,编译器会尝试编译所有分支的代码。比如调用Add<A>时,new B(_args...)也会被编译,但此时参数数量不匹配B的构造函数,直接触发编译失败。此外还存在裸指针内存泄漏、未处理重复添加的问题。
具体修改点
- 用编译期分支替代运行期判断:使用C++17的
if constexpr,让编译器只编译匹配的分支,避免参数不匹配的编译错误。 - 替换裸指针为智能指针:用
std::unique_ptr自动管理组件内存,避免手动释放的麻烦和泄漏。 - 添加重复添加检查:添加组件前先判断是否已存在,避免内存泄漏。
修正后的代码
#include <memory> // 引入智能指针头文件 #include <stdexcept> // 引入标准异常 struct A{ float x; float y; A(float _x, float _y):x(_x), y(_y){} }; struct B{ float x; float y; float z; B(float _x, float _y, float _z):x(_x), y(_y), z(_z){} }; struct Entity{ std::unique_ptr<A> a; std::unique_ptr<B> b; Entity() = default; template<typename T, typename... Args> void Add(Args&&... _args){ // 编译期判断T的类型 if constexpr (std::is_same_v<T, A>){ if (a) { throw std::runtime_error("Component A already exists"); } a = std::make_unique<A>(std::forward<Args>(_args)...); } else if constexpr (std::is_same_v<T, B>){ if (b) { throw std::runtime_error("Component B already exists"); } b = std::make_unique<B>(std::forward<Args>(_args)...); } else { throw std::runtime_error("Invalid component type"); } } };
调用代码(无需修改)
Entity ent; ent.Add<A>(12.24f, 123.246f); ent.Add<B>(1.f, 1.2f, 1.23f);
C++17之前版本的替代实现
如果无法使用C++17的if constexpr,可以改用模板特化实现:
#include <memory> #include <stdexcept> struct A{ float x; float y; A(float _x, float _y):x(_x), y(_y){} }; struct B{ float x; float y; float z; B(float _x, float _y, float _z):x(_x), y(_y), z(_z){} }; struct Entity{ std::unique_ptr<A> a; std::unique_ptr<B> b; Entity() = default; // 通用模板(默认报错) template<typename T, typename... Args> void Add(Args&&... _args){ throw std::runtime_error("Invalid component type"); } // A组件的特化版本 template<typename... Args> void Add<A>(Args&&... _args){ if (a) throw std::runtime_error("Component A already exists"); a = std::make_unique<A>(std::forward<Args>(_args)...); } // B组件的特化版本 template<typename... Args> void Add<B>(Args&&... _args){ if (b) throw std::runtime_error("Component B already exists"); b = std::make_unique<B>(std::forward<Args>(_args)...); } };
内容的提问来源于stack exchange,提问作者Seon Il
相关产品推荐
相关产品推荐

