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

C++ 在std::variant中按类型获取索引,c++,c++17,c++-standard-library,C++,C++17,C++ Standard Library,标准库中是否有实用程序可用于获取std::variant中给定类型的索引?还是我应该为自己做一个?也就是说,我想在std::variant中获取B的索引,并返回1 对于相反的操作,有std::variant\u备选方案。当然,在std::variant的列表中可能有许多相同的类型,因此此操作不是双射,但对我来说不是问题(我可以在列表中第一次出现类型,或者在std::variant列表中出现唯一的类型).一个有趣的方法是将您的变量转换为自定义类层次结构,所有这些都实现了一个特定的静态成员函数,并具

标准库中是否有实用程序可用于获取
std::variant
中给定类型的索引?还是我应该为自己做一个?也就是说,我想在
std::variant
中获取
B
的索引,并返回
1


对于相反的操作,有
std::variant\u备选方案
。当然,在
std::variant
的列表中可能有许多相同的类型,因此此操作不是双射,但对我来说不是问题(我可以在列表中第一次出现类型,或者在
std::variant
列表中出现唯一的类型).

一个有趣的方法是将您的
变量
转换为自定义类层次结构,所有这些都实现了一个特定的静态成员函数,并具有不同的结果供您查询

换句话说,给定
变量
,创建一个如下所示的层次结构:

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;
};
struct base\u A{
静态积分常数get(tag);
};
结构基础{
静态积分常数get(tag);
};
结构基础{
静态积分常数get(tag);
};
结构getter:base\u A、base\u B、base\u C{
使用base_A::get,base_B::get,base_C::get;
};
然后,
decltype(getter::get(tag())
是索引(或不编译)。希望这是有道理的


在实数代码中,上述内容变为:

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...>
{ };
模板结构标记{};
模板
结构基{
静态std::积分常数get(tag);
};
模板
结构getter_impl;
模板
结构getter_impl
:基地。。。
{
使用base::get。。。;
};
模板
结构getter:getter\u impl
{ };
一旦你建立了一个getter,实际上使用它要简单得多:

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>()))
{ };
模板
结构获取索引;
模板
结构获取索引
:decltype(getter::get(tag()))
{ };

这只适用于类型不同的情况。如果你需要它来处理独立类型,那么你能做的最好的可能就是线性搜索

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...>
{ };
模板
结构获取索引;
模板
结构get\u index\u impl
{ };
模板
结构get\u index\u impl
:std::积分常数
{ };
模板
结构get\u index\u impl
:获取索引
{ };
模板
结构获取索引
:获取索引
{ };
我找到了元组的答案,并对其进行了轻微修改:

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 std::大小变量索引(){
if constexpr(index==std::variant\u size\u v){
收益指数;
}否则,如果constexpr(std::is_same_v){
收益指数;
}否则{
返回变量_index();
}
} 

它对我来说很有用,但现在我很好奇,如果不使用constexpr,作为一个结构,如何用老方法来实现它。

我们可以利用这样一个事实,即几乎已经做了正确的事情

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

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()>
{ };

模板结构标记{};// 也可以使用折叠表达式执行此操作:

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;
}
模板
constexpr size\u t get\u index(std::variant const&){
尺寸r=0;
自动测试=[&](布尔b){
如果(!b)++r;
返回b;
};
(测试(标准:是否相同?);
返回r;
}
折叠表达式在我们第一次匹配类型时停止,此时我们停止递增
r
。这甚至适用于重复类型。如果找不到类型,则返回大小。在这种情况下,如果可取的话,可以很容易地将其更改为not
return
,因为
constexpr
函数中缺少
return
是格式错误的

如果您不想以
variant
为例,这里的参数可以是
标记

我的两美分解决方案:

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));
模板
constexpr std::size\u t variant\u index\u impl(std::variant**)
{
std::size_t i=0;(!std::is_same_v&&++i)&&&……;返回i;
}
模板
constepr std::size_t variant_index_v=variant_index_impl(static_cast(nullptr));

