Do lambda function have a signature so that I can pass them around?
template<class Fn>
HRESULT foreach(Fn _fuction)
{
}
object->foreach(
[param1, param2] (int item)
{
}
);
object->foreach(
[param1, param2, param3] (int item)
{
}
);
I want to use a typedef function instead of templates, I think the type of the function will strict the formal parameters but will allow captured list to vary.
can I use typedef instead of templates to pass lambda functions around?
something like:
typedef void (*Fn)(int);
but allows lambda not function pointer.
I tried using std::fuction but seems not working.
The type of the lambda-expression (which is also the type of the closure object) is a unique, unnamed non-
union class type — called the closure type ...
(C++11 §5.1.2/3, emphasis mine)
My reading of this is that even if two lambda-expressions have the same parameter list, return type, and capture list, they nevertheless have different types.
auto f = []{};
typedef decltype(f) lambda_type;
void g(lambda_type lambda) { lambda(); }
int main() {
g(f); // OK
g([]{}); // error: type mismatch
}
So no, what you are trying to do cannot be done. As others have suggested, try using std::function, or retain the template (what's wrong with templates, anyway?)
The actual type of a lambda is unspecified. But they are convertible to function; in your case, std::function<void(int)> (void is the return type, the parameter types go in the parentheses).
Related
void(*)(void) is a function pointer while I suppose void(void) is also a way to represent function type. It is used as template parameter in std::function <functional>
What is void(void) and how it is different from void(*)(void)?
What is void(void) and how it is different from void(*)(void)?
They are distinct types. Although the former(void(void)) can be implicitly converted to the latter(void(*)(void)) in many contexts.
The type void(void) is called a function type. The following are also function types:
int (int, int) //this is a function type
void(int) //this is a function type as well
On the other hand, void (*)(void) is a pointer to a function type. That is, it is a pointer that points to a function with 0 parameters and return type of void.
TL;DR
The void(void) is a specification of a function type, it's
signature.
The void(*)(void) is a pointer to function.
These are distinct.
First, let's start by saying, that sometimes the void(void) will be automatically treated as a pointer to function (i.e. in a parameter list). We still can be explicit and use the void(*)(void), but the void(void) will be an equivalent in these cases.
// Here, we explicitly state that the f is a pointer to function
void foo(void (*f)(void), int i);
// Equivalent, f is automatically treated as a pointer to the function
void foo(void f(void), int i);
This will not be the case for the mentioned std::function for example. The types of the two are different, yet may have similar behavior (i.e. we can use the function call operator on a pointer to function).
void bar();
// Prints 1
std::cout << std::is_same<void(void), decltype(bar)>::value << '\n';
// Prints 1 as well
// Note the pointer type specifier
std::cout << std::is_same<void(*)(void), decltype(bar)*>::value << '\n';
// Prints 0
// Note the absence of the pointer type specifier
std::cout << std::is_same<void(*)(void), decltype(bar)>::value << '\n';
And particularly:
// Ok
std::function<decltype(bar)> fn1 = bar;
// error: variable ‘std::function<void (*)()> fn2’ has initializer but incomplete type
std::function<decltype(bar)*> fn2 = bar;
Note, that we can however "store" a pointer to a member function in std::function, but even then the template's parameter still won't be of a pointer to function type, but a plain function signature.
struct MyType {
void func(int) {}
};
int main() {
// Note, we can't have a member function without an instance of the type
// Thus we specify the usually implicit first param and the explicit int param
std::function<void(MyType&, int)> fn_mem = &MyType::func;
MyType my_object;
fn_mem(my_object, 21);
}
For more on std::function please refer to the reference. In short, the use of the std::function instead of the function pointers has the same moto as using the smart pointers instead of the raw pointers.
You may wonder on other uses of the void(void) or int(int) style function type specifications, so here's another example you may see often:
using func_t = int(int);
// func_ptr_func return a pointer to the func_t type alias
func_t* func_ptr_func();
N.B. unlike in the case of a parameter, the compiler won't treat the function type in a return as a pointer to function type. Thus, we must explicitly specify the pointer type in case of the return.
// Here, baz is a function that doesn't take any agruments
// And returns a pointer to a function that takes an int argument and "returns" void
void (*baz())(int);
for void fun(){}, std::is_same_v<void(void)>, decltype(fun)> is true; and std::is_same_v<void(*)(void)>, decltype(&fun)> is true. In fact, those are their real types. However, the standard approve you convert an object that has type void(void) to void(*)(void) implicitly (so you can even write (******funptr)()), that's why we always confuse them.
I'm trying to design a C++ macro that needs to look something like this:
#define MY_MACRO(OBJECT, METHOD) \
[](BaseClass* obj) \
{ \
return static_cast<decltype(OBJECT)>(obj)->METHOD();\
}
Basically, a macro that translates into a lambda that calls a given method on a given object. But the lambda needs to take a base class of the object as a parameter (My use case guarantees that the cast will always work). Furthermore, the method to be called might not be on the base class.
The usage for this macro is that I have another method which I cannot modify declared as:
void Foo(std::function<int(BaseClass*)>);
and I need to be able to call it using my macro as a parameter like so:
T x;
Foo(MY_MACRO(x, method)); // match std::function<int(T*)>
However, the macro code doesn't work because I'm not capturing OBJECT, so it's not in scope when I need to pass it to decltype. Conceptually though, all the information the compiler needs is there... How can I do this? Is it possible?
A few constraints:
The lambda's parameter needs to be BaseClass. I can't make it decltype(OBJECT).
My situation does not allow me to capture OBJECT.
I don't have access to the C++14 feature of generalized lambda captures.
I need access to the type of the object without capturing it.
You can do it directly. You are required to capture only when you odr-use the named entity, and unevaluated operands, like those of decltype, don't odr-use anything. This is perfectly fine:
void f(){
int x;
[]{ decltype(x) y = 0; };
}
You can add an optional parameter to the lambda with the type that you want, and use decltype on that parameter. Here's an example of the pattern, minus the macro:
int main() {
int foo = 4;
auto lambda = [](double* bar, decltype(foo)* TP = nullptr) {
return static_cast<std::remove_pointer<decltype(TP)>::type>(*bar);
};
double x = 5;
return lambda(&x);
}
I get a pointer to decltype(foo) here because pointer types can easily be defaulted to nullptr to ensure that the parameter is optional. If decltype(foo) already resolves to a pointer type, as in your case if I got it right, you wouldn't need it (and the remove_pointer).
Here's an attempt:
template <typename T>
auto lambda_maker(int (T::* MF)())
{
return [](T* p) -> int { return (p->*MF)(); };
}
#define MY_MACRO(OBJ, METH) lambda_maker<decltype(OBJ)>(METH)
What is the type of the lambda expression in (1) ?
Why can this code compile?
#include<functional>
#include<iostream>
int main() {
std::cout <<
[](auto&& f0,auto&& a0){return f0(f0,a0);}
(
[](auto& f,auto&& a)->int{ return (a>1) ? f(f,a-1)*a : 1; }, // (1)
5
)
<< std::endl;
}
I think that infinite recursion is caused by type inference for lambda expression (1) in this code.
I think that auto& f is replaced to a type name such as std::function<int(std::function<int(std::function<int(......)>)>)>.
Please point out my mistake.
First mistake: std::function is a type unrelated to any lambda.
A lambda is an anonymous type with an operator() and a few other known properties.
std::function<R(Args...)> is a type erasure class for copy construct, destroy and invoke with Args... and return R. It can be constructed from a lambda, but is not otherwise a related type.
As you cannot name the type of a lambda, using a std::function to store it is common. The lambda is not a std::function however. std::functions have nearly unavoidable overhead from their type erasure and polymorphism: lambdas lack any polymorphism, which makes it really easy for the compiler to understand what () does at the point of invocation.
In your case you have two lambdas.
Your first lambda is:
[](auto&& f0,auto&& a0){return f0(f0,a0);}
This looks like a form of y-combinator, or a variant, used help with recursion. The operator() in this case has signature:
template<class F0, class A0>
auto operator()(F0&&,A0&&)const
-> std::result_of_t<F0&(F0&,A0&)>
roughly.
A more useful version (in my opinion) is:
[](auto&& f0){
return [f0=std::forward<decltype(f0)>(f0)]
(auto&&...args) {
return f0(f0, std::forward<decltype(args)>(args)...);
};
}
which takes an f0, stores it, and invokes it with any arguments passing f0 first. This lets you bind the recursion 'out of sight'. Making the inner lambda mutable is optional (depends if you want to invoke in a const context)
Anyhow, the next lambda:
[](auto& f,auto&& a)->int{ return (a>1) ? f(f,a-1)*a : 1; }
has an operator() signature of:
template<class F, class A>
auto operator()(F&,A&&)const
-> int
You then pass an instance of the second lambda to the first, plus an argument, and it calculates n!.
The types deduced by the template operator () do not depend on the types that the arguments themselves deduce, so there is no infinite type deduction problem. The return type of the inner lambda is hard coded to int, so you don't have to deduce what () recursively returns to know it returns int.
If you want to store the first lambda in a std::function, however, you are going to be disappointed. std::function cannot erase a template operator(): it can only erase a fixed signature, and a template member is a factory of methods, not a method itself.
However, remember my better version of y combination above?
Call your first lambda g, your second h and my lambda y and the lambda my lambda returns z.
Then g(h,x) = y(h)(x) -- and y(h) can be stored in a std::function<int(int)> no problem. We hide the part of the recursion that basically requires a recursive type signature, which std::function does not support1. What is left, while it has a template operator(), can be bound to a simple signature.
1 note that you could write std::function to support recursive signatures, like std::function< std::vector<SELF_TYPE>(int) >. You can see how this might work with how boost::variant works with recursive variants.
From [expr.prim.lambda], emphasis mine:
The
lambda return type is auto, which is replaced by the trailing-return-type if provided and/or deduced from
return statements as described in 7.1.6.4.
You provide a trailing-return-type, that is the ->int in your code, so no type deduction has to happen. The return type is just int.
However, even without the ->int, you can still get your function to compile if you just provided a if statement instead of using the conditional operator:
auto f = [](auto& f0, auto&& a) {
if (a <= 1) {
return 1; // this *must* be the first return case.
}
else {
return f0(f0, a-1) * a;
}
};
std::cout << f(f, 5) << std::endl; // prints 120
This case, and only this case, fits one of the rules as above mentioned in §7.1.6.4 [dcl.spec.auto]:
If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression,
the program is ill-formed. Once a return statement has been seen in a function, however, the return type
deduced from that statement can be used in the rest of the function, including in other return statements.
[Example:
auto sum(int i) {
if (i == 1)
return i; // sum’s return type is int
else
return sum(i-1)+i; // OK, sum’s return type has been deduced
}
—end example ]
So, defining a predicate inside a function and use as std::list::remove_if argument is not allowed?
Consider the following code, which fails to compile:
struct a { };
int main()
{
struct pred { bool operator()( const a& ) { return false; } };
std::list< a > l; // fill l
l.remove_if( pred() );
return 0;
}
error: no matching function for call to
‘std::list<a, std::allocator<a> >::remove_if(main()::pred)’
Now, if I replace l.remove_if( pred() ); with
pred()( *l.begin() );
// or
pred p;
p( *l.begin() );
which remove_if does internally, it compiles and works as expected.
And even more: if I move struct pred to be defined outside main, both tests work as expected.
This doesn't make any sense to me.
I thought that it could be something with dependent names and ADL and things like this, but... the argument of remove_if is an instance, not a type. It's true, that this is a template function and the argument's type is still resolved, but..
Can somebody explain what and why happens?
The answer to your very first question is that yes, prior to C++11 some types (such as local types) are NOT allowed as template parameters. See 14.3.1/2:
A local type, a type with no linkage, an unnamed type or a type
compounded from any of these types shall not be used as a
template argument for a template type parameter.
Since remove_if is a template, you cannot use the local predicate as its parameter.
Consider a template class like:
template<typename ReturnType, ReturnType Fn()>
class Proxy
{
void run()
{
ReturnType ret = Fn();
// ... do something ...
}
};
// and a functions
int fn1() { return 5; }
float fn2() { return 5; }
This can be instantiated by using:
Proxy<int, &fn1> p1;
But explicitly declaring the return value type seems needless. What I am trying to achieve is something like:
someProxyInstantation<&fn1> p1;
someProxyInstantation<&fn2> p2;
Unfortunately, I'm no c++ expect and this seems like a hidden corner of the language (at least for me).
If I could just get from the pointer to the function to its type - something like:
std::tr1::result_of<&fn>::type // Error 1 error C2923: 'std::tr1::result_of' : 'fn1' is not a valid template type argument for parameter '_Fty'
the error makes sense since the parameter is not a "type" at all
C++0x has the decltype(&fn1) but that is years away.
Any way of doing this in C++03 (+ tr1)?
Restrictions:
- I don't want to pass the functor, f1 and f2 have to remain global functions that have a return value (can't move it to parameter).)
This isn't possible in C++03. If you want to pass a function pointer as a non-type parameter, the compiler has to know the type of the parameter. So you have to provide the missing pieces (in this case, the return type). You can give the proxy the function pointer as a value at runtime, and provide it with the type of it as the only argument. Then you could write a generator function for you that does this job:
template<typename T>
Proxy<T> make_proxy(T t) { return Proxy<T>(t); }
Sadly, in current C++, you still have to give it the type in order to assign to a automatic variable:
Proxy<int(*)()> p = make_proxy(&fn1);
You can't use auto p = make_proxy(&fn1); yet. Note that if you want to use a function type on the left side, you have to change the generator function to provide not a function pointer type:
template<typename T>
Proxy<typename boost::remove_pointer<T>::type> make_proxy(T t) {
return Proxy<typename boost::remove_pointer<T>::type>(t);
}
Now you can do
Proxy<int()> p = make_proxy(&fn1);
using the proxy, you can now just do
doSomething(make_proxy(&fn1));
And if doSomething is templated or otherwise polymorphic, it will not require you to know the exact type of the function.