std::result_of simple function
c++, c++11, result-of, std
Solution
`std::result_of<T>` requires `T` to be a type --but not just any type. `T` must be a function type so this partial specialization of `result_of` will be used:
template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;
such that:
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...))
is well-formed (C++11 20.9.7.6). (INVOKE is defined in 20.8.2.)
The reason `std::result_of<f(int)>` does not work is because `f` is not a type --it is an instance of a function type. To declare `x` to be the return type of `f` applied to an `int`, simply write this:
decltype(f(int{})) x;
or if you prefer hard-coding an `int`:
decltype(f(32)) x;
If the type of `f` is desired then use:
using FuncPtr = decltype(f);
In the provided code `F` (i.e., not lower case `f`) however is a type and so `F(int)` defines a the type representing the function that returns `F` accepting an `int` as an argument. Clearly this is not what F is! `F`'s type is a struct whose instances can use the function call operator. `F` also has no explicit or implicit constructors taking an `int`, etc. as well. How can this work? Short answer: Template "magic".
Essentially, the definition of `std::result_of` takes the type, `F(int)` and separates the return type from the argument types so it can determine which case of INVOKE() would allow it to work. The cases of INVOKE are:
- F is a pointer to a member function for some class T
- If there is only one argument, F is a pointer to a data member of class T, or,
- An instance of F can be used as a function, i.e.,
declval<F>()(declval<int>())
which can be a normal function call or some type of functor (e.g., like your example).
Once this has been determined `result_of` can then determine the return type of the valid expression. This is what is returned via `result_of`'s `type` member.
The beautiful thing about this is that the user of `result_of` need not know anything about how this actually works. The only thing one has to understand is that `result_of` needs a function TYPE. If one is using names that are not types within code (e.g., `f`) then `decltype` will need to be used to obtain the type of an expression with such.
Finally, part of the reason why `f` cannot be considered as a type is because template parameters also allow constant values and `f` is a constant function pointer value. This is easily demonstrated (using the question's definition of `f`):
template <double Op(int)>
double invoke_op(int i)
{
return Op(i);
}
and later:
std::cout << invoke_op<f>(10) << std::endl;
So to obtain the return value type of an expression properly invoking `f` with some `int` one would write:
decltype(f(int{}))
(Note: `f` is never called: the compiler simply uses the expression within `decltype` to determine its result i.e., its return value in this instance.)
Problem
``` #include <iostream> #include <type_traits> double f(int i) { return i+0.1; } struct F { public: double operator ()(int i) { return i+0.1; } }; int main(int, char**) { std::result_of<F(int)>::type x; // ok // std::result_of<f(int)>::type x; // error: template argument 1 is invalid x = 0.1; std::cerr << x << std::endl; } ``` Please explain why `std::result_of<f(int)>::type x;` is invalid... cppreference says "(`std::result_of`) Deduces the return type of a function call expression at compile type.". what's the problem?