15

如果我有一个具有不同元素类型的元组,例如

std::tuple<T0, T1, T2, ...>

以及如何获取元素类型的索引?

template<class T, class Tuple>
struct Index
{
    enum {value = ?;}
};

谢谢。

4

5 回答 5

35
template <class T, class Tuple>
struct Index;

template <class T, class... Types>
struct Index<T, std::tuple<T, Types...>> {
    static const std::size_t value = 0;
};

template <class T, class U, class... Types>
struct Index<T, std::tuple<U, Types...>> {
    static const std::size_t value = 1 + Index<T, std::tuple<Types...>>::value;
};

在 Coliru 现场观看

此实现返回给定类型第一次出现的索引。请求不在元组中的类型的索引会导致编译错误(并且是一个相当丑陋的错误)。

于 2013-08-05T16:59:33.003 回答
2
template< size_t I, typename T, typename Tuple_t>
constexpr size_t index_in_tuple_fn(){
    static_assert(I < std::tuple_size<Tuple_t>::value,"The element is not in the tuple");

    typedef typename std::tuple_element<I,Tuple_t>::type el;
    if constexpr(std::is_same<T,el>::value ){
        return I;
    }else{
        return index_in_tuple_fn<I+1,T,Tuple_t>();
    }
}

template<typename T, typename Tuple_t>
struct index_in_tuple{
    static constexpr size_t value = index_in_tuple_fn<0,T,Tuple_t>();
};

上面的例子避免了生成大量子元组,当你调用index_in_tuple大元组时,这会导致编译失败(内存不足)

于 2020-03-26T13:30:19.837 回答
1

还有一个使用折叠表达式。它还会在未找到时将值设置为 -1。

template <class X, class Tuple>
class Idx;

template <class X, class... T>
class Idx<X, std::tuple<T...>> {
    template <std::size_t... idx>
    static constexpr ssize_t find_idx(std::index_sequence<idx...>) {
        return -1 + ((std::is_same<X, T>::value ? idx + 1 : 0) + ...);
    }
public:
    static constexpr ssize_t value = find_idx(std::index_sequence_for<T...>{});
};

直播:https ://onlinegdb.com/SJE8kOYdv

编辑:

正如@Jarod42 所建议的,可以使用 std::max:

template <class X, class Tuple>
class Idx;

template <class X, class... T>
class Idx<X, std::tuple<T...>> {
    template <std::size_t... idx>
    static constexpr ssize_t find_idx(std::index_sequence<idx...>) {
        return std::max({static_cast<ssize_t>(std::is_same_v<X, T> ? idx : -1)...});
    }
public:
    static constexpr ssize_t value = find_idx(std::index_sequence_for<T...>{});
};
template<typename X, class Tuple>
inline constexpr ssize_t Idx_v = Idx<X, Tuple>::value;

在重复类型的情况下,此版本返回最后一个的索引。

直播:https ://onlinegdb.com/WenEBQs0L

于 2020-10-30T10:48:16.743 回答
1

使用constexpr“函数”(或 lambda),您可能会这样做

template <class T, class Tuple>
struct Index;

template <class T, typename... Ts>
struct Index<T, std::tuple<Ts...>>
{

    static constexpr std::size_t index = [](){
        constexpr std::array<bool, sizeof...(Ts)> a{{ std::is_same<T, Ts>::value... }};

        // You might easily handle duplicate index too (take the last, throw, ...)
        // Here, we select the first one.
        const auto it = std::find(a.begin(), a.end(), true);

        // You might choose other options for not present.

        // As we are in constant expression, we will have compilation error.
        // and not a runtime expection :-)
        if (it == a.end()) throw std::runtime_error("Not present");

        return std::distance(a.begin(), it);
    }();
};

实际上要求标准函数缺少 C++20 constexpr,但可以很容易地为以前的版本重写。(C++11 对 的严格限制会更加棘手constexpr)。

于 2021-10-13T15:38:51.673 回答
0

试试这个,如果元组为空,T 不存在或在元组中不唯一,则报告错误:

template <template <typename ...> class TT, typename ...Ts>
struct lazy_inst
{
    using type = TT<Ts...>;
};

template <typename, typename, typename>
struct tuple_index_helper;

template <std::size_t I, typename T, typename U, typename ...Vs>
struct tuple_index_helper<std::integral_constant<std::size_t, I>, T, std::tuple<U, Vs...>>
{
    static_assert(!std::is_same_v<T, U>, "Type not unique.");
    using index = tuple_index_helper<std::integral_constant<std::size_t, I>, T, tuple<Vs...>>::index;
};

template <std::size_t I, typename T>
struct tuple_index_helper<std::integral_constant<std::size_t, I>, T, std::tuple<>>
{
    using index = std::integral_constant<std::size_t, I>;
};

template <typename, typename, typename>
struct tuple_index;

template <std::size_t I, typename T, typename U, typename ...Vs>
struct tuple_index<std::integral_constant<std::size_t, I>, T, std::tuple<U, Vs...>>
{
    using index = std::conditional_t<std::is_same_v<T, U>,
                                     lazy_inst<tuple_index_helper, std::integral_constant<std::size_t, I>, T, std::tuple<Vs...>>,
                                     lazy_inst<tuple_index, std::integral_constant<std::size_t, I + 1>, T, std::tuple<Vs...>>>::type::index;
};

template <std::size_t I, typename T>
struct tuple_index<std::integral_constant<std::size_t, I>, T, std::tuple<>>
{
    static_assert(!(I == 0), "Empty tuple.");
    static_assert(!(I != 0), "Type not exist.");
};

template <typename U, typename V>
inline constexpr std::size_t tuple_index_v = tuple_index<std::integral_constant<std::size_t, 0>, U, V>::index::value;

例子:

std::tuple<int, float, const char*> t1();
std::tuple<int, float, int> t2();
std::tuple<> t3();

constexpr auto idx = tuple_index_v<float, decltype(t1)>;           // idx = 1
// constexpr auto idx2 = tuple_index_v<long long, decltype(t1)>    // Error: Type not exist.
// constexpr auto idx3 = tuple_index_v<int, decltype(t2)>          // Error: Type not unique.
// constexpr auto idx4 = tuple_index_v<int, decltype(t3)>          // Error: Empty tuple.
于 2022-03-01T03:43:52.857 回答