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.
Related
Let's consider two functions with same names:
int func(int&)
{
return 7;
}
int func(const int&)
{
return 5;
}
Let int mutableValue = 5 be defined somewhere. Is there any possibility that template function call in call(mutableValue, func) would take only int func(int&)?
I don't want to use static_cast<int(&)(int&)> - because it's so noisy.
Naive implementation:
template<typename Arg, typename Ret>
void call(Arg& val, Ret (&fun)(Arg&))
{
fun(val);
}
works on gcc, but does not work on clang - solutions must work on both compilers.
Accepting only const Arg& is easy. Deleting const Arg& overload does not help.
I believe clang is the only compiler that gets this right. This should not compile. As long as the argument to fun is an overload set that does not contain any function templates, template argument deduction will be attempted for each member of the overload set. If template argument deduction would succeed for more than one of these overloads, the function parameter becomes a non-deduced context [temp.deduct.call]/6.2.
In the example in question, the argument to fun is the overload set func, which does indeed not contain any function templates. Thus, argument deduction on fun is attempted for both overloads of func, which succeeds. As a result, the parameter fun becomes a non-deduced context, which means that no argument can be deduced for Ret and the call fails as there are no candidates (exactly what clang complains about).
To disambiguate this call, simply explicitly specify the argument for the first template parameter:
call<int>(mutableValue, func)
Since it seems to be impossible to resolve the ambiguity in one template argument deduction:
If you are ok with a change of the syntax at the call site you can separate the call into two calls/deduction passes:
template<typename Arg>
auto call(Arg& val)
{
return [&](auto (&fun)(Arg&)){ fun(val); };
}
to be called as
call(mutableValue)(func)
Another downside is however that the lambda could be stored by a caller and accidentally used later when the captured reference isn't valid anymore.
You could maybe hide this in a macro call, so that the syntax matches what you want and to reduce the potential for misuse.
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.
I have the following template method:
template<typename T, typename... Args>
void register_scene(const std::string& name, Args&&... args) {
auto func = std::bind(&T::template create<Window*, Args&...>, std::placeholders::_1, std::forward<Args>(args)...);
_store_scene_factory(name, [=](Window* window) -> SceneBasePtr {
auto ret = func(window);
ret->set_name(name);
return ret;
});
}
Essentially all I need to do is bind the variadic Args to T::create (which itself is a static variadic template method), but allow populating the first argument (window) separately when it's called.
The above code fails with the following error
error: no match for call to ‘(const std::_Bind<std::shared_ptr<{anonymous}::SceneWithArgs> (*(std::_Placeholder<1>, const char*))(smlt::Window*&, const char (&)[4])>) (smlt::Window*&)’
auto ret = func(window);
~~~~^~~~~~~~
When calling the code like this:
manager.register_scene<SceneWithArgs>("test", "arg");
I don't really understand the error, or how to fix it.
I initially solved this by simply calling create inside the lambda, and this works on GCC 4.9 and above, but I have to remain compatible with GCC 4.8.4 and there's a bug which prevents using variadic args inside a lambda :(
UPDATE
OK so adding std::decay (as seen in the comments) didn't fix the problem entirely, the first argument kept deducing to Window*&& rather than Window*, but actually specifying the type of func (e.g. std::function<SceneBasePtr (Window*)>) instead of using auto made things compile.
I'm not sure why that is though...
You did not show the declaration of create, but I assume it looks as follows:
template <typename... Args>
static SceneBasePtr create(Args&&...);
It looks good. You use forwarding references, so that the compiler can deduce the exact types of arguments. As long as the compiler deduces the types, this will work. However...
&T::template create<Window*, Args&...>
Here, you instantiate the function template explicitly. That is, the compiler will no longer deduce the types, instead, it will replace the template parameters by the template arguments you provide. What are these template arguments?
template <typename T, typename... Args>
void register_scene(const std::string& name, Args&&... args);
//...
manager.register_scene<SceneWithArgs>("test", "arg");
The first one is passed explicitly, it's Window*, this one is obvious. Now Args... -- "test" and "arg" are raw string literals, and their corresponding types are const char[5] and const char[4], respectively. Then Args... (after a deduction process) become const char(&)[5] and const char(&)[4]. Then what happens now, when you instantiate create with these types? You will end up with a declaration as below:
static SceneBasePtr create(Window*&&, const char(&)[5], const char(&)[4]);
Note that reference collapsing rules turn the first parameter into an r-value reference, while the rest are l-value references.
std::bind deduces types as well. However, std::bind will want to store the arguments, so that they can be reused later. The problem is that std::bind cannot store const char[5]. Instead, it will decay the type of each argument. This means that each raw string literal will become const char*, and will be passed as an l-value argument of this type to the bound function, and this does not match the manually instantiated create. A similar problem is with the window argument. It is deduced as an l-value, however, the manually instantiated create function expects an r-value.
It's best not to explicitly specify template types of a function template. If for some reasons (bug?) you cannot use args... inside the lambda, you can generate a proper create signature:
&T::template create<Window*&, typename std::decay<Args>::type&...>
// ~^~ ~~~~~~~~~^
To end up with:
static SceneBasePtr create(Window*&, const char*&, const char*&);
This question is a follow-up of How to deduce the type of the functor's return value?
I'm reformulating it in a more abstract way.
Given the pseudocode of a template function
template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(<decl-expr>)
{
// do something
// ............
return fn(<ret-expr>)
}
where <ret-expr> is an arbitrary expression which involves arg, what shall I use for <decl-expr> to set the return type of ComputeSomething equal to the return type of the functor.
The functor may be a class, a lambda or a function pointer.
Partial solutions I found so far.
(a) The answer for my linked question done by ecatmur. Essentially, it is repeating the return statement in <decl-expr>. Problems: it is error-prone and wouldn't work if contains local variables.
(b) It works only for function pointers
template <typename Arg, typename Ret>
Ret ComputeSomething(Arg arg, Ret(*fn)(Arg))
(c) It assumes that the argument of the functor is of type Arg (which may not hold in general) and requires Arg to be default-constructible
template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(Arg())
(d) Using std::declval which is supposed to lift the default-constructible restriction, as suggested in how to deduce the return type of a function in template. Could anybody explain how it works?
template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(std::declval<Arg>())
Use result_of. It is backwards compatible and takes all the ugly declval pain out of your code. You still need to remember to add rvalue reference qualifiers (&&) if you actually just forward values.
Something else I find important: Your function forwards arguments to another function. In such cases you should always use rvalue references to pass the arguments.
If all you are trying to do is improve maintainability: there are several attempts at a RETURNS macro around that try to minimize the repetition between the return type declaration and the actual return expression, but I haven't seen any that allows a function body that contains more than the actual return statement.
As for how declval works: Its compiler dependent. It isn't allowed to occur in an evaluated content and its argument can be an incomplete type. See 20.2.4
std::declval is a function template that is only declared (not defined). It can thus only be used in unevaluated contexts such as the argument to sizeof and decltype. It is declared to return an rvalue of the specified type. This allows you to use it to manufacture a dummy parameter for a function call in a decltype expression.
e.g.
typedef decltype(fn(std::declval<Arg>())) t;
declares t to be the type of the result of calling fn with an rvalue of type Arg. This is similar to your case (c) (fn(Arg())), but it doesn't require anything of Arg, so it works on types without default constructors.
If your return expression uses a local variable of type foo, then you can use decltype(fn(std::declval<foo>())), again regardless of how you construct a foo.
If you need an lvalue, such as a named object or an lvalue reference, then you can use std::declval<foo&>(). This allows you to handle the case where the type depends on whether you have an lvalue or an rvalue.
Here's my own solution, the best I could get
template <typename Arg, typename Fn>
typename std::result_of<Fn(Arg)>::type ComputeSomething(Arg arg, Fn fn)
To make (c) works for anything, you need 2 overloads. 1st as shown in (c), 2nd:
template <typename Arg, typename Ret>
Ret ComputeSomething(Arg arg, std::function<Ret(Arg)> fn)
Also, as gcc bug 54111 shows - deduction of return type is very unreliable.
A variant of (b) working not only with function pointers should be something like
template<typename Arg, typename Ret>
Ret ComputeSomething (Arg arg, function<auto (Arg) -> Ret> f)
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);
}