Matching a C++ lambda expression in templates - c++

I am trying to match callable objects in C++ templates and provide different implementations based on the type of callable object that is receives as an argument.
I have the following template functions:
template<class Ret, class... Args>
void fun(std::function<Ret(Args...)> f) {}
template<class Ret, class... Args>
void fun(Ret(*f)(Args...)) {}
They accept std::function objects and ordinary function pointers, and correctly deduce their return Ret and argument Args types. However, I am having difficulty passing anonymous lambda expressions as they do not match any of the templates above.
Calling something like
fun([](int x, int y){return x + y;})
results in a compilation error. What is the correct way to implement a template expression that accepts anonymous lambda functions? Maybe one way is to check if the class that is being passed has an operator() method?

Conversions happen after template argument substitution. The type of a lambda is not std::function, but an anonymous type. Indeed, you'll have the receive an anonymous lambda in order to receive any callable object.
It seems your concern is about restricting the object received to have the operator() defined. This problem is easily solvable with expression sfinae. However, since you're not receiving parameters, it will be harder to check if the expression is valid.
This is not a general example, but it will work for any lambda that does not have auto as one of it's parameter, or any object that has operator() not overloaded:
template<typename F>
auto fun(F f) -> void_t<decltype(&T::operator())> {
// ...
}
With void_t implemented as follow:
template<typename...>
using void_t = void;
The expression inside the decltype must be valid in order for the function to exist.
If you want your code to work with more types, you'll have to offer an overload that you can specify the parameters. The check is now trivial to implement:
// Another overload of your function
template<typename F, typename... Args>
auto fun(F f) -> void_t<decltype(f(std::declval<Args>()...)> {
// ...
}
That way, it will works for generic lambda and overloaded operators too.

This is valid C++ code, and gcc 6.1.1 at C++14 level will back me up on this:
template<typename F>
void foo(F &&f)
{
}
void bar()
{
auto lambda=[](auto a, auto b) {};
foo(lambda);
}
As you can see, a template can't possibly deduce the arguments of a lambda. The argument types to a lambda are not known until they are used.
You can deduce that something has an operator(), as the other answer shows, but that's pretty much as far as it goes.

Related

Strange inconsistency between function template and "normal" function

I have the two functions that are almost the same (with the exception that one of them is a template):
int* bar(const std::variant<int*, std::tuple<float, double>>& t)
{
return std::get<0>(t);
}
template <typename... Args>
int* foo(const std::variant<int*, std::tuple<Args...>>& t)
{
return std::get<0>(t);
}
Than, they are use like this:
foo(nullptr);
bar(nullptr);
The second one compiles and returns (int*)nullptr, but the first one doesn't (in Visual Studio 2019 using C++17 giving the error foo: no matching overload found). Why? Why does making this function a template cause it to cease to compile?
Using foo like below doesn't help either, so the inability to deduce Args is probably not the problem:
foo<>(nullptr);
In contrary, the following does work:
foo(std::variant<int*, std::tuple<>>(nullptr));
Is it possible to somehow avoid the need to write this in such a long manner?
Apparently if a type of a function parameter depends on a template parameter that has to be deduced (because it's not specified in <...>), then implicit conversions don't apply when passing an argument to that parameter.
Source:
The function parameters that do not participate in template argument
deduction (e.g. if the corresponding template arguments are explicitly
specified) are subject to implicit conversions to the type of the
corresponding function parameter (as in the usual overload
resolution).
A template parameter pack that is explicitly specified may be extended
by template argument deduction if there are additional arguments:
template<class ... Types> void f(Types ... values);
void g() {
f<int*, float*>(0, 0, 0); // Types = {int*, float*, int}
}
This also explains why foo<>(nullptr); still doesn't work. Since the compiler tries to deduce additional types to extend Args, in this case there doesn't seem to be any difference between foo(nullptr); and foo<>(nullptr);.
When a template function is considered it will only work for an exact match of the argument types at the call. This means that no conversions will be made (except for cv qualifiers).
A simple workaround in your case would be to make a function catch std::nullptr_t and forward that to your template.
int* foo(std::nullptr_t) {
return foo(std::variant<int*, std::tuple<>>{nullptr});
}
I would avoid this construct simply because the rules about exactly how the compiler will (if it even does it at all) resolve the overload are so confusing that I couldn't really tell you what it did without looking at a standards document, and code like that should be avoided. I would force the overload resolution you want this way instead:
template <typename... Args>
int *foo(const ::std::variant<int*, ::std::tuple<Args...>> &t)
{
return ::std::get<0>(t);
}
int *foo(int *ip)
{
using my_variant = ::std::variant<int *, ::std::tuple<>>;
return foo(my_variant{ip});
}
template <typename... Args>
int *foo(::std::tuple<Args...> const &t)
{
using my_variant = ::std::variant<int *, ::std::tuple<Args...>>;
return foo(my_variant{t});
}
template <typename... Args>
int *foo(::std::tuple<Args...> &&t)
{
using my_variant = ::std::variant<int *, ::std::tuple<Args...>>;
return foo(my_variant{::std::move(t)});
}

Implicit template argument is invalid, but compiles anyway

I have the following class:
class FunctionCallback
{
public:
static CallbackHandle Create(const std::function<void(void)> &function);
template<typename T,typename... TARGS>
static CallbackHandle Create(const std::function<T(TARGS...)> &function);
};
I then call 'Create' like this:
FunctionCallback::Create<void,float>([](float f) -> void {}); // Whether I use a lambda-function or a function pointer makes no difference
Even though that should be correct (?), visual studio underlines that line in read with the message:
Error: no instance of overloaded function "FunctionCallback::Create" matches the argument list
argument types are: (lambda []void (float f)->void)
However the program compiles fine in visual studio without any warnings or errors.
g++-5 is unable to compile it altogether with a similar message.
When changing it to:
FunctionCallback::Create<void,float>(std::function<void(float)>([](float f) -> void {}));
It doesn't display the message anymore and compiles both on windows and linux.
Why can't it deduce the type properly unless I explicitly specify it?
You almost never want to deduce the type of a type-eraser, that std::function is an example of. With a few exceptions, template type deduction attempts to deduce the exact type of an argument. For a std::function<T>-type parameter this means that the compiler can deduce T if the corresponding argument is a std::function as well. The type of a lambda expression is not a std::function.
Even if you explicitly specify type template arguments, the deduction still continues after corresponding types have been substituted with the arguments. In the case of a parameter pack, the compiler still tries to deduce the rest of arguments, so for FunctionCallback::Create<void,float> the compiler ends up with:
template <typename... TARGS>
static CallbackHandle Create(std::function<void(float, TARGS...)> function);
and this won't match anything else than std::function<void(float, TARGS...)> (or something that derives from this type); otherwise the deduction for TARGS fails.
If your goal is to always specify the parameter pack's types explicitly, you can put that pack (or the entire parameter) in a non-deduced context:
template <typename T> struct identity { using type = T; };
template <typename T> using identity_t = typename identity<T>::type;
struct FunctionCallback
{
template <typename T, typename... TARGS>
static void Create(identity_t<std::function<T(TARGS...)>> function);
};
FunctionCallback::Create<void, float>([](float f) -> void {});
DEMO
In this case, however, you'll have to pay the price of type-erasure. Instead, you can consider accepting any function object type:, letting the compiler to deduce the exact type of the argument:
struct FunctionCallback
{
template <typename F>
static void Create(F function){}
};
FunctionCallback::Create([](float f) -> void {});
DEMO 2

Can I use template type T in std::function to pass a lambda type function C++11?

I am new to c++11, I define a template to take a function as parameter,
template <typename T>
void print_for_each(vector<T> v, std::function<void (T)> func){
std::for_each(v.begin(), v.end(), func);
}
I tried to pass the following Lambda expression to print_for_each,
auto printElement = [](int y) {
std::cout << y << " ";
};
Then I received the compiler warn said,
error: no matching function for call to 'print_for_each'
Then I changed the template function to be,
std::function<void (int)> func)
This time works.
My question is, can std::function takes template type T ?
Thanks for answering it ! : )
Lambdas are not the same as std::functions. The former are functors (function objects) in disguise, whereas the latter are a different beast. Even though a lambda is "decay"-able to a std::function, in template type deduction the compiler does not perform implicit conversions. So in this case the compiler tries to match two objects of incompatible types, hence it fails.
You may wonder why the compiler does not perform such implicit conversions. That's because by performing type conversions on top of argument type deduction, one can arrive at lots of "paradoxical" situations, in which more than one incompatible type can be a candidate.
Possible solutions:
You can change the signature of your function to
template <typename T, typename F>
void print_for_each(vector<T> v, F&& func){
std::for_each(v.begin(), v.end(), func);
}
You can wrap the lambda in a std::function and passing the latter to your function (see #Brian's comment)
You can "help" the compiler and explicitly specify the template type parameter
print_for_each<int>(v, func);
In this case there is no template type deduction going on, and the lambda is allowed to decay to a std::function via an implicit conversion.
std::for_each does not require a std::function, just a functor.
Your template should do the same:
template< typename C, typename F >
void print_for_each( C& c, F f )
{
std::for_each( c.begin(), c.end(), f );
}

explicit call to variadic function template with empty parameter pack

Consider this simple (bad) function template for which lots of variations exist on this site:
template <typename R, typename... Args>
R call_with(std::function<R(Args...)> f,
Args... args)
{
return f(args...);
}
And two attempts at calling it:
call_with([]{}); // (a)
call_with<void>([]{}); // (b)
I cannot call (a) because a lambda is not a std::function<R(Args...)> so template deduction fails. Straightforward.
However, (b) also fails. I suspect this is because the compiler cannot determine that I mean to provide all the type arguments and reasons that I am simply providing R - so it is trying (and failing) to deduce Args... for the same reason that the initial call failed.
Is there a way to explicitly specify that I am providing all the template arguments? To clarify, I am interested only in how to explicitly provide the template arguments so that there is no template deduction - I am not looking for the correct way to write call_with or for a way to make template deduction succeed when called with a lambda.
The short answer to your--edited--question is: if you cannot change the declaration of call_with(), then either use the type casts demonstrated by #CoffeeandCode, or use the technique described below to create a wrapper for call_with().
The problem is in the fact that the compiler is trying to deduce the template arguments from the first function argument. You can prevent this if you write your code like this:
#include <functional>
#include <iostream>
// identity is a useful meta-function to have around.
// There is no std::identity, alas.
template< typename T>
struct identity
{
using type = T;
};
template <typename R, typename... Args>
R call_with( typename identity<std::function<R(Args...)>>::type f,
Args... args)
{
return f(args...);
}
int main()
{
call_with<void>([](int){ std::cout << "called" << std::endl; }, 2);
}
Using a template meta-function to "generate" the std::function type means that the compiler cannot even try to deduce the function type from the first argument and it will only use the other arguments.
You would still need to explicitly provide the return type of course, but for the other arguments you can now choose whether to explicitly specify them or leave it up to the compiler to deduce them from the arguments given.
If you really want to enforce that all template arguments are provided and not deduced, you can also wrap the argument pack in an invocation of identity this way:
template <typename R, typename... Args>
R call_with( typename identity<std::function<R(Args...)>>::type f,
typename identity<Args>::type... args)
In summary, if you want to prevent the compiler from deducing function template argument types that also appear as function parameters, wrap them in a meta function such as identity.
You could specify the function type beforehand, like this:
int main(){
std::function<void()> f = []{};
call_with(f);
}
or, in a little more messy but more compact way:
int main(){
call_with(static_cast<std::function<void()>>([]{}));
}
This is because the compiler doesn't know what return type and arguments to deduce for your template parameters before you ask it to implicitly convert a lambda, which is an unspecified function object defined by the compiler, to a std::function with those template parameters.
Really, you should just change your function warpper to be more generic:
template<typename Functor, typename ... Args>
auto wrapper(Functor &&f, Args &&... args) -> decltype(f(std::forward<Args>(args)...)){
return f(std::forward<Args>(args)...);
}
This should work for any function or functor type. It's also a really good example of the use of trailing return types.
Here's a live example

Accepting lambda as function parameter and extracting return type [duplicate]

This question already has answers here:
How to convert a lambda to an std::function using templates
(10 answers)
Closed 8 years ago.
I need to accept lambda function as a parameter and figure out its return type. So far, I couldn't come up with anything workable. Here is the example:
template <typename T1, typename T2>
T1 foo(T2 arg, std::function<T1(T2)> f)
{
return f(arg);
}
...
int n = foo(3, [](int a){ return a + 2; }); <-- ERROR!
How can it be done? Boost solution is OK, too.
You shouldn't pass std::function as a parameter. It has some overhead because it uses type erasure to store any type of callable, such as function pointers, functors, lambdas (which are just an automatically generated functor), etc. Instead, you should just template the function type. Then you can use a trailing return type to figure out the return type of the function:
template <typename T, typename Function>
auto foo(T arg, Function f) -> decltype(f(arg))
{
return f(arg);
}
An std::function and a lambda are not the same, they are distinct types. The former is a container that can store any type of callable object, while the latter is a callable object. Assigning a lambda to an std::function works because std::function has a constructor that performs the necessary conversion. But that's not the case with template argument deduction, which requires exact matches, and user defined conversions are not considered. That's why your code fails to compile.
The solution is simple, as Rapptz's answer states, modify foo to take the callable as a template argument.
template <typename Arg, typename Func>
auto foo(Arg arg, Func f)
-> decltype(f(arg))
{
return f(arg);
}