Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic way to deduce the return type of a functor?

This question is a follow-up of How to deduce the type of the functor's return value? I'm reformulating it in a more abstract way.

Given the pseudocode of a template function

template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(<decl-expr>)
{
// do something
// ............
return fn(<ret-expr>)
}

where <ret-expr> is an arbitrary expression which involves arg, what shall I use for <decl-expr> to set the return type of ComputeSomething equal to the return type of the functor.

The functor may be a class, a lambda or a function pointer.

Partial solutions I found so far.

(a) The answer for my linked question done by ecatmur. Essentially, it is repeating the return statement in <decl-expr>. Problems: it is error-prone and wouldn't work if contains local variables.

(b) It works only for function pointers

template <typename Arg, typename Ret>
Ret ComputeSomething(Arg arg, Ret(*fn)(Arg))

(c) It assumes that the argument of the functor is of type Arg (which may not hold in general) and requires Arg to be default-constructible

template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(Arg())

(d) Using std::declval which is supposed to lift the default-constructible restriction, as suggested in how to deduce the return type of a function in template. Could anybody explain how it works?

template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(std::declval<Arg>())
like image 333
Andrey Avatar asked Aug 20 '12 07:08

Andrey


4 Answers

Use result_of. It is backwards compatible and takes all the ugly declval pain out of your code. You still need to remember to add rvalue reference qualifiers (&&) if you actually just forward values.

Something else I find important: Your function forwards arguments to another function. In such cases you should always use rvalue references to pass the arguments.

If all you are trying to do is improve maintainability: there are several attempts at a RETURNS macro around that try to minimize the repetition between the return type declaration and the actual return expression, but I haven't seen any that allows a function body that contains more than the actual return statement.

As for how declval works: Its compiler dependent. It isn't allowed to occur in an evaluated content and its argument can be an incomplete type. See 20.2.4

like image 122
pmr Avatar answered Oct 18 '22 22:10

pmr


std::declval is a function template that is only declared (not defined). It can thus only be used in unevaluated contexts such as the argument to sizeof and decltype. It is declared to return an rvalue of the specified type. This allows you to use it to manufacture a dummy parameter for a function call in a decltype expression.

e.g.

typedef decltype(fn(std::declval<Arg>())) t;

declares t to be the type of the result of calling fn with an rvalue of type Arg. This is similar to your case (c) (fn(Arg())), but it doesn't require anything of Arg, so it works on types without default constructors.

If your return expression uses a local variable of type foo, then you can use decltype(fn(std::declval<foo>())), again regardless of how you construct a foo.

If you need an lvalue, such as a named object or an lvalue reference, then you can use std::declval<foo&>(). This allows you to handle the case where the type depends on whether you have an lvalue or an rvalue.

like image 37
Anthony Williams Avatar answered Oct 18 '22 21:10

Anthony Williams


Here's my own solution, the best I could get

template <typename Arg, typename Fn>
typename std::result_of<Fn(Arg)>::type ComputeSomething(Arg arg, Fn fn)
like image 5
Andrey Avatar answered Oct 18 '22 21:10

Andrey


To make (c) works for anything, you need 2 overloads. 1st as shown in (c), 2nd:

template <typename Arg, typename Ret>
Ret ComputeSomething(Arg arg, std::function<Ret(Arg)> fn)

Also, as gcc bug 54111 shows - deduction of return type is very unreliable.

like image 3
Leonid Volnitsky Avatar answered Oct 18 '22 22:10

Leonid Volnitsky