在std :: variant中按类型获取索引

问题描述 投票:7回答:5

标准库中是否有一个实用程序来获取std::variant中给定类型的索引?或者我应该为自己制作一个?也就是说,我想在B得到std::variant<A, B, C>的索引,然后返回1

相反的操作有std::variant_alternative。当然,在std::variant的列表上可能有很多相同的类型,所以这个操作不是双射,但对我来说这不是问题(我可以在列表上首次出现类型,或者在std::variant列表上有唯一类型)。

c++ c++17 c++-standard-library
5个回答
12
投票

我们可以利用index()几乎已经做正确的事实。

我们不能随意创建各种类型的实例 - 我们不知道如何做,任意类型可能不是文字类型。但是我们可以创建我们知道的特定类型的实例:

template <typename> struct tag { }; // <== this one IS literal

template <typename T, typename V>
struct get_index;

template <typename T, typename... Ts> 
struct get_index<T, std::variant<Ts...>>
    : std::integral_constant<size_t, std::variant<tag<Ts>...>(tag<T>()).index()>
{ };

也就是说,要找到Bvariant<A, B, C>的指数,我们用variant<tag<A>, tag<B>, tag<C>>构造一个tag<B>并找到它的指数。

这仅适用于不同类型。


3
投票

我找到this回答元组并略微修改它:

template<typename VariantType, typename T, std::size_t index = 0>
constexpr std::size_t variant_index() {
    if constexpr (index == std::variant_size_v<VariantType>) {
        return index;
    } else if constexpr (std::is_same_v<std::variant_alternative_t<index, VariantType>, T>) {
        return index;
    } else {
        return variant_index<VariantType, T, index + 1>();
    }
} 

它适用于我,但现在我很好奇如何在没有constexpr的情况下以旧方式进行,如果,作为一个结构。


3
投票

您也可以使用折叠表达式执行此操作:

template <typename T, typename... Ts>
constexpr size_t get_index(std::variant<Ts...> const&) {
    size_t r = 0;
    auto test = [&](bool b){
        if (!b) ++r;
        return b;
    };
    (test(std::is_same_v<T,Ts>) || ...);
    return r;
}

折叠表达式在我们第一次匹配类型时停止,此时我们停止递增r。这适用于重复类型。如果未找到类型,则返回大小。如果这是优选的话,这可以很容易地改为不是return,因为在return函数中缺少constexpr是不正确的。

如果你不想拿一个variant的例子,这里的论点可能是一个tag<variant<Ts...>>


2
投票

一种有趣的方法是将您的variant<Ts...>转换为自定义类层次结构,所有这些层次结构都实现了一个特定的静态成员函数,并且您可以查询不同的结果。

换句话说,给定variant<A, B, C>,创建一个如下所示的层次结构:

struct base_A {
    static integral_constant<int, 0> get(tag<A>);
};
struct base_B {
    static integral_constant<int, 1> get(tag<B>);
};
struct base_C {
    static integral_constant<int, 2> get(tag<C>);
};
struct getter : base_A, base_B, base_C {
    using base_A::get, base_B::get, base_C::get;
};

然后,decltype(getter::get(tag<T>()))是索引(或不编译)。希望这是有道理的。


在实际代码中,以上内容变为:

template <typename T> struct tag { };

template <std::size_t I, typename T>
struct base {
    static std::integral_constant<size_t, I> get(tag<T>);
};

template <typename S, typename... Ts>
struct getter_impl;

template <std::size_t... Is, typename... Ts>
struct getter_impl<std::index_sequence<Is...>, Ts...>
    : base<Is, Ts>...
{
    using base<Is, Ts>::get...;
};

template <typename... Ts>
struct getter : getter_impl<std::index_sequence_for<Ts...>, Ts...>
{ };

一旦你建立了一个吸气剂,实际使用它会更直接:

template <typename T, typename V>
struct get_index;

template <typename T, typename... Ts>
struct get_index<T, std::variant<Ts...>>
    : decltype(getter<Ts...>::get(tag<T>()))
{ };

这仅适用于类型不同的情况。如果您需要它来使用独立类型,那么您可以做的最好的可能是线性搜索?

template <typename T, typename>
struct get_index;

template <size_t I, typename... Ts> 
struct get_index_impl
{ };

template <size_t I, typename T, typename... Ts> 
struct get_index_impl<I, T, T, Ts...>
    : std::integral_constant<size_t, I>
{ };

template <size_t I, typename T, typename U, typename... Ts> 
struct get_index_impl<I, T, U, Ts...>
    : get_index_impl<I+1, T, Ts...>
{ };

template <typename T, typename... Ts> 
struct get_index<T, std::variant<Ts...>>
    : get_index_impl<0, T, Ts...>
{ };

-1
投票

我的两个 仙 解决方案:

template <typename T, typename... Ts>
constexpr std::size_t variant_index_impl(std::variant<Ts...>**)
{
    std::size_t i = 0; ((!std::is_same_v<T, Ts> && ++i) && ...); return i;
}

template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T>(static_cast<V**>(nullptr));

template <typename T, typename V, std::size_t... Is>
constexpr std::size_t variant_index_impl(std::index_sequence<Is...>)
{
    return ((std::is_same_v<T, std::variant_alternative_t<Is, V>> * Is) + ...);
}

template <typename T, typename V>
constexpr std::size_t variant_index_v = variant_index_impl<T, V>(std::make_index_sequence<std::variant_size_v<V>>{});

如果您希望查找不包含类型或重复类型的硬错误 - 这里是静态断言:

    constexpr auto occurrences = (std::is_same_v<T, Ts> + ...);
    static_assert(occurrences != 0, "The variant cannot have the type");
    static_assert(occurrences <= 1, "The variant has duplicates of the type");
© www.soinside.com 2019 - 2024. All rights reserved.