Deduce Template Argument from Std::Function Call Signature

Deduce template argument from std::function call signature

std::function<bool()> bar;

foo(bar); // works just fine

C++ can't deduce the return type from your function bar because it would have to know the type before it could find all the constructors that take your function pointer.

For example, who's to say that std::function<std::string()> doesn't have a constructor taking a bool (*)()?

C++ 17: Deduce signature from Callable in a template

std::function can be constructed from any callable, and can deduce the signature (since c++17). We can use this to make a type trait that extracts the signature.

#include <functional>

template <typename T>
struct get_signature;

template <typename R, typename... Args>
struct get_signature<std::function<R(Args...)>> {
using type = R(Args...);
};

template <typename Callable>
auto deduceSignature(Callable&& c)
{
using Signature = typename get_signature<decltype(std::function{c})>::type;
}

int main() {
deduceSignature([](int a){return 5;});
}

Deduce template arguments from std::function parameter types

Two issues.

  1. Implicit conversion (from lambda to std::function) won't be considered in template argument deduction; which causes deduction failing.

  2. The existence of std::decay_t results in non-deduced context; then Ts can't be deduced.

The following code compiles.

#include <functional>

template <class... Ts>
class A
{
public:

using Func = std::function<void(Ts...)>;

A(Func func) : m_func(func)
{
}

private:

Func m_func;
};

int main()
{
//compiles
A<int, bool> a([](int, bool) {});

//compiles
A b(std::function([](int, bool) {}));

return 0;
}

Deducing template parameters from std::function

The compiler can't deduce the std:function arguments and return type, because you are not passing exec a std::function at all.


Instead of a std::function, you can just make exec accept an arbitrary type of callable (which includes functions), and let the compiler deduce its signature:

template <typename Func, typename... Args>
static double exec(Func func, Args && ... args);

If you do need to know the return type of the function that you pass to exec, you can do it like this:

template <typename Func, typename... Args>
static double exec(Func func, Args && ... args)
{
using R = decltype(func(args...));
// ...
}

The answer is adapted from @IgorTandetnik's comments.

Failure to deduce template argument std::function from lambda function

A std::function is not a lambda, and a lambda is not a std::function.

A lambda is an anonymous type with an operator() and some other minor utility. Your:

auto foo = [](int i) {
std::cout << i << std::endl;
};

is shorthand for

struct __anonymous__type__you__cannot__name__ {
void operator()(int i) {
std::cout << i << std::endl;
}
};
__anonymous__type__you__cannot__name__ foo;

very roughly (there are actual convert-to-function pointer and some other noise I won't cover).

But, note that it does not inherit from std::function<void(int)>.


A lambda won't deduce the template parameters of a std::function because they are unrelated types. Template type deduction is exact pattern matching against types of arguments passed and their base classes. It does not attempt to use conversion of any kind.


A std::function<R(Args...)> is a type that can store anything copyable that can be invoked with values compatible with Args... and returns something compatible with R.

So std::function<void(char)> can store anything that can be invoked with a char. As int functions can be invoked with a char, that works.

Try it:

void some_func( int x ) {
std::cout << x << "\n";
}
int main() {
some_func('a');
some_func(3.14);
}

std::function does that some conversion from its signature to the callable stored within it.


The simplest solution is:

template <class F, class T>
void call(F f, T v) {
f(v);
}

now, in extremely rare cases, you actually need the signature. You can do this in c++17:

template<class T>
void call(std::function<void(T)> f, T v) {
f(v);
}
template<class F, class T>
void call(F f_in, T v) {
std::function f = std::forward<F>(f_in);
call(std::move(f), std::forward<T>(v));
}

Finally, your call is a crippled version of std::invoke from c++17. Consider using it; if not, use backported versions.

std::function - could not deduce template argument for ... (variadic template functions)

Lambdas are not std::function, so cannot be deduced. Fortunately, std::function has CTAD (c++17) allowing to solve your issue with an extra overload:

template<typename Func>
void RegisterFunction(Func func)
{
RegisterFunction(std::function{func}); // forward to overload taking std::function
}

Demo

Could not deduce template argument (vector, std::function)

Template argument deduction doesn't consider implicit conversion.

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

Then type deduction of the template parameter Value on the 2nd function argument f fails because the implicit conversion from lambda to std::function are not considered.

As you showed you can specify template arguments explicitly (to bypass the template argument deduction). Or you can exclude the 2nd argument from deduction by using std::type_identity to declare it as non deduced context.

The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:

e.g.

template<class Value, class Allocator>
void foo(const std::vector<Value, Allocator>& v, const std::function<void(const std::type_identity_t<Value>&)>& f)
{
}

LIVE

PS: std::type_identity is supported from C++20; if the compiler you're using doesn't support it it's not hard to make one.

How to have template type deduced in std::function arguments with lambda?

I recommend you think of std::function<Sig> as a container of any one functor that conforms to Sig as a signature -- and which can be replaced at any moment. This functionality comes in very handy for e.g. std::vector<std::function<Sig>> because such a container can then hold functors of different types.

In your case, because you only care to have just the one functor you really don't need the functionality of std::function<Sig>. As such, I recommend you declare your function template like so:

template<typename T, typename Variant, typename Functor>
void if_init(Variant& opt_variant, Functor functor);

If you are worried that this doesn't communicate that Functor must conform to a void(T) signature, please note that std::function<Sig> does not enforce that either: although obviously you will end up with a compilation error, it is not a nice one. It's planned to be changed (and maybe your implementation has that either), but changed to a different kind of error. Still not that helpful for your case.

I personally make use of template aliases (in the template parameter list) to both document and enforce what a functor should conform to. This ends up looking like:

// Documents that e.g. long l = std::forward<Functor>(functor)(42.)
// should be a valid expression -- a functor that returns int would
// also be accepted.
// Triggers a hard-error (typically a static_assert with a nice message)
// on violation.
template<typename Functor, Requires<is_callable<Functor, long(double)>>...>
R foo(Functor functor);

// Documents that this function template only participates in overload resolution
// if the functor conforms to the signature.
// Does not trigger a hard-error (necessary by design); if everything goes right
// then another overload should be picked up -- otherwise an error of the kind
// 'no matching overload found' is produced
template<typename Functor, EnableIf<is_callable<Functor, long(double)>>...>
R bar(Functor functor);

As to your exact question, the rules of C++ do not allow for a template parameter to be deduced in your case. It's really not an easily fixed 'problem', if it is one. You can find more information on this.



Related Topics



Leave a reply



Submit