问题
std::tuple a{1,3,4,5}
-> make it to numbers greater than 3
std::tuple b{4,5}
Or
std::tuple a{
std::integral_constant<int,1> {},
std::integral_constant<int,3> {},
std::integral_constant<int,4> {},
std::integral_constant<int,5> {}
}
to
std::tuple a{
std::integral_constant<int,4>{},
std::integral_constant<int,5>{}
};
How to convert this at compile time? I can do this using integer_sequence
but that is a cumbersome. Is there a simpler way in C++17 using fold expressions or std::apply
Also after filter, also need to get a tuple of unique entries. But my assumption is if filtering can be done, then finding unique would be trivial.
Edit so that is more clear:
std::tuple<int_c<1>, int_c<3>,int_c<4>,int_c<5>> to std::tuple<int_c<4>,int_c<5>
<-- If such is possible in a concise c++17 way without extra declare functions, it would do!.
Edit: I was fiddling around, maybe something like this would work:
with template... C
as the list of integrals constants:
constexpr auto result = std::tuple_cat(std::conditional_t<(C::value > 3), std::tuple<C>, std::tuple<>>{}...);
回答1:
To turn out your tuple_cat
with c++17:
constexpr auto result = std::apply([](Ts...) {
return std::tuple_cat(std::conditional_t<(Ts::value > 3),
std::tuple<Ts>,
std::tuple<>>{}...);
}, tup);
回答2:
A possible solution is to produce a trait that will output std::tuple<T>
for desirable elements T
and std::tuple<>
for undesirable elements and to use std::tuple_cat to recombine those tuples into a single type. For example :
#include <tuple>
#include <type_traits>
#include <utility>
template <typename Pred, typename Tuple> struct filter;
template <typename t_Predicate, typename ...Ts>
struct filter<t_Predicate, std::tuple<Ts...>>
{
// If this element has to be kept, returns `std::tuple<Ts>`
// Otherwise returns `std::tuple<>`
template<class E>
using t_filter_impl = std::conditional_t<
t_Predicate<E>::value,
std::tuple<E>, std::tuple<>>;
// Determines the type that would be returned by `std::tuple_cat`
// if it were called with instances of the types reported by
// t_filter_impl for each element
using type = decltype(std::tuple_cat(std::declval<t_filter_impl<Ts>>()...));
};
Where t_Predicate<T>
is any predicate type with a bool value;
member which determines whether or not T
is a desirable type. For example to apply this solution to the original question, first write a predicate type specialized for std::integral_constant
:
// Non integral_constant are not kept
template<class T>
struct four_or_more : std::integral_constant<bool, false> {};
// integral_const types are kept if their value is >=4
template<class T, T V>
struct four_or_more<std::integral_constant<T, V>> :
std::integral_constant<bool, V >= 4> {};
And here is a demonstration :
#include <iostream>
int main()
{
auto a = std::make_tuple(
std::integral_constant<int,1> {},
std::integral_constant<int,3> {},
std::integral_constant<int,4> {},
std::integral_constant<int,5> {}
);
using b_type = filter<four_or_more, decltype(a)>::type;
std::cout << "size : " << std::tuple_size<b_type>() << std::endl;
std::cout << std::tuple_element_t<0, b_type>::value << std::endl;
std::cout << std::tuple_element_t<1, b_type>::value << std::endl;
}
回答3:
You can do that with new STL utilities from C++17. That would be something like that:
template<typename T>
auto filter(T tup) {
return std::apply([](auto first, auto... rest) {
auto filtered_rest = [](){
if constexpr (sizeof...(rest)) {
return filter(std::tuple{rest...});
} else {
return std::tuple{};
}
}();
if constexpr (first > 3) {
return std::tuple_cat(std::tuple{first}, filtered_rest);
} else {
return filtered_rest;
}
}, tup);
}
Of course, there is many other ways to do it. In this case I used std::apply
and recursion. I start by an empty tuple and I add one element at a time.
来源:https://stackoverflow.com/questions/50971320/filter-a-tuple-of-types-in-c17