简体   繁体   中英

what is the type signature of a c++11/1y lambda function?

I was wondering if there is a standard way to get the type signature (ie the return type and the types) of its parameters of any given lambda?

The reason I ask is that I've always wondered what exactly is the type auto in the declaration like auto l =[](int x,int y)->int{return x+y;} . In other use cases of auto , it's a convenience and shorter alternative for a longer type name. But for lambdas, is there even an alternative way to declare the lambda variable?

My understanding is that a standard lambda is nothing more than a function object, and it is its own type. So, even if two lambdas have the same return type and parameter types, they are still two different, unrelated classes/functors. But this there a way to capture the fact that they are the same in terms of type signature?

I think the type signature I am looking for can be something like a std::function<> object of the correct types.

A more useful/involved question is, if it's possible to extract the type signature, this is possible to write a general wrapper function to convert any lambda function to a std::function object of the same type signature.

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;
}

You are correct the types of C++11 lambdas are anonymous and instance-unique. the std::function type can store references to any kind of lambda I have come across, but there is said to be a performance hit.

Try

std::function<int (int, int)> f = [](int x, int y) -> int { 
    return x + y; 
};

note the -> int can be omitted in non ambiguous scenarios such as this.

C++14 lets us write

std::function<int (int, int)> f = [](auto x, auto y) { 
    return x + y; 
};

which is handy for long type names.

As noted by @Jonathan Wakely, this approach captures a specific instantiation using std::function with fixed template arguments. In C++14, template variables can be specified. Additionally, also per C++14, lambda parameters can have can have their types inferred via auto , allowing for the following:

template<class T>
std::function<T (T, T)> g = [](auto x, auto y) -> auto {
    return x + y;
};

Currently, VC++, and GCC do not seem to support templates on variable declarations at function level, but allow them on member, namespace, and global declarations. I am unsure whether or not this restriction emanates from the spec.

Note: I do not use clang.

在C ++ 1y中,存在通用的lambda,并且没有单个调用签名( operator()()是模板)。

I was wondering if there is a standard way to get the type signature (ie the return type and the types) of its parameters of any given lambda?

No, there isn't.

The reason I ask is that I've always wondered what exactly is the type auto in the declaration like auto l =[](int x,int y)->int{return x+y;} .

It's an unspecified class type, created by the implementation. The whole point of lambdas is they are "anonymous functions" ie you do not know their type.

If you want a known type then write a function object type.

In other use cases of auto, it's a convenience and shorter alternative for a longer type name. But for lambdas, is there even an alternative way to declare the lambda variable?

No.

If you want to declare the type yourself then don't use a lambda expression.

My understanding is that a standard lambda is nothing more than a function object, and it is its own type. So, even if two lambdas have the same return type and parameter types, they are still two different, unrelated classes/functors.

Correct. Each lamda expression generates a unique type.

But this there a way to capture the fact that they are the same in terms of type signature?

No, there is no language feature to allow that.

I think the type signature I am looking for can be something like a std::function<> object of the correct types.

Even if it was possible in C++11, it would not help in C++14 where lambda expressions can take any number and any type of argument, eg [](auto... a) { }

And anyway, if you don't know the call signature of your lambda function then I would say you are using lambdas wrong. When you write the lambda you should know what its properties are, so either use it right away or put it in a std::function (or some other type that captures its call signature) as early as possible, when you know its properties. If you're creating lambdas and using them non-locally where you don't know the call signature, you're doing it wrong.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM