C++使用enable_if根据模板参数数量决定成员变量类型
C++可变模板类条件定义成员类型实现
当BaseEvent的可变模板参数包仅传入1个类型时,直接使用该类型作为m_data的存储类型、避免冗余std::variant包装的实现方案如下:
核心思路
不需要对整个类做偏特化,通过类型萃取+SFINAE(std::enable_if)即可实现条件分支逻辑:
- 编译期计算参数包长度,判断传入类型的总数量
- 单类型场景下
m_data直接存储目标类型值,去掉无意义的variant包装 - 多类型场景下
m_data使用std::variant<T...>存储多类型值 - 对
get()方法通过enable_if做重载区分,分别适配两种存储模式,避免编译错误
完整实现代码
#include <variant> #include <type_traits> #include <tuple> #include <cstdint> #include <utility> template <typename ...T> class BaseEvent { // 编译期记录参数包长度 static constexpr size_t type_count = sizeof...(T); // 条件定义存储数据的类型 using data_type = std::conditional_t< (type_count == 1), std::tuple_element_t<0, std::tuple<T...>>, std::variant<T...> >; public: // 构造函数参数类型随存储类型自动适配,修正原代码自我初始化的笔误 BaseEvent(const unsigned int index, const uint8_t id, data_type data) : m_index(index), m_id(id), m_data(std::move(data)) {} virtual ~BaseEvent() = default; // 单类型场景的get方法,仅当参数包长度为1、请求类型匹配时生效 template <typename V, std::enable_if_t< type_count == 1 && std::is_same_v<V, std::tuple_element_t<0, std::tuple<T...>>>, int > = 0 > const V get() { static_assert(std::is_same_v<V, std::tuple_element_t<0, std::tuple<T...>>>, "Requested type does not match the single data type of this event"); return m_data; } // 多类型场景的get方法,仅当参数包长度大于1、请求类型在参数包内时生效 template <typename V, std::enable_if_t< (type_count > 1) && (std::is_same_v<V, T> || ...), int > = 0 > const V get() { static_assert((std::is_same_v<V, T> || ...), "Requested type is not supported by this variant event"); return std::get<V>(m_data); } protected: unsigned int m_index; uint8_t m_id; data_type m_data; }; // 子类StringEvent无需修改核心逻辑,补充必要的语法适配即可正常使用 template <class T> class StringEvent : public BaseEvent<T> { public: using BaseEvent<T>::BaseEvent; ~StringEvent() override = default; // 调用模板父类的模板方法需要加this->template前缀,避免语法解析错误 const T string() { return this->template get<T>(); } };
注意点
- 原代码构造函数中
m_data(m_data)属于自我初始化的笔误,会触发未定义行为,实现时已经修正为传入的参数初始化 - 原代码中
static_assert(constexpr std::is_same_v<V, T...>)存在语法错误,已经替换为更友好的编译期类型检查,传错类型时会输出明确的报错信息 - 如果习惯偏特化写法,也可以针对
sizeof...(T) == 1的情况写BaseEvent的偏特化版本单独定义m_data类型,但上述方案代码更集中,后续维护成本更低
内容的提问来源于stack exchange,提问作者demberto
相关产品推荐
相关产品推荐

