Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return type of a C++ lambda

Tags:

c++

c++11

lambda

I'm writing a template function that takes a function object (for now, a lambda) as a parameter, with lambda datatype as a template parameter, and returns the same type the lambda returns. Like this:

template<typename TFunctor, typename TReturn>
TReturn MyFunc(TFunctor &Func, TReturn) //The second arg is just to keep the template spec happy
{
    return Func();
}

And the consumption code goes like this:

int n = MyFunc([](){return 17;}, int());

I don't like the ugly way the return datatype is specified. Is there some built-in typedef in the compiler-generated lambda class that would give me its return type? So that MyFunc can go somehow like this:

template<typename TFunctor>
TFunctor::return_type MyFunc(TFunctor &Func)
{ //...

I want it to return the same type that lambda returns without explicitly spelling out that type.

EDIT: for now, all lambdas I'm concerned with are argumentless. Variable capture does the trick just as well.

like image 359
Seva Alekseyev Avatar asked Jan 30 '14 17:01

Seva Alekseyev


2 Answers

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);
like image 88
leemes Avatar answered Sep 29 '22 20:09

leemes


@leemes answer is nice but as a bonus you can use since C++17 the built-in std::invoke_result_t:

#include <type_traits>
#include <iostream>
 
struct S {
    double operator()(char, int&);
    float operator()(int) { return 1.0;}
};
      
int main()
{   
    // std::invoke_result uses different syntax (no parentheses)
    std::invoke_result<S,char,int&>::type b = 3.14;
    static_assert(std::is_same<decltype(b), double>::value, "");
}
 
like image 26
fouronnes Avatar answered Sep 29 '22 21:09

fouronnes