A templated function that takes a function pointer can deduce the arguments of that function pointer like this:
template<class... Args>
void func(void (*ptr)(Args&& ...)) {
//Do something useful knowing the Args...
}
Can you do the same with a lambda as the argument;
without resorting to std::function or writing a metaprogramming traits class like a function_traits? i.e. deduce the arguments just using a function.
No, that's not possible. Template argument deduction can only deduce types, constants, and templates that are "compositionally" part of the argument types, for example, deducing void and Args... from void(*)(Args&&...) as in your example, or deducing T and N from T(&)[N]. It cannot deduce anything that doesn't appear in the type.
For a non-polymorphic lambda type T, the type of &T::operator() contains deducible information about the lambda's argument types. But T itself does not.
Related
Consider the following code:
template <typename>
struct S { };
void g(S<int> t);
template <typename T>
void f(T, std::function<void(S<T>)>);
When attempting to invoke
f(0, g);
I get the following error:
error: no matching function for call to 'f'
f(0, g);
^
note: candidate template ignored: could not match
'function<void (S<type-parameter-0-0>)>'
against 'void (*)(S<int>)'
void f(T, std::function<void(S<T>)>);
^
live example on godbolt.org
While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context
In this case T can first be deduced by the passed argument 0, and then substituted into std::function<void(S<T>)> to get std::function<void(S<int>)>.
I would expect that after deducing T=int, the compiler would substitute T everywhere in the signature and then attempt to construct the std::function parameter with the argument g.
Why is that not the case? I presume that the ordering in which substitution/deduction happens has something to do with this, but I'd like to see the relevant Standard wording.
Bonus question: is this something that could potentially be changed in a future Standard while preserving backwards compatibility, or is there a fundamental reason why this kind of substitution doesn't work?
While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context.
It is not a non-deduced context. Quite the contrary. Because deduction for the parameter of std::function is attempted, but the argument is not a std::function, deduction fails. The deduction of template arguments from function arguments must agree for all function arguments. If it fails for one, it fails entirely.
[temp.deduct.type]
2 In some cases, the deduction is done using a single set of
types P and A, in other cases, there will be a set of corresponding
types P and A. Type deduction is done independently for each P/A pair,
and the deduced template argument values are then combined. If type
deduction cannot be done for any P/A pair, or if for any pair the
deduction leads to more than one possible set of deduced values, or if
different pairs yield different deduced values, or if any template
argument remains neither deduced nor explicitly specified, template
argument deduction fails.
Making the type of the second function parameter into a non-deduced context is actually how one can overcome the error.
#include <functional>
template<typename T>
struct type_identity {
using type = T;
};
template <typename>
struct S { };
void g(S<int> ) {}
template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}
int main() {
f(0, g);
}
T is deduced successfully from the first function argument, and there is nothing left to deduce. So the dedcution is deemed a success.
Live
While I understand that generally the type of the std::function parameter can't be deduced as it is a non-deduced context, in this case T can first be deduced by the passed argument 0.
This is not true. T is deduceable in this context. If you change the code to
template <typename T>
void f(std::function<void(S<T>)>);
int main()
{
f(std::function<void(S<int>)>(g));
}
the code would compile and T is correctly deduced.
Your issue is that you are passing an object to the function that it can't extract T from. The compiler will not do any conversion of the function arguments when it tries to deduce T. That means you have a int and a function as the types passed to the function. It gets int from 0, then tries to get the type from the std::function you pass in the second parameter but since you didn't pass a std::function it can't extract T and because of that, you get an error.
I have a function printint which take an integer as parameter. How I can pass it into a variadic template as below? I have try run_callback(printint,1) but it says "no instance match with the function". Please help, thank you!
void printint(int i)
{
}
template <typename ...Args>
void run_callback(const std::function<void(Args...)>& func, Args ...as)
{
....
}
When you call your function template, e.g., like so
run_callback(printint, 1);
the compiler will try to deduce template arguments which, when substituted into the function parameter types, will make the function parameter types match the types of the arguments. The problem here is that there are no Args... that you could put into const std::function<void(Args...)>& to make that type match the type of printint, which is void(int). Therefore, template argument deduction fails.
If you just want your run_callback to use any function with a signature that matches Args..., have it take a reference to a function with such signature:
template <typename... Args>
void run_callback(void (&func)(Args...), Args... as)
{
}
live example
This, however, will be somewhat brittle as it basically requires that the types in Args are exact matches for the parameter types of the callback. Most likely, you'd want run_callback to work with any function (or, more general, any callable) that could be called with the given args. One way to achieve this would be to have your function template accept any type as a callback but only enable the overload when the func parameter is actually a callable that is invocable using the respective argument list:
template <typename F, typename... Args>
auto run_callback(F&& f, Args&&... as) -> std::enable_if_t<std::is_invocable_v<F, Args...>>
{
f(std::forward<Args>(as)...); // call the callback
}
live example
Finally, if, for some reason, you really absolutely need your func parameter the be an std::function, you could hide the Args... parameter pack in func in a non-deduced context:
template <typename... Args>
void run_callback(const std::common_type_t<std::function<void(Args...)>>& func, Args... as)
{
}
live example
The trick here is the use of std::common_type_t<T>, which really is just a shorthand for std::common_type<T>::type, which will just come out to be T again in the end (Note: there's nothing special about std::common_type except that it's already there for us to use; we could use any other helper template; all we need is something that forwards its argument to a dependent name). It's impossible in general to unambiguously deduce which T you'd have to plug into C<T>::xyz to make C<T>::xyz become a certain type. It's not even guaranteed that there is such an xyz for every single T or that C<T>::xyz would be a type to begin with. For this reason, the nested-name-specifier in a qualified-id is defined to be a non-deduced context [temp.deduct.type]/5.1. Long story short, that means that Args... now appears in the type of the parameter func in such a way that the compiler will not try to deduce what Args... should be from an argument passed for the func parameter (i.e., it will not try to do the thing that caused your original code to fail). It still can deduce Args... from the as function parameter pack, however. Thus, type deduction will just make Args... be whatever the types of the arguments passed for as... are and succeed. After type deduction succeeded, the deduced Args... are substituted back into the remaining parameters and the type of func will come out to be const std::function<void(Args...)>& but now with the types of Args... taken from what they were deduced to be from the as parameter pack.
Another way to do basically the same thing would be to, e.g., wrap the argument for the first parameter in an initializer list:
run_callback({ printint }, 1);
live example
An argument that is an initializer list also makes the parameter a non-deduced context in this case [temp.deduct.type]/5.6, so the explanation for why this works is basically the same as in the previous example. Note that, while the previous approach solves the problem from within the template declaration, this approach solves it at the site of the function call.
Given the following templated function:
template <typename T>
void f(std::function <void (T)>) {}
Can I extract T without having to explicitly mention it when calling f?
f<int>([](int){}); works fine but I'd like T to be deduced and f([](int){}); to just work. The latter errors out with "no matching function for call to".
You can deduce T if the object passed into the function actually has type std::function<void(T)>. If the type you pass in is a lambda, then you'll have to deduce it from the type of the lambda's function call operator, like so:
template <class Callable, class Arg, class T>
void f_helper(Callable callable, Ret (Callable::*)(T)) {
// do something with T
}
template <class Callable>
void f(Callable callable) {
f_helper(callable, &callable::operator());
}
Actually, in reality it is a bit more annoying than this, because you need at least two overloads for f_helper, according to whether the lambda is declared mutable (which determines whether operator() is const), and in C++17 you also need to double the number of overloads again according to whether the lambda is noexcept.
The standard library sidesteps issues like this by not attempting to extract the argument type from the callables you pass to algorithms such as std::sort. It just accepts an arbitrary callable type and then tries to call it.
C++ allows non-type template parameters to be of pointer, including function pointer, type. I recently asked a question about what this is useful for, and this is a follow up to one of the answers.
Is it posible to deduce the value of a function pointer template parameter, from a function argument that is the function pointer in question? For example:
using VoidFunction = void(*)();
template <VoidFunction F>
void templ(VoidFunction);
...
void func(); // a VoidFunction
...
templ<func>(func); // works, but I have to specify the template parameter explicitly
templ(func); // <-- I would like to be able to do this
Is there a way to get this deduction to happen? It seems technically possible from a compiler implementer's point of view, as long as the function argument can be resolved to a function in the code at compile time.
If you're wondering about the motivation behind this, see the comments under this answer, particularly a possible optimization for the implementation of std::bind().
EDIT: I realize that I could simply remove the function argument and use the template argument, as in templ<func>(). My only purpose of adding in the function argument was to try to avoid having to pass the template argument.
I guess what I really want, is to also deduce the type of the function pointer, as in:
template <typename Function, Function F>
void templ(/* something */);
and then be able to call
templ(func);
or
templ<func>();
and have both the type and value be deduced from a single mention of the function pointer.
Hope that makes more sense now.
Template arguments for a function are deduced from the types of the function's template parameters. Template arguments can only be deduced from a type when that type is one of the allowed forms. The allowed forms are specified in [temp.deduct.type]
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
A template type argument T, a template template argument TT or a template non-type argument i can be deduced if P and A have one of the following forms:
T
cv-list T
T*
T&
T[integer-constant]
template-name (where template-name refers to a class template)
type(*)(T)
T(*)()
T(*)(T)
T type::*
type T::*
T T::*
T (type::*)()
type (T::*)()
type (type::*)(T)
type (T::*)(T)
T (type::*)(T)
T (T::*)()
T (T::*)(T)
type[i]
template-name<i> (where template-name refers to a class template)
TT<T>
TT<i>
TT<>
where (T) represents argument lists where at least one argument type contains a T, and () represents argument lists where no parameter contains a T. Similarly, <T> represents template argument lists where at least one argument contains a T, <i> represents template argument lists where at least one argument contains an i and <> represents template argument lists where no argument contains a T or an i.
When considering only non-type template arguments, the relevant forms are those that contain i:
type[i]
template-name<i> (where template-name refers to a class template)
TT<i>
Therefore it is not possible to deduce the value directly from the value of a function argument that is the function pointer. However it is possible to deduce the value of a non-type template argument if the function parameter has one of the specified forms.
The following code ahieves this by wrapping the non-type template argument value in a class-template called NonType. The parameter of f is in the form template-name<i>, making it possible for the value of its non-type template argument to be deduced.
template<typename T, T value>
struct NonType {};
template<typename T, T value>
void f(NonType<T, value>)
{
}
void g();
struct A
{
void f();
int m;
};
int i;
#define MAKE_NONTYPE(value) NonType<decltype(value), (value)>()
int main()
{
f(MAKE_NONTYPE(0)); // NonType<int, 0>
f(MAKE_NONTYPE(&g)); // NonType<void(*)(), &g>
f(MAKE_NONTYPE(&A::f)); // NonType<void(A::*)(), &A::f>
f(MAKE_NONTYPE(&A::m)); // NonType<int A::*, &A::m>
f(MAKE_NONTYPE(&i)); // NonType<int*, &i>
}
Note that decltype and the MAKE_NON_TYPE macro are used here only as a convenience, to avoid having to write out the full template argument list of NonType
I define a method like so:
template <class ArgT>
void foo(ArgT arg, ::boost::function< void(ArgT) > func)
{
func(arg);
}
and use it like this --for instance--:
foo(2, [](int i) -> void { cout << i << endl; });
Why can't the compiler deduce the type since it's definitely an int?
I get 'void foo(ArgT,boost::function<void(ArgT)>)' : could not deduce template argument for 'boost::function<void(ArgT)>' from 'anonymous-namespace'::<lambda0>'.
While C++ lambdas are strictly monomorphic, they are merely shorthand for function objects (aka functors), and in general functors can be polymorphic; i.e., their call operators can be overloaded or templated. As a result, functors (and, consequently, lambdas) are never implicitly convertible to templated std::function<> (or boost::function<>) instances because functors' operator() argument types are not automatically inferable.
To phrase it slightly differently, the natural type of your lambda expression is a functor with a parameterless constructor and an operator() with the signature void operator ()(int) const. However obvious this fact may be to you and I, it's not automatically inferrable that ArgT should resolve to int because lambdas are functors and functors' operator()s are possible to overload and template.
TL;DR: What you want isn't possible.
You want a conversion from the lambda function to boost::function<void(ArgT)> where ArgT is to be deduced. As a general rule, you cannot have type deduction and conversion in the same argument of a function: no conversions take place when deducing a template parameter.
The reasoning behind this is as follows. There are three types involved here: (1) the template parameter, (2) the function parameter type, (3) the passed object type. Two of the types (1 and 2) can be deduced from one another, but both are unknown. If the compiler can assume 2 and 3 are the same type, the problem is solved, but if all the compiler knows is that 3 can be converted to 2 there could be any number of possible solutions, and the compiler is not expected to solve the problem. In practice we know that in this particular case there is only one possible solution, but the standard does not make a distinction between cases.
The rule above applies in all deducible contexts, even if the template parameter can be deduced from another function parameter. The solution here is make the relevant function parameter a non-deducible context, ie a context in which the compiler will never attempt to deduce the template parameter from the function parameter. This can be done as follows:
template <class T> struct identity { typename T type; };
template <class ArgT>
void foo(ArgT arg, typename identity<::boost::function<void(ArgT)>>::type func)
{
func(arg);
}