获取元组元素类型的索引?

问题描述 投票:0回答:8

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

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

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

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

谢谢。

c++ templates tuples
8个回答
44
投票
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 观看直播

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


4
投票
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
时,这会导致编译失败(内存不足)。


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 作为 std 函数缺少 

constexpr

,
但可以很容易地重写以前的版本。 (对于 
constexpr
 的严格限制,C++11 会更加棘手)。


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


1
投票
template <typename T, typename U, typename... Us> constexpr auto getIndex() { if constexpr (is_same_v<T, U>) { return 0; } else { if constexpr (sizeof...(Us)) { return 1 + getIndex<T, Us...>(); } else {} } } template <typename T, typename U, typename... Us> constexpr auto getIndex(const tuple<U, Us...> &) { return getIndex<T, U, Us...>(); }
用法

tuple the_tuple{'\0', 1, 2L, 3.0, "4", string{"5"}}; cout << getIndex<char>(the_tuple) << endl; // 0 cout << getIndex<double>(the_tuple) << endl; // 3 cout << getIndex<const char *>(the_tuple) << endl; // 4 cout << getIndex<string>(the_tuple) << endl; // 5 /* cout << getIndex<short>(the_tuple) << endl; // compile error */
    

1
投票
这与Qiang的作用相同,但它没有那种看起来奇怪的空else分支。

它还确保将具有唯一类型的元组传递给它以进行良好的测量。

template <typename...> inline constexpr auto is_unique = std::true_type{}; template <typename T, typename... Rest> inline constexpr auto is_unique<T, Rest...> = std::bool_constant<(!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>>{}; template <typename T, typename U, typename... Us> constexpr auto getIndexImpl() { if constexpr (std::is_same<T, U>::value) { return 0; } else { static_assert(sizeof...(Us) > 0, "This tuple does not have that type"); return 1 + getIndexImpl<T, Us...>(); } } template <typename T, typename U, typename... Us> constexpr auto getIndex(const std::tuple<U, Us...> &) { static_assert(is_unique<U, Us...>, "getIndex should only be called on tuples with unique types."); return getIndexImpl<T, U, Us...>(); }
    

0
投票
尝试这个,如果元组为空,T不存在或在元组中不唯一,它会报告错误:

template <template <typename ...> class TT, std::size_t I, typename ...Ts> struct defer { using type = TT<I, Ts...>; }; template <std::size_t, typename, typename> struct tuple_index_helper; template <std::size_t I, typename T, typename U, typename ...Vs> struct tuple_index_helper<I, T, std::tuple<U, Vs...>> { static_assert(!std::is_same_v<T, U>, "Type not unique."); static constexpr std::size_t index = tuple_index_helper<I, T, std::tuple<Vs...>>::index; }; template <std::size_t I, typename T> struct tuple_index_helper<I, T, std::tuple<>> { static constexpr std::size_t index = I; }; template <std::size_t, typename, typename> struct tuple_index; template <std::size_t I, typename T, typename U, typename ...Vs> struct tuple_index<I, T, std::tuple<U, Vs...>> { static constexpr std::size_t index = std::conditional_t<std::is_same_v<T, U>, defer<tuple_index_helper, I, T, std::tuple<Vs...>>, defer<tuple_index, I + 1, T, std::tuple<Vs...>>>::type::index; }; template <std::size_t I, typename T> struct tuple_index<I, T, std::tuple<>> { static_assert(!(I == 0), "Empty tuple."); static_assert(!(I != 0), "Type not exist."); }; template <typename T, typename U> inline constexpr std::size_t tuple_index_v = tuple_index<0, T, U>::index;
示例:

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.
    

0
投票
我认为你不能做得更短(并且可以在 C++17 中工作):

template<class T, class... Ts> constexpr std::size_t index_of(const std::tuple<Ts...>&) { int found{}, count{}; ((!found ? (++count, found = std::is_same_v<T, Ts>) : 0), ...); return found ? count - 1 : count; } int main(int argc, char** argv) { std::tuple tup{"hi", 3, 4.}; std::cout << index_of<char const*>(tup) << '\n'; // Prints 0 std::cout << index_of<double>(tup) << '\n'; // Prints 2 std::cout << index_of<std::string>(tup) << '\n'; // Prints tuple size return 0; }
如果元组类型是可复制且默认可构造的,并且保证查询的类型存在于元组中,那么这也适用:

template<class T, class... Ts> constexpr std::size_t index_of_2(const std::tuple<Ts...>& tup) { std::variant<Ts...> var{T{}}; return var.index(); }
    
© www.soinside.com 2019 - 2024. All rights reserved.