模板
constexpr std::size\u t variant\u index\u impl(std::index\u序列)
{
返回((std::is_same_v*is)+…);
}
模板
constexpr std::size\u t variant\u index\u v=variant\u index\u impl(std::make\u index\u sequence{});

如果您希望在查找不包含类型或重复类型时出现硬错误-以下是静态断言:

    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");
constexpr自动出现=(std::is_same_v+…);
静态_断言(出现次数!=0,“变量不能具有类型”);

静态断言(事件另一种有趣的实现方式,使用:

模板
使用get_index=std::integral_常量<
标准:尺寸,
mp11::mp_变换(mp11::mp_列表{}).index()
>;

这也适用于C++17,以防您还不能使用。

对于Boost.Mp11,这是一个简短的单行程序:

template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
模板
constexpr size\u t IndexInVariant=mp\u find::value;
全部:

#包括
#包括
使用名称空间boost::mp11;
模板
constexpr size\u t IndexInVariant=mp\u find::value;
int main()
{
使用V=std::variant;
静态断言(IndexInVariant==0);
//对于重复项,返回第一个idx
静态断言(IndexInVariant==1);
静态断言(IndexInVariant==2);
//找不到返回“.end()”/或变量的大小
静态断言(IndexInVariant==4);
//注意const、volatile和ref没有被剥离
静态断言(IndexInVariant==4);
静态断言(IndexInVariant==4);
静态断言(IndexInVariant==4);
}
另一种看法:

#include <type_traits>

namespace detail {
    struct count_index {
        std::size_t value = 0;
        bool found = false;
    
        template <typename T, typename U>
        constexpr count_index operator+(const std::is_same<T, U> &rhs)
        {
            if (found)
                return *this;
    
            return { value + !rhs, rhs};
        }
    };
}

template <typename Seq, typename T>
struct index_of;

template <template <typename...> typename Seq, typename... Ts, typename T>
struct index_of<Seq<Ts...>, T>: std::integral_constant<std::size_t, (detail::count_index{} + ... + std::is_same<T, Ts>{}).value> {
    static_assert(index_of::value < sizeof...(Ts), "Sequence doesn't contain the type");
};
#包括
名称空间详细信息{
结构计数索引{
标准::尺寸值=0;
bool-found=false;
模板
constexpr count_index操作符+(const std::is_same&rhs)
{
如果(找到)
归还*这个;
返回{value+!rhs,rhs};
}
};
}
模板
结构索引;
模板
结构索引:std::整型常数{
斯达
#include <variant>
#include <boost/mp11/algorithm.hpp>

using namespace boost::mp11;

template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;

int main()
{
    using V = std::variant<int,double, char, double>;
    static_assert(IndexInVariant<V, int> == 0);
    // for duplicates first idx is returned
    static_assert(IndexInVariant<V, double> == 1);
    static_assert(IndexInVariant<V, char> == 2);
    // not found returns ".end()"/ or size of variant
    static_assert(IndexInVariant<V, float> == 4); 
    // beware that const and volatile and ref are not stripped
    static_assert(IndexInVariant<V, int&> == 4); 
    static_assert(IndexInVariant<V, const int> == 4); 
    static_assert(IndexInVariant<V, volatile int> == 4); 
}
#include <type_traits>

namespace detail {
    struct count_index {
        std::size_t value = 0;
        bool found = false;
    
        template <typename T, typename U>
        constexpr count_index operator+(const std::is_same<T, U> &rhs)
        {
            if (found)
                return *this;
    
            return { value + !rhs, rhs};
        }
    };
}

template <typename Seq, typename T>
struct index_of;

template <template <typename...> typename Seq, typename... Ts, typename T>
struct index_of<Seq<Ts...>, T>: std::integral_constant<std::size_t, (detail::count_index{} + ... + std::is_same<T, Ts>{}).value> {
    static_assert(index_of::value < sizeof...(Ts), "Sequence doesn't contain the type");
};
#include <variant>

struct A{};
struct B{};
struct C{};
using V = std::variant<A, B, C>;

static_assert(index_of<V, B>::value == 1);
static_assert(index_of<std::tuple<int, float, bool>, float>::value == 1);