C++20中如何判断类型是否继承自vector/list/deque?
如何让C++20类型特性支持判断容器的派生类?
问题背景
我正在使用C++20,已通过以下代码实现判断某类型是否为std::vector、std::list或std::deque:
template <typename N> struct is_listish_trait { static constexpr bool value = 0; }; /* These are listish specializations */ template <typename N, typename A> struct is_listish_trait<std::vector<N, A>> { static constexpr bool value = 1; }; template <typename N, typename A> struct is_listish_trait<std::list<N, A>> { static constexpr bool value = 1; }; template <typename N, typename A> struct is_listish_trait<std::deque<N, A>> { static constexpr bool value = 1; }; template <typename T> static constexpr bool is_listish = is_listish_trait<T>::value; static_assert(!is_listish<int>); static_assert(is_listish<std::vector<int>>);
现在希望is_listish能支持判断继承自这些容器的派生类,例如:
struct MyVec : public std::vector<int> { }; static_assert(is_listish<MyVec>); // 期望结果为true
尝试了以下代码但编译器报错无法推导B:
template <typename B, typename D> requires std::is_base_of_v<B, D> struct is_listish_trait<D> { static constexpr bool value = 1; };
错误原因分析
上述特化代码的问题在于:模板参数B没有出现在特化的类型参数列表(即<D>)中,编译器无法从D推导出B的具体类型,因此无法匹配这个特化模板。这种写法不符合C++模板特化的推导规则。
正确实现方案
借助C++20的requires表达式,我们可以更简洁、直观地实现需求,同时支持原生容器和其派生类:
方案1:直接用requires表达式检查转换
#include <vector> #include <list> #include <deque> #include <type_traits> // 判断类型本身是否是目标容器之一 template <typename T> concept IsListishContainer = std::same_as<std::remove_cvref_t<T>, std::vector<typename T::value_type, typename T::allocator_type>> || std::same_as<std::remove_cvref_t<T>, std::list<typename T::value_type, typename T::allocator_type>> || std::same_as<std::remove_cvref_t<T>, std::deque<typename T::value_type, typename T::allocator_type>>; template <typename T> constexpr bool is_listish = IsListishContainer<T> || // 检查是否能隐式转换为std::vector的const引用(公有继承允许该转换) requires { []<typename U, typename A>(const std::vector<U, A>&) {}(std::declval<const T&>()); } || // 检查是否能隐式转换为std::list的const引用 requires { []<typename U, typename A>(const std::list<U, A>&) {}(std::declval<const T&>()); } || // 检查是否能隐式转换为std::deque的const引用 requires { []<typename U, typename A>(const std::deque<U, A>&) {}(std::declval<const T&>()); }; // 测试用例 struct MyVec : public std::vector<int> { }; struct MyList : public std::list<double> { }; struct MyDeque : public std::deque<char> { }; static_assert(!is_listish<int>); static_assert(is_listish<std::vector<int>>); static_assert(is_listish<std::list<double>>); static_assert(is_listish<std::deque<char>>); static_assert(is_listish<MyVec>); static_assert(is_listish<MyList>); static_assert(is_listish<MyDeque>); static_assert(is_listish<const MyVec>); // 支持const限定的类型
方案2:复用模板容器检查逻辑
可以将容器的检查逻辑封装为通用concept,让代码更简洁:
#include <vector> #include <list> #include <deque> #include <type_traits> template <typename T> concept IsListishContainer = std::same_as<std::remove_cvref_t<T>, std::vector<typename T::value_type, typename T::allocator_type>> || std::same_as<std::remove_cvref_t<T>, std::list<typename T::value_type, typename T::allocator_type>> || std::same_as<std::remove_cvref_t<T>, std::deque<typename T::value_type, typename T::allocator_type>>; // 通用concept:判断T是否派生自指定的模板容器 template <template <typename...> typename Container, typename T> concept DerivedFromContainer = requires(const T& t) { static_cast<const Container<typename T::value_type, typename T::allocator_type>&>(t); }; template <typename T> constexpr bool is_listish = IsListishContainer<T> || DerivedFromContainer<std::vector, T> || DerivedFromContainer<std::list, T> || DerivedFromContainer<std::deque, T>; // 测试用例同上
实现说明
- 利用公有继承的隐式转换特性:派生类对象可以隐式转换为基类的const引用,通过
requires表达式尝试这种转换,就能判断类型是否继承自目标容器; - 使用
std::remove_cvref_t处理带const、volatile或引用限定的类型,确保类型匹配的准确性; - 模板lambda的作用是让编译器自动推导容器的模板参数(元素类型
U和分配器A),无需手动指定。
内容的提问来源于stack exchange,提问作者Suraaj K S
相关产品推荐
相关产品推荐

