What Is the Type Signature of a C++11/1Y Lambda Function

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 than a?

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



Leave a reply



Submit