Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding out the return type of a function, lambda or function

Tags:

c++

c++11

This seems to be solved for the case of lambdas in this question. But that one is a 2011 answer and I'm looking for a general case: lambdas, regular functions, and functors. And, if possible, by means of the most modern c++ language additions. (Note: g++ -std=c++1y test.cpp).

So, given a function (or a lambda), I'm trying to find out what its return type is. For instance, to declare a variable (simplified case).

using namespace std;

template<typename F>
void test (F h) {
  // any of the following should be equivalent to int a; int b; int c;
  decltype(h) a; // <<<<< wrong
  result_of(h) b; // <<<<<< wrong
  result_of<decltype(h)> c; // <<<<<< wrong
}

int a_function (int i)  { 
  return 2*i; 
}

int main ()  {
  test (a_function);
}

Thanks.

like image 993
cibercitizen1 Avatar asked Jan 07 '15 15:01

cibercitizen1


People also ask

What is the return type of lambda function?

The return type for a lambda is specified using a C++ feature named 'trailing return type'. This specification is optional. Without the trailing return type, the return type of the underlying function is effectively 'auto', and it is deduced from the type of the expressions in the body's return statements.

What is return type of lambda function in python?

Lambda functions are syntactically restricted to return a single expression. You can use them as an anonymous function inside other functions. The lambda functions do not need a return statement, they always return a single expression.

Does lambda expression have return type?

A return statement is not an expression in a lambda expression. We must enclose statements in braces ({}). However, we do not have to enclose a void method invocation in braces. The return type of a method in which lambda expression used in a return statement must be a functional interface.

How do you return a value from a lambda function in C++?

Lambda expression in C++ Generally return-type in lambda expression are evaluated by compiler itself and we don't need to specify that explicitly and -> return-type part can be ignored but in some complex case as in conditional statement, compiler can't make out the return type and we need to specify that.


2 Answers

Assuming that:

  1. You want only the return type.
  2. You don't know what are/will be the types of arguments (so neither decltype() nor std::result_of<> is an option.
  3. The functor object passed as argument will not have an overloaded or generic operator().

then you can use the below trait that infers the return type of any functor object:

template <typename F>
struct return_type_impl;

template <typename R, typename... Args>
struct return_type_impl<R(Args...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(Args..., ...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(*)(Args...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(*)(Args..., ...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(&)(Args...)> { using type = R; };

template <typename R, typename... Args>
struct return_type_impl<R(&)(Args..., ...)> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...)> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...)> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) &> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) &> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) &&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) &&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) volatile> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) volatile> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) volatile&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) volatile&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) volatile&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) volatile&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const volatile> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const volatile> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const volatile&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const volatile&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args...) const volatile&&> { using type = R; };

template <typename R, typename C, typename... Args>
struct return_type_impl<R(C::*)(Args..., ...) const volatile&&> { using type = R; };

template <typename T, typename = void>
struct return_type
    : return_type_impl<T> {};

template <typename T>
struct return_type<T, decltype(void(&T::operator()))>
    : return_type_impl<decltype(&T::operator())> {};

template <typename T>
using return_type_t = typename return_type<T>::type;

Tests:

#include <type_traits>

template <typename F>
void test(F h)
{
    static_assert(std::is_same<return_type_t<F>, int>{}, "!");

    return_type_t<F> i = 1;
}

int function(int i) { return 2*i; }

int c_varargs_function(...) { return 1; }

struct A
{
    int mem_function(double, float) { return 1; } 
};

int main()
{
    // Function
    test(function);

    // C-style variadic function
    test(c_varargs_function);

    // Non-generic lambda
    test([](int i) { return 2*i; });

    // Member function
    test(&A::mem_function);
}

DEMO

like image 97
Piotr Skotnicki Avatar answered Oct 17 '22 22:10

Piotr Skotnicki


std::result_of<h(int)>::type a;

(Taken from here.)

like image 44
Andrey Mishchenko Avatar answered Oct 17 '22 22:10

Andrey Mishchenko