what is the type signature of a c++11/1y lambda function?
According to Can the 'type' of a lambda expression be expressed?, there is actually a simple way in current c++ (without needing c++1y) to figure out the return_type and parameter types of a lambda. Adapting this, it is not difficult to assemble a std::function
typed signature type (called f_type
below) for each lambda.
I. With this abstract type, it is actually possible to have an alternative way to auto
for expressing the type signature of a lambda, namely function_traits<..>::f_type
below. Note: the f_type
is not the real type of a lambda, but rather a summary of a lambda's type signature in functional terms. It is however, probably more useful than the real type of a lambda because every single lambda is its own type.
As shown in the code below, just like one can use vector<int>::iterator_type i = v.begin()
, one can also do function_traits<lambda>::f_type f = lambda
, which is an alternative to the mysterious auto
. Of course, this similarity is only formal. The code below involves converting the lambda to a std::function
with the cost of type erasure on construction of std::function
object and a small cost for making indirect call through the std::function
object. But these implementation issues for using std::function
aside (which I don't believe are fundamental and should stand forever), it is possible, after all, to explicitly express the (abstract) type signature of any given lambda.
II. It is also possible to write a make_function
wrapper (pretty much like std::make_pair
and std::make_tuple
) to automatically convert a lambda f
( and other callables like function pointers/functors) to std::function
, with the same type-deduction capabilities.
Test code is below:
#include <cstdlib>
#include <tuple>
#include <functional>
#include <iostream>
using namespace std;
// For generic types that are functors, delegate to its 'operator()'
template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
//enum { arity = sizeof...(Args) };
typedef function<ReturnType (Args...)> f_type;
};
// for pointers to member function
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) > {
typedef function<ReturnType (Args...)> f_type;
};
// for function pointers
template <typename ReturnType, typename... Args>
struct function_traits<ReturnType (*)(Args...)> {
typedef function<ReturnType (Args...)> f_type;
};
template <typename L>
typename function_traits<L>::f_type make_function(L l){
return (typename function_traits<L>::f_type)(l);
}
long times10(int i) { return long(i*10); }
struct X {
double operator () (float f, double d) { return d*f; }
};
// test code
int main()
{
auto lambda = [](int i) { return long(i*10); };
typedef function_traits<decltype(lambda)> traits;
traits::f_type ff = lambda;
cout << make_function([](int i) { return long(i*10); })(2) << ", " << make_function(times10)(2) << ", " << ff(2) << endl;
cout << make_function(X{})(2,3.0) << endl;
return 0;
}
Is there a way to shorten the C++11 lambda signature in declaration?
Looks like you're looking for an empty lambda which does nothing, so that your std::function
object will always be in callable state!
If so, then use this one which can be reused, for any number of parameters:
static const struct empty_lambda_t //static and const applies to the object!
{
template<typename ...T>
void operator()(T && ... ) const {} //does nothing
}empty_lambda {}; //declare an object which is static and const
And then use it as:
std::function<void()> fun1 = empty_lambda;
std::function<void(int,int)> fun2 = empty_lambda;
std::function<void(whatever)> fun3 = empty_lambda;
Hope that helps.
Is it safe to assume that identical lambda expressions have different types?
Is the compiler allowed to let
c
be of the same type thana
?
No. [&counter](){counter++;}
is a lambda expression and per [expr.prim.lambda.closure]/1:
The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.
So, for each lambda expression, even if it is identical to a previous one, you will get a unique type.
You can use typeid
to check that this is the case like:
#include <iostream>
#include <typeinfo>
template <typename T> void once(T t){
static bool first_call = true;
std::cout << typeid(t).name() << std::endl;
if (first_call) {
t();
}
first_call = false;
}
int main() {
int counter = 0;
auto a = [&counter](){counter++;};
once(a);
once(a);
std::cout << counter << std::endl; // 1
auto b = a; // same type
once(b);
std::cout << counter << std::endl; // 1
auto c = [&counter](){counter++;}; // different type
once(c);
once(c);
std::cout << counter << std::endl; // 2
}
result:
Z4mainEUlvE_
Z4mainEUlvE_
1
Z4mainEUlvE_
1
Z4mainEUlvE0_
Z4mainEUlvE0_
2
and you can see there are two function template instantiations.
Return type of a C++ lambda
Since the return type can depend on the arguments given to the functor, you need to specify them somewhere in order to query the return type. Therefore, when speaking of generic functors (not restricting them to (non-generic) lambdas), it's not possible to determine the return type when not knowing the types of the arguments.
C++11 has the keyword decltype
which can be used in conjunction with a trailing return type in order to specify the return type of your function by naming an expression which can depend on the function arguments (here, it depends on what Func
is):
template<typename TFunctor>
auto MyFunc(TFunctor &Func) -> decltype(Func(/* some arguments */))
{ ... }
So if you were to call it for example with no argument (I assume this when looking at your lambda example), simply write:
template<typename TFunctor>
auto MyFunc(TFunctor &Func) -> decltype(Func())
{
return Func();
}
In C++14, you can even completely omit the return type and simply write
template<typename TFunctor>
auto MyFunc(TFunctor &Func)
{
return Func();
}
Note that even in C++03, you don't have to provide another function argument; another template argument is enough:
template<typename TReturn, typename TFunctor>
TReturn MyFunc(TFunctor &Func)
{
return Func();
}
int n = MyFunc<int>(someFunctorReturningAnInt);
Can the 'type' of a lambda expression be expressed?
No, you cannot put it into decltype
because
A lambda-expression shall not appear in an unevaluated operand
You can do the following though
auto n = [](int l, int r) { return l > r; };
std::set<int, decltype(n)> s(n);
But that is really ugly. Note that each lambda expression creates a new unique type. If afterwards you do the following somewhere else, t
has a different type than s
auto m = [](int l, int r) { return l > r; };
std::set<int, decltype(m)> t(m);
You can use std::function
here, but note that this will incur a tiny bit of runtime cost because it needs an indirect call to the lambda function object call operator. It's probably negligible here, but may be significant if you want to pass function objects this way to std::sort
for example.
std::set<int, function<bool(int, int)>> s([](int l, int r) { return l > r; });
As always, first code then profile :)
How to make C++11 functions taking function<> parameters accept lambdas automatically
Finally figured out a generic wrapper function make_function
(in current c++11) for converting any lambda to its corresponding std::function
object with type deduction. Now instead of using ctor:
map(function<int (int)>( [](int x) -> int { return x;} ), {1,2,3});
which requires giving the same type information twice, the following succinct form works
map(make_function([](int x) -> int { return x;}),a); //now OK
Code is below:
#include <vector>
#include <functional>
using namespace std;
template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const> {
typedef function<ReturnType (Args...)> f_type;
};
template <typename L>
typename function_traits<L>::f_type make_function(L l){
return (typename function_traits<L>::f_type)(l);
}
template <typename A,typename B>
vector<B> map(std::function<B (A)> f, vector<A> arr) {
vector<B> res;
for (int i=0;i<arr.size();i++) res.push_back(f(arr[i]));
return res;
}
int main () {
vector<int> a = {1,2,3};
map(make_function([](int x) -> int { return x;}),a); //now OK
return 0;
}
--original answer--
To answer my own question after a couple of weeks' search (and getting chastised for using std::function<> as parameters), probably the best way I can find to have function<>-typed parameters accept lambda's (in c++11) is simply via explicit cast:
map((function<int (int)>) ([](int x) -> int { return x;} ), {1,2,3});
Or using ctor:
map(function<int (int)>( [](int x) -> int { return x;} ), {1,2,3});
For comparison, if you have a function taking std::string (e.g. void ff(string s) {...}
), it can take const char*
automatically. (ff("Hi")
would work). The automatic conversion from lambda to std::function<>
does not similarly work in c++11 (, which is unfortunate, IMO).
Hopefully, things will improve in c++14/1y when lambdas can be properly typed or better type-deduced.
How to infer a function type parameter in a template function with a lambda passed as argument?
The actual problem is that a lambda function has it own type, that cannot be reduced to R(&)(T)
. Because of that, C<T>
is an incomplete type as correctly outlined by the compiler.
As long as you use non-capturing lambdas, you can rely on the fact that they decay to function pointers and do this:
auto p3 = makeC(*(+[](int a){return a;}));
Or this:
template<typename T>
auto makeC(T&& fun) -> C<decltype(*(+std::forward<T>(fun)))> {
return C<decltype(*(+std::forward<T>(fun)))>(std::forward<T>(fun));
}
Another possible solution that works with capturing lambdas is this:
#include <utility>
#include <string>
template<typename T>
class C: T {
template<typename F>
C(F&& fun): T{std::forward<F>(fun)} {}
};
template<typename R, typename T>
class C<R(&)(T)> {
public:
template<typename F>
C(F&& fun) {}
};
template<typename T>
C<T> makeC(T&& fun) {
return C<T>(std::forward<T>(fun));
}
int foo(int a){return a;}
int main() {
auto p1 = makeC(foo);
auto p2 = C<int(&)(int)>([](int a){return a;});
auto p3 = makeC([](int a){return a;});
}
This way, when dealing with a lambda, C
actually inherits from it and privately contains its operator()
.
Function overload using lambda function signature
Your compiler is correct according to C++11. In C++14, a rule is added that says that the constructor template shall not participate in overload resolution unless the type of the argument is actually callable with the std::function
's argument types. Therefore, this code is supposed to compile in C++14, but not in C++11. Consider this to be an oversight in C++11.
For now, you can work around this by explicit conversion:
foo(std::function<int()>([](){return 3;}));
Related Topics
Getting the Current Time (In Milliseconds) from the System Clock in Windows
Proper Way of Casting Pointer Types
C++ Cannot Convert from Base a to Derived Type B via Virtual Base A
How to Define a Move Constructor
Function Template with an Operator
Overload Resolution and Arrays: Which Function Should Be Called
Using Opengl Glutdisplayfunc Within Class
C++ Object Instantiation VS Assignment
Using Continue in a Switch Statement
How to Perform a Pairwise Binary Operation Between the Elements of Two Containers
Why Gcc Does Not Use Load(Without Fence) and Store+Sfence for Sequential Consistency
Any Way Faster Than Pow() to Compute an Integer Power of 10 in C++
Getting a Buffer into a Stringstream in Hex Representation:
Vector <Unsigned Char> VS String for Binary Data