如何为模板化Queue类实现适配Vec的可选构造函数
前置信息
我正在实现一个队列,现有如下Vec类:
#include <cstddef> #include <span> template<typename T, size_t Extent, bool Dynamic = (Extent == std::dynamic_extent)> class Vec { public: constexpr size_t capacity(void) const; ~Vec(); }; template<typename T, size_t Extent> class Vec<T, Extent, true> { // 动态版本 public: Vec(size_t capacity); }; template<typename T, size_t Extent> class Vec<T, Extent, false> { // 静态大小版本 public: constexpr Vec() = default; };
换句话说,当Extent不等于std::dynamic_extent时,Vec可通过constexpr Vec()构造;否则必须传入大小,使用Vec(size_t capacity)构造,该设计模仿std::span的API。
问题
如何在新类Queue中集成该设计,使两类共享大部分功能同时拥有特化构造函数?例如我希望写出如下代码:
template<typename T, size_t Extent = std::dynamic_extent> class Queue { public: template</* 仅当Extent != std::dynamic_extent时启用 */> constexpr Queue() {} template</* 仅当Extent == std::dynamic_extent时启用 */> Queue(size_t size) : m_slots(size) {} // 其他方法... private: Vec<T, Extent> m_slots; // 其他成员变量... };
我知道可以添加与Vec<T>匹配的bool Dynamic参数,但这样需要重复所有字段和函数定义。
我尝试了多种模板表达式(仅展示constexpr Queue()的模板,Queue(size_t size)的表达式为其逆):
尝试1:带默认参数的匿名模板(可能无效)
template<typename = typename std::enable_if_t<Extent != std::dynamic_extent>> constexpr Queue() {}
尝试2:模仿C++文档中std::enable_if的用法
template<std::enable_if_t<Extent != std::dynamic_extent, bool> = true>
两种尝试均编译失败,错误信息如下:
/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/type_traits:2608:44: error: no type named 'type' in 'std::enable_if<false, bool>'; 'enable_if' cannot be used to disable this declaration 2608 | using enable_if_t = typename enable_if<_Cond, _Tp>::type; | ^~~~~ ../src/queues/spsc.hh:34:16: note: in instantiation of template type alias 'enable_if_t' requested here 34 | template<std::enable_if_t<Extent == std::dynamic_extent, bool> = true> |
如何实现该行为?是否有我未考虑到的更优模式?
解决方案
你的问题核心在于:类模板的成员模板直接使用类模板参数作为enable_if条件时,编译器会在实例化类模板时尝试解析所有成员模板的enable_if表达式,条件不满足时就会触发错误。以下是几种可行的解决方式:
方法1:C++20 requires 约束(最简洁)
直接用requires表达式约束构造函数,编译器会在重载决议阶段自动过滤不符合条件的构造函数,不会触发enable_if的实例化错误:
#include <cstddef> #include <span> // 保留原有Vec定义 template<typename T, size_t Extent, bool Dynamic = (Extent == std::dynamic_extent)> class Vec { public: constexpr size_t capacity(void) const; ~Vec(); }; template<typename T, size_t Extent> class Vec<T, Extent, true> { public: Vec(size_t capacity); }; template<typename T, size_t Extent> class Vec<T, Extent, false> { public: constexpr Vec() = default; }; template<typename T, size_t Extent = std::dynamic_extent> class Queue { public: constexpr Queue() requires (Extent != std::dynamic_extent) : m_slots() {} Queue(size_t size) requires (Extent == std::dynamic_extent) : m_slots(size) {} // 其他共享方法 private: Vec<T, Extent> m_slots; };
方法2:兼容C++17的enable_if写法
如果需要兼容C++17,可引入一个依赖于成员模板的参数,让enable_if的条件变为成员模板的依赖项,避免类模板实例化时直接解析条件:
template<typename T, size_t Extent = std::dynamic_extent> class Queue { public: template<size_t E = Extent, std::enable_if_t<E != std::dynamic_extent, int> = 0> constexpr Queue() : m_slots() {} template<size_t E = Extent, std::enable_if_t<E == std::dynamic_extent, int> = 0> Queue(size_t size) : m_slots(size) {} // 其他共享方法 private: Vec<T, Extent> m_slots; };
方法3:拆分公共逻辑+部分特化(更易维护)
如果Queue的大部分功能在动态/静态版本中一致,可把公共逻辑提取到基类,再对Queue做部分特化,避免成员模板的复杂约束:
// 公共基类,存放所有共享功能 template<typename T, size_t Extent> class QueueBase { protected: Vec<T, Extent> m_slots; public: // 共享方法示例 size_t capacity() const { return m_slots.capacity(); } }; // 动态版本特化 template<typename T> class Queue<T, std::dynamic_extent> : public QueueBase<T, std::dynamic_extent> { public: Queue(size_t size) : QueueBase<T, std::dynamic_extent>{size} {} }; // 静态版本特化 template<typename T, size_t Extent> class Queue<T, Extent> : public QueueBase<T, Extent> { public: constexpr Queue() = default; };
内容的提问来源于stack exchange,提问作者Carson
相关产品推荐
相关产品推荐

