问题
how could one do this in c++ today without using two separate holders?
typedef std::function<void(int a, int b)> f1;
typedef std::function<void(int a)> f2;
std::vector<f1> m;
void add(f1 f)
{
m.push_back(f);
}
void add(f2 f)
{
// add one more (unused) parameter to f2 so we can add f2 to f1 vector holder?
}
can we somehow overload f1 function to include different set of parameters? could this be solved by variadic templates nowdays or something similar?
回答1:
Create a new lambda matching the new signature and add that instead:
void add(f2 f)
{
m.push_back( [g = std::move(f)](int a, int /* unused */){ g(a); } );
}
回答2:
This wraps a std::function
in a lambda that ignores any extra args:
template<class R, class...Args>
auto ignore_extra_args( std::function<R(Args...)> f ) {
return [f = std::move(f)](Args...args, auto&&...)->R{
return f(std::forward<Args>(args)...);
};
}
it gets trickier if we don't want that needless layer of type erasure.
You need to find the longest prefix of Args...
which you can invoke a given object f
with, then invoke f
with it. This involves some tricky metaprogramming.
It is far easier if you ask the caller to pass in a signature:
template<class Sig>
struct ignore_extra_args_helper;
template<class R, class...Args>
struct ignore_extra_args_helper<R(Args...)> {
template<class F>
auto operator()( F&& f ) {
return [f = std::forward<F>(f)](Args...args, auto&&...)->R{
return f(std::forward<Args>(args)...);
};
}
};
template<class Sig, class F>
auto ignore_extra_args( F&& f ) {
return ignore_extra_args_helper<Sig>{}(std::forward<F>(f));
}
which saves on possible overhead.
template<class F, decltype(std::declval<F const&>()(1,1))* =nullptr>
void add(F&& f) {
m.push_back(std::forward<F>(f));
}
template<class F, class...Unused>
void add(F&& f, Unused&&...) {
add( ignore_extra_args<void(int)>(std::forward<F>(f)) );
}
live example
来源:https://stackoverflow.com/questions/31919895/stdfunction-variable-arguments-in-one-vector-map