如果我有一个具有不同元素类型的元组,例如
std::tuple<T0, T1, T2, ...>
以及如何获取元素类型的索引?
template<class T, class Tuple>
struct Index
{
enum {value = ?;}
};
谢谢。
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;
};
此实现返回给定类型第一次出现的索引。请求不在元组中的类型的索引会导致编译错误(并且是一个相当丑陋的错误)。
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。
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;
在重复类型的情况下,此版本返回最后一个的索引。
使用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
)。
试试这个,如果元组为空,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.