Question: How can I retrieve the return type of a function to use in a template?

Question

How can I retrieve the return type of a function to use in a template?

Answers 4
Added at 2016-12-31 11:12
Tags
Question

I have a function somewhere called x that returns a know value and has known parameters:

int x(int y);

I have somewhere else, I want to create a container to contain n invocations of this function. Then I want to execute it that many times.

The problem is, I don't want to rely on it being an int return type. I need to deduce the return type at compile time. Something like:

std::vector<result_of<x(int)>::type> results;

But I don't want to have to specify the parameter values because they're static.

Answers
nr: #1 dodano: 2016-12-31 12:12

You are close. Assuming T is the template argument of the caller function:

std::vector<decltype(x(std::declval<T>()))> results;
nr: #2 dodano: 2016-12-31 12:12

You can misuse std::function::result_type:

int x(int y);
static_assert(std::is_same_v<int,std::function<decltype(x)>::result_type>);

Of course this will only work if x is really a function. If x is an arbitrary function object, then its result type may depend on its argument type, in which case you cannot know its result type without specifying the arguments.

nr: #3 dodano: 2016-12-31 12:12

You can create your own traits, something like:

template <typename F> struct my_result_of;

template <typename F> struct my_result_of<F*> : my_result_of<F> {};

template <typename Ret, typename ... Ts>
struct my_result_of<Ret(Ts...)>
{
    using type = Ret;
};

template <typename F> using my_result_of_t = typename my_result_of<F>::type;

And use it like (assuming no overloads of x):

std::vector<my_result_of_t<decltype(x)>::type> results;
nr: #4 dodano: 2016-12-31 15:12

I assume you can use up to the most recent standard revision, for you didn't specify it.
Here is a minimal, working example:

#include<vector>
#include<functional>
#include<utility>

template<std::size_t... I, typename F, typename... A>
auto gen(std::index_sequence<I...>, F &&f, A... args) {
    return std::vector<decltype(std::forward<F>(f)(args...))>{
        (I, std::forward<F>(f)(args...))...
    };
}

template<std::size_t N, typename F, typename... A>
auto gen(F &&f, A... args) {
    return gen(std::make_index_sequence<N>{}, std::forward<F>(f), args...);
}

int f(int, char) { return 0; }

int main() {
    auto vec = gen<10>(&f, 0, 'c');
}

Return type of your function is easily deduced by:

decltype(std::forward<F>(f)(args...))

I want to create a container to contain n invocations of this function. Then I want to execute it that many times.

To do that, I used an std::index_sequence to create a parameter pack having the right size.
Then the vector itself is initialized as it follows:

(I, std::forward<F>(f)(args...))...

The basic idea is to exploit the comma operator to unpack the parameter pack mentioned above and execute N times the function. The values returned by the invokations of f are used to fill the vector.

Note that args are not perfectly forwarded to f.
It could cause problems in case of moveable objects consumed during the first execution.

Source Show
◀ Wstecz