如何让类型特性适配std::variant的所有派生类型?
问题
我有一个用于检查std::variant是否可容纳指定类型T的type trait与concept。现在定义了继承自std::variant的类型variant2,希望让该类型特性适用于variant2,但现有实现无法正常工作。示例代码如下:
#include <variant> #include <string> #include <iostream> #include <concepts> #include <type_traits> template<typename T, typename Variant> struct variant_type; template<typename T, typename... Args> struct variant_type<T, std::variant<Args...>> : public std::disjunction<std::is_same<T, Args>...> {}; template<typename T, typename Variant> concept is_variant_type = variant_type<T, Variant>::value; template <typename... Ts> struct variant2 : public std::variant<Ts...> { }; variant2<std::monostate, int, bool, std::string> var; int main() { using T = std::string; if constexpr (is_variant_type<T, decltype(var)>) { std::cout << "Worked!" << std::endl; } }
程序无法输出"Worked!",原因是默认的类型特性被SFINAE机制过滤了。该如何优雅解决?
解决方案
方法一:修改type trait识别所有继承自std::variant的类型
通过模板偏特化匹配任意继承std::variant的类型,复用原始std::variant的特化逻辑,无需修改variant2定义:
#include <variant> #include <string> #include <iostream> #include <concepts> #include <type_traits> template<typename T, typename Variant> struct variant_type; // 匹配原始std::variant template<typename T, typename... Args> struct variant_type<T, std::variant<Args...>> : public std::disjunction<std::is_same<T, Args>...> {}; // 匹配继承自std::variant的派生类 template<typename T, typename... Args, typename Derived> struct variant_type<T, Derived> : public std::enable_if_t<std::is_base_of_v<std::variant<Args...>, Derived>, variant_type<T, std::variant<Args...>>> {}; template<typename T, typename Variant> concept is_variant_type = variant_type<T, Variant>::value; template <typename... Ts> struct variant2 : public std::variant<Ts...> { }; variant2<std::monostate, int, bool, std::string> var; int main() { using T = std::string; if constexpr (is_variant_type<T, decltype(var)>) { std::cout << "Worked!" << std::endl; } }
方法二:利用std::variant的嵌套types类型
C++17的std::variant自带嵌套的std::tuple类型types,通过暴露该类型并检查tuple元素实现适配:
#include <variant> #include <string> #include <iostream> #include <concepts> #include <type_traits> #include <tuple> // 辅助模板:检查tuple是否包含指定类型 template<typename T, typename Tuple, std::size_t... Idx> constexpr bool contains_type_impl(std::index_sequence<Idx...>) { return (std::is_same_v<T, std::tuple_element_t<Idx, Tuple>> || ...); } template<typename T, typename Tuple> constexpr bool contains_type() { return contains_type_impl<T, Tuple>(std::make_index_sequence<std::tuple_size_v<Tuple>>{}); } // 重新定义concept template<typename T, typename Variant> concept is_variant_type = requires { typename Variant::types; } && contains_type<T, typename Variant::types>(); template <typename... Ts> struct variant2 : public std::variant<Ts...> { // 暴露基类的types类型 using types = typename std::variant<Ts...>::types; }; variant2<std::monostate, int, bool, std::string> var; int main() { using T = std::string; if constexpr (is_variant_type<T, decltype(var)>) { std::cout << "Worked!" << std::endl; } }
方法三:给variant2显式特化type trait
如果只需要适配variant2,直接为其提供特化是最直接的方式:
#include <variant> #include <string> #include <iostream> #include <concepts> #include <type_traits> template<typename T, typename Variant> struct variant_type; template<typename T, typename... Args> struct variant_type<T, std::variant<Args...>> : public std::disjunction<std::is_same<T, Args>...> {}; // 为variant2提供特化 template<typename T, typename... Args> struct variant_type<T, variant2<Args...>> : public variant_type<T, std::variant<Args...>> {}; template<typename T, typename Variant> concept is_variant_type = variant_type<T, Variant>::value; template <typename... Ts> struct variant2 : public std::variant<Ts...> { }; variant2<std::monostate, int, bool, std::string> var; int main() { using T = std::string; if constexpr (is_variant_type<T, decltype(var)>) { std::cout << "Worked!" << std::endl; } }
方法四:定义通用的variant-like concept
先判断类型是否符合variant的接口特征,再检查类型是否存在,适配所有类variant类型:
#include <variant> #include <string> #include <iostream> #include <concepts> #include <type_traits> #include <tuple> // 定义variant-like概念:要求有types嵌套类型,且支持std::get template<typename V> concept variant_like = requires(V v) { typename V::types; std::get<0>(v); }; // 检查variant-like类型是否包含指定类型 template<typename T, variant_like V> constexpr bool is_variant_type_impl() { using Tuple = typename V::types; return []<std::size_t... Idx>(std::index_sequence<Idx...>) { return (std::is_same_v<T, std::tuple_element_t<Idx, Tuple>> || ...); }(std::make_index_sequence<std::tuple_size_v<Tuple>>{}); } template<typename T, typename V> concept is_variant_type = variant_like<V> && is_variant_type_impl<T, V>(); template <typename... Ts> struct variant2 : public std::variant<Ts...> { using types = typename std::variant<Ts...>::types; }; variant2<std::monostate, int, bool, std::string> var; int main() { using T = std::string; if constexpr (is_variant_type<T, decltype(var)>) { std::cout << "Worked!" << std::endl; } }
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

