问题
for example
// we have a variadic function
void print(...);
// I need such a function to filter parameters of specific type
template<typename... Args>
void print_filter(const Args&... args)
{
// filter non-integral type
print(integral args);
}
// my goal, all non-integral type can be ignored:
print_filter(1.0, 2, "abc", 3) == print(2, 3)
I have used up my knowledge to do that... can you help? or just to prove it's impossible, which also be very helpful. Thanks
回答1:
A neat trick is to convert the arguments you want into a 1-element forwarding tuple, the arguments you don't want into an empty tuple, tuple_cat
the results, then apply
(C++17) the resulting tuple to the function you want to invoke:
template<typename... Args>
void print_filter(Args&&... args) {
std::apply(
[](auto&&... args) { return print(std::forward<decltype(args)>(args)...); },
std::tuple_cat(
std::get<std::is_integral<typename std::decay<Args>::type>::value ? 0 : 1>(
std::make_tuple(
[](Args&& arg) { return std::tuple<Args&&>{std::forward<Args>(arg)}; },
[](Args&&) { return std::tuple<>{}; }))(
std::forward<Args>(args))...));
}
Note that this employs another trick, which is to use get
to conditionally apply one of two functions to an argument.
Example.
回答2:
#include <cstddef>
#include <type_traits>
#include <utility>
#include <tuple>
template <typename S, typename M, typename O = std::index_sequence<>>
struct filter : O {};
template <std::size_t I, std::size_t... Is, std::size_t... Js, std::size_t... Ks>
struct filter<std::index_sequence<I, Is...>, std::index_sequence<0, Js...>, std::index_sequence<Ks...>>
: filter<std::index_sequence<Is...>, std::index_sequence<Js...>, std::index_sequence<Ks...>> {};
template <std::size_t I, std::size_t... Is, std::size_t... Js, std::size_t... Ks>
struct filter<std::index_sequence<I, Is...>, std::index_sequence<1, Js...>, std::index_sequence<Ks...>>
: filter<std::index_sequence<Is...>, std::index_sequence<Js...>, std::index_sequence<Ks..., I>> {};
template <template <typename T> class F, typename... Args>
using Filter = filter<std::make_index_sequence<sizeof...(Args)>, std::index_sequence<F<Args>{}...>>;
template <typename... Args, std::size_t... Is>
void print_filter_impl(std::tuple<Args...>&& tuple, std::index_sequence<Is...>)
{
print(std::get<Is>(std::move(tuple))...);
}
template <typename... Args>
void print_filter(Args&&... args)
{
print_filter_impl(std::forward_as_tuple(std::forward<Args>(args)...), Filter<std::is_integral, std::decay_t<Args>...>{});
}
DEMO
回答3:
Here's one way to do it. Start by creating a dummy separator class:
class SEP { };
Then, a helper function that discards all non-integral arguments by pushing the other arguments to the end of the argument list:
template <class T, class... R>
void print_filter_(T x, R... a) {
if (std::is_integral<T>::value) {
print_filter_(a..., x);
} else {
print_filter_(a...);
}
}
After going through all the initial arguments, it is left with only the integral ones:
template <class... T>
void print_filter_(SEP, T... a) {
print(a...);
}
Finally, call the helper function:
template <class... T>
void print_filter(T... a) {
print_filter_(a..., SEP());
}
回答4:
#include <iostream>
#include <type_traits>
#include <utility>
#include <tuple>
template <template <typename> class Predicate, std::size_t N, typename Output, typename... Args> struct find_indices_h;
template <template <typename> class Predicate, std::size_t N, std::size_t... Is, typename First, typename... Rest>
struct find_indices_h<Predicate, N, std::index_sequence<Is...>, First, Rest...> : std::conditional_t<
Predicate<First>::value,
find_indices_h<Predicate, N+1, std::index_sequence<Is..., N>, Rest...>,
find_indices_h<Predicate, N+1, std::index_sequence<Is...>, Rest...>
> {};
template <template <typename> class Predicate, std::size_t N, typename Sequence>
struct find_indices_h<Predicate, N, Sequence> {
using type = Sequence;
};
template <template <typename> class Predicate, typename... Args>
using find_indices = typename find_indices_h<Predicate, 0, std::index_sequence<>, Args...>::type;
template <typename... Args>
void print (Args&&... args) {
const int a[] = {(std::cout << args << ' ', 0)...};
static_cast<void>(a);
std::cout << '\n';
}
template <typename F, typename Tuple, std::size_t... Is>
void partial_apply (F f, Tuple&& tuple, std::index_sequence<Is...>) {
f(std::get<Is>(std::forward<Tuple>(tuple))...);
}
template<typename... Args>
void print_filter (const Args&... args) {
const auto partial_print = [](auto&&... ps) { return print(std::forward<decltype(ps)>(ps)...); };
partial_apply(partial_print, std::forward_as_tuple(args...), find_indices<std::is_integral, Args...>{});
}
int main() {
std::cout << std::boolalpha;
print_filter(1, "hello", 'a', true, 1.3, 1000); // 1 a true 1000
}
来源:https://stackoverflow.com/questions/36747123/can-we-use-variadic-template-function-to-filter-parameters-of-specific-type-the