I have the following template function:
template <typename...Args, typename Func>
void call(const char *name, Args...args, Func f)
{
f(3);
}
When I try to use it, like
call("test", 1, 2, 3, [=](int i) { std::cout<< i; });
The compiler complains that it cannot infer the template argument Func
.
How can this problem be solved, knowing that args
can be any type except a function pointer.
Write get_last
, which extracts the last element of a parameter pack.
Call it f
. Call f
.
As an example,
template<typename T0>
auto get_last( T0&& t0 )->decltype(std::forward<T0>(t0))
{
return std::forward<T0>(t0);
}
template<typename T0, typename... Ts>
auto get_last( T0&& t0, Ts&&... ts )->decltype(get_last(std::forward<Ts>(ts)...))
{
return get_last(std::forward<Ts>(ts)...);
}
if you don't care about overload resolution, just calling get_last
and treating it like a functor might be enough:
template <typename...Args>
void call(const char *name, Args...&& args)
{
auto&& f = get_last(std::forward<Args>(args)...);
f(3);
}
The next step up would be to do some SFINAE enable_if
magic in order to make the call
fail to match if you don't pass a valid functor last: however, this is probably overkill.
To detect if the f(3)
will work, a simple traits class:
// trivial traits class:
template<typename T>
struct is_type:std::true_type {};
template<typename Functor, typename=void>
struct can_be_called_with_3:std::false_type {}
template<typename Functor>
struct can_be_called_with_3<Functor,
typename std::enable_if<
std::is_type< decltype(
std::declval<Functor>(3)
) >::value
>::type
>:std::true_type {}
which is pretty silly. A fancier traits class would have to be used if your requirements for the passed in type are more complex (say, you want it to be called with the arguments).
Then you augment call
with:
template <typename...Args>
auto call(const char *name, Args...&& args)
-> typename std::enable_if<
can_be_called_with_3< decltype( get_last(std::forward<Args>(args)... ) ) >::value
>::type
{ /* body unchanged */ }
which is pretty obtuse.