简体   繁体   中英

When defining a function, what type is a lambda function/expression?

I want to define a function that takes (besides its usual input arguments) a lambda function. And I want to restrict that function as far as possible (its own input- and return types).

int myfunc( const int a, LAMBDA_TYPE (int, int) -> int mylamda )
{
    return mylambda( a, a ) * 2;
}

Such that I can call the function as follows:

int input = 5;
myfunc( input, [](int a, int b) { return a*b; } );

What is the correct way to define myfunc ?

And is there a way to define a default lambda? Like this:

int myfunc( const int a, LAMBDA_TYPE = [](int a, int b) { return a*b; });

If you take a std::function<int(int,int)> it will have overhead, but it will do what you want. It will even overload correctly in C++14.

If you do not want type erasure and allocation overhead of std::function you can do this:

template<
  class F,
  class R=std::result_of_t<F&(int,int)>,
  class=std::enable_if_t<std::is_same<int,R>{}>
>
int myfunc( const int a, F&& f )

or check convertability to int instead of sameness. This is the sfinae solution. 1

This will overload properly. I used some C++14 features for brevity. Replace blah_t<?> with typename blah<?>::type in C++11.

Another option is to static_assert a similar clause. This generates the best error messages.

Finally you can just use it: the code will fail to compile if it cannot be used the way you use it.

In C++1z concepts there will be easier/less code salad ways to do the sfinae solution.


1 On some compilers std::result_of fails to play nice with sfinae. On those, replace it with decltype(std::declval<F&>()(1,1)) . Unless your compiler does not support C++11 (like msvc 2013 and 2015) this will work. (luckily 2013/3015 has a nice result_of ).

There are two alternatives, the type-erasing std::function<signature> forces a particular signature which goes along the lines of your question, but it imposes some additional cost (mainly it cannot be inlined in general). The alternative is to use a template an leave the last argument as a generic object. This approach can be better from a performance point of view, but you are leaving the syntax checking to the compiler (which I don't see as a problem). One of the comments mentions passing a lambda as a function pointer , but that will only work for lambdas that have no capture.

I would personally go for the second option:

template <typename Fn>
int myFunc(const int a, Fn&& f) {
   return f(a, a) * 2;
}

Note that while this does not explicitly force a particular signature, the compiler will enforce that f is callable with two int and yields something that can be multiplied by 2 and converted to int .

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