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

Bar*_*gor 11 c++ c++-standard-library c++17

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

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

Bar*_*gor 12

我为元组找到了这个答案并稍微修改了它:

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>();
    }
} 
Run Code Online (Sandbox Code Playgroud)

它对我有用,但现在我很好奇如何在没有 constexpr if 的情况下以旧方式做到这一点,作为一个结构。


Bar*_*rry 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()>
{ };
Run Code Online (Sandbox Code Playgroud)

也就是说,要找到的索引B中variant<A, B, C>我们构建了一个variant<tag<A>, tag<B>, tag<C>>具有tag<B>和找到自己的指数.

这仅适用于不同类型.

  • @R2RT 从 C++17 开始,标准库具有 std::in_place_type_t ,其用途与本示例中的标记结构完全相同。 (3认同)
  • @R2RT 不。这不能解决某些类型不是文字类型的问题,并且无论如何您都不能在评估的上下文中使用“declval”。 (2认同)

Bar*_*rry 8

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

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;
}
Run Code Online (Sandbox Code Playgroud)

当我们第一次匹配类型时,折叠表达式就会停止,此时我们将停止递增r。即使对于重复的类型,这也适用。如果未找到类型,则返回大小。return如果需要的话,在这种情况下可以很容易地将其更改为“不” ,因为函数return中的缺失constexpr是格式错误的。

如果您不想获取 的实例variant,则此处的参数可以是 a tag<variant<Ts...>>。


NoS*_*tAl 5

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

template<typename Variant, typename T>
constexpr size_t IndexInVariant = mp_find<Variant, T>::value;
Run Code Online (Sandbox Code Playgroud)

完整示例:

#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); 
}
Run Code Online (Sandbox Code Playgroud)