Here is a typical C function that takes a classic function pointer as an argument:
int DoThingy( const char* stuff, int(*helper)(int))
{
int result = 0;
//...call helper...
return result;
}
Below I call the above with a non-capturing lambda that magically "degrades" into a function pointer. At "A" the conversion is implicit. At "B" it's explicit. All good.
void UseThingy()
{
auto lam = [](int)->int {
return 42;
};
int i;
i = DoThingy( "hello", lam); //"A" works
int (*ptr)(int) = lam;
i = DoThingy( "hello", ptr); //"B" works
}
But in this new example, the signature of the callback function depends on a template type:
template <typename T>
int DoThingy2( const char* stuff, int (*helper)(T))
{
int result = 0;
//...call helper...
return result;
}
When I try to use this version as above, line "C" won't even compile. Yet the explicit version at "D" works. What? Why aren't these the same? Notice when I give an explicit template parameter at "E" it works, but certainly the <int>
can be inferred from the the signature of lam
, right?
void UseThingy2()
{
auto lam = [](int)->int {
return 42;
};
int i;
i = DoThingy2( "hello", lam); //"C" won't compile
int (*ptr)(int) = lam;
i = DoThingy2( "hello", ptr); //"D" works
i = DoThingy2<int>( "hello", lam); //"E" works
}
The following also doesn't compile and I wish it would:
i = DoThingy2( "hello", [](int)->int {return 42;}); //"F" won't compile
What I should have said earlier is I need the function parameter to default to 0
so that an internal version of helper
is used when none is provided by the caller.
Note this question is distinctly different from the proposed duplicate. In particular with regards to optional arguments and further it has a better set of answers using C++17 template selection features.
Your issue is with template argument deduction. When that process runs, no conversions happen, the compiler takes the provided object, gets the type of it, and then tries to deduce the template parameters from this type
In this case the function gets lamba_object_from_main
(made up type name), when it expects a function pointer. It can't deduce T
because lamba_object_from_main
is the only thing it has to work with.
Instead of using a function pointer you can change the function to just take in anything for the second parameter like
template <typename Func>
int DoThingy( const char* stuff, Func helper)
{
int result = 0;
//...call helper...
return result;
}
and if you want to make sure that helper
has a specific return type and parameters you can constrain the template like
template <typename Func, std::enable_if_t<is_invocable_r_v<int, Func, int>, bool> = true>
int DoThingy( const char* stuff, Func helper)
{
int result = 0;
//...call helper...
return result;
}
If you want the function to be optional there is at least a couple options. One is to change the template to
template <typename Func = int(*)(int),
std::enable_if_t<std::is_invocable_r_v<int, Func, int>, bool> = true>
int DoThingy2( const char*, Func helper = [](int i){ return internal_function(i); })
{
int result = 0;
helper(result);
return result;
}
which uses a default lambda to call your default internal function
Another option is to have two overloads, one that takes the fucntion and one that does not but calls the one that does with your default function like
template <typename Func, std::enable_if_t<is_invocable_r_v<int, Func, int>, bool> = true>
int DoThingy( const char* stuff, Func helper)
{
int result = 0;
//...call helper...
return result;
}
int DoThingy( const char* stuff)
{
return DoThingy(stuff, internal_function);
}