c++ 如何在编译时找到T类型的元组元素的索引?

jm81lzqq  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(201)

我想找到一个类型为T的元组元素的索引。我想在编译时完成这个任务。怎么做呢?每个类型只在元组中使用一次,所以类型是唯一的。
以下是我目前掌握的信息:

using Tuple_Type = std::tuple
<
    CoolType,
    SomethingElse,
    Boing,
    BingBang
>;

Tuple_Type  m_pool_of_data;

template <typename T>
inline T& Get()
{
    return std::get<T>(m_pool_of_data);     // This one works great
}

template <typename T>
inline int GetIndexOfType()
{
    return ??????;    // How to make this work?
}

Boing b = Get<Boing>();     // Gets the element of type Boing from the tuple
int index_of_type = GetIndexOfType<Boing>();    // Should return 2

字符串
如何使用int GetIndexOfType()
我用的是C++17
额外信息:
std::get正确区分由

using Boing = int;
using BingBang = int;


但是std::is_same_v似乎认为这两个是相同的类型。所以如果我想使用std::is_same_v解决这个问题,这是一个问题。
解决方案:
这是我想出来的,基于这个线程中给出的想法和反馈。似乎按预期工作。

template <typename T, int I>
static constexpr int GetTypeIndexFromTuple()
{
    constexpr int tuple_element_count = std::tuple_size<Tuple_Type>();

    // No data type found?
    if constexpr (I >= tuple_element_count)
    {
        return -1;
    } else {
        // Data type found?
        if constexpr (std::is_same_v<std::tuple_element_t<I, Tuple_Type>, T>)
            return I;
        else
            return GetTypeIndexFromTuple<T, I + 1>();   // Test the next tuple element
    }
}

template <typename T>
inline int GetTypeIndex()
{
    int index = GetTypeIndexFromTuple<T, 0>();

    assert(index != -1);

    return index;
}


这似乎工作。
我还把所有的数据类型都 Package 在一个结构体中,如果它们最初不是结构体或类的话。这解决了相同数据类型的冲突。
这个系统在一个类中使用,因为我正在开发的类是一个专用容器。

7vhp5slm

7vhp5slm1#

使用C++20模板化lambda的答案:

template <typename Tuple, typename T>
constexpr std::size_t GetIndexOfType()
{
    return []<typename... Ts>(std::type_identity<std::tuple<Ts...>> t){
        std::size_t index = 0;
        (void)((++index, std::is_same_v<Ts, T>) || ...);
        return index - 1;
    }(std::type_identity<Tuple>{});
}

字符串
fold表达式是一种令人费解的方式,表示“当类型不相同时,递增索引”。
尽管更规范的方法是使用注解中建议的std::index_sequence
演示:https://godbolt.org/z/5a4P5Phfa
通过编写自己的type_identity并将lambda转换为自由函数,它可以轻松地适应C++17。

template <typename T>
struct type_identity{
    using type = T;
};

template <typename T, typename... Ts>
constexpr std::size_t GetIndexOfTypeHelper(type_identity<std::tuple<Ts...>>){
    std::size_t index = 0;
    (void)((++index, std::is_same_v<Ts, T>) || ...);
    return index - 1;    
}

template <typename Tuple, typename T>
constexpr std::size_t GetIndexOfType()
{
    return GetIndexOfTypeHelper<T>(type_identity<Tuple_Type>{});
}


演示:https://godbolt.org/z/Yhrrxv5dq

nnvyjq4y

nnvyjq4y2#

首先,注意GetIndexOfType真的不应该是一个函数,因为你没有使用任何对象,你只是在使用类型进行元编程。在这种情况下,类模板通常更优雅:

// helper template finds index of a type in a pack
template <typename T, typename... Ts>
struct index_of_type {
    static inline constexpr int value = [] {
        int i = 0;
        bool found = ((++i, std::is_same_v<T, Ts>) || ...);
        return found ? i - 1 : -1;
    }();
};
// helper template to find the index of a type in a tuple-like type
template <typename T, typename Tuple, typename = void>
struct index_of_tuple_type_impl;

template <typename T, typename Tuple>
struct index_of_tuple_type_impl<T, Tuple, std::void_t<decltype(std::tuple_size_v<Tuple>)>> {
    template <std::size_t... Is>
    static constexpr auto get(std::index_sequence<Is...>) {
        return index_of_type<T, std::tuple_element_t<Is, Tuple>...>::value;
    }
    static inline constexpr auto value = get(std::make_index_sequence<std::tuple_size_v<Tuple>>{});
};
// clean interface
template <typename T, typename Tuple>
struct index_of_tuple_type : index_of_tuple_type_impl<T, Tuple> {};

template <typename T, typename Tuple>
inline constexpr int index_of_tuple_type_v = index_of_tuple_type<T, Tuple>::value;

测试

static_assert(index_of_tuple_type_v<int, std::tuple<float, double, int>> == 2);
static_assert(index_of_tuple_type_v<long, std::tuple<float, double, int>> == -1);
static_assert(index_of_tuple_type_v<int, std::array<int, 3>> == 0);

进一步说明

但是std::is_same_v似乎认为这两个是相同的类型。所以如果我想用std::is_same_v解决这个问题,这是一个问题。
这不是你能解决的;这是C++的一个基本问题。别名是弱的,即typedefusing不会创建一个不同的类型。std::is_same_v<int, alias_for_int>总是为真,无论你做什么。
另请参阅Strongly typed using and typedef了解变通方法。

相关问题