C++17中如何推导std::variant模板参数并为模板类添加约束
在C++17里可以通过**自定义类型特征(type trait)**配合模板特化来实现你的需求,既约束模板参数必须是std::variant,又能提取它的模板参数。
步骤1:定义判断std::variant的类型特征
先写一个特征类来判断传入的类型是不是std::variant的实例:
#include <variant> #include <type_traits> template<typename T> struct is_variant : std::false_type {}; // 匹配std::variant的所有特化版本 template<typename... Args> struct is_variant<std::variant<Args...>> : std::true_type {}; // 辅助变量模板,简化调用 template<typename T> constexpr bool is_variant_v = is_variant<T>::value;
步骤2:提取std::variant的模板参数
再写一个特征类用来提取std::variant的模板参数包,这里我们把参数包封装成std::tuple类型(也可以直接暴露参数包,按需选择):
template<typename T> struct variant_types; template<typename... Args> struct variant_types<std::variant<Args...>> { // 将variant的参数包转换为tuple类型 using tuple_type = std::tuple<Args...>; // 如果需要直接获取参数包,可添加这一行: // using types = Args...; };
步骤3:实现带约束的模板类X
推荐用模板部分特化的方式,能直接拿到std::variant的参数包,使用更灵活:
#include <tuple> // 主模板:默认不定义,用于非variant类型的编译报错 template<class T, typename = void> class X; // 仅当T是std::variant<Args...>时,匹配此特化版本 template<typename... Args> class X<std::variant<Args...>, std::enable_if_t<true>> { public: // 直接用参数包定义tuple std::tuple<Args...> t; };
使用示例:
X<std::variant<int, float>> x; // 正常编译,t的类型是std::tuple<int, float> // X<int> x; // 编译错误,不符合约束(主模板未定义)
如果你更习惯用enable_if直接约束主模板,也可以这么写:
template<class T, std::enable_if_t<is_variant_v<T>, bool> = true> class X { public: // 借助variant_types获取tuple类型 using TupleType = typename variant_types<T>::tuple_type; TupleType t; };
关键注意点
- 绝对不要直接访问
std::variant的_Types这类成员:这些是编译器内部的非标准化实现,不同编译器命名可能不同,属于未公开细节,随时可能变更,完全不可靠。 - 模板部分特化的方式更直观:能直接获取
std::variant的参数包Args...,无需额外类型转换,扩展性更强。
内容的提问来源于stack exchange,提问作者hopeless-programmer
相关产品推荐
相关产品推荐

