You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让类型特性适配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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 12:44:56