Difference Between Std::Result_Of and Decltype

Difference between std::result_of and decltype

result_of was introduced in Boost, and then included in TR1, and finally in C++0x. Therefore result_of has an advantage that is backward-compatible (with a suitable library).

decltype is an entirely new thing in C++0x, does not restrict only to return type of a function, and is a language feature.


Anyway, on gcc 4.5, result_of is implemented in terms of decltype:

  template<typename _Signature>
class result_of;

template<typename _Functor, typename... _ArgTypes>
struct result_of<_Functor(_ArgTypes...)>
{
typedef
decltype( std::declval<_Functor>()(std::declval<_ArgTypes>()...) )
type;
};

I'm not understanding these uses of std::result_of and decltype

result_of

First, the GCC compiler requires the keyword typename before std::result_of because the return value of the latter is a class. And you have to instruct it to use its type to declare a new variable.

Regarding your comment:

Also, because I've read that result_of is being deprecated as of C++14 or C++17

std::result_of is deprecated as of C++17 (See here why) and is replaced by
the newly introduced std::invoke_result, so you could use it instead if you have a compliant compiler.

decltype

Since std::result_of is declared in terms of decltype in the following manner:

  template<typename _Signature>
struct result_of;

template<typename _Functor, typename... _ArgTypes>
struct result_of<F(Args...)> {
typedef decltype( std::declval<F>()(std::declval<Args>()...) ) type;
};

You could use a similar definition:

decltype( std::declval<T>()() ) myVar = 5;

What's the difference between result_ofF(Args... and decltypef(args...)?

Your version doesn't work with e.g. pointers to members. A closer, but still not exact version would be:

template <class F, class... Args>
auto async(F&& f, Args&&... args)
-> future<decltype( ref(f)(forward<Args>(args)...) )>;

The only difference remaining with std::result_of is that this forwards the functor as an lvalue (a problem your version also shares). In other words, the result of such a call (via an std::reference_wrapper<F>) is typename std::result_of<F&(Args...)>::type.

This is an awkward situation where several components of the Standard library (to name a few, in addition to those we've just witnessed: std::thread, std::bind, std::function) are specified in terms of an elusive INVOKE(f, a0, a1, ..., aN) pseudo-expression, which isn't exactly equivalent to f(a0, a1, ... aN). Since std::result_of is one of those components, and serves in fact to compute the result type of INVOKE, that's the discrepancy you're noticing.

Because there is no std::invoke that comes in tandem with the std::result_of type trait I am of the opinion that the latter is only useful for describing e.g. the return types of the relevant Standard Library components, when your code calls them. If you want a concise and self-documenting way of writing e.g. a return type (a very worthy goal for readability, compared to sprinkling decltype everywhere), then I recommend you write your own alias:

template<typename F, typename... A>
using ResultOf = decltype( std::declval<F>()(std::declval<A>()...) );

(If you want the alias to be used as ResultOf<F(A...)> instead of ResultOf<F, A...> then you need a little bit of machinery to pattern match over the function signature.)

An added benefit of this alias is that it is SFINAE friendly, unlike std::result_of. Yes, that is one more of its flaws. (To be fair though this has been amended for the upcoming Standard and implementations are following suit already.)

You would not be missing anything if you were using such a trait because you can adapt pointers to members thanks to std::mem_fn.

How Can I Use result_of Instead of decltype?

Let's patch it up. std::result_of expects only types, its result should be retrieve from its type inner typedef, and you need typename to access said typedef because it depends on a template parameter.

template<typename T>
using to_string_t = typename std::result_of<decltype(std::to_string)(T)>::type;
^^^^^^^^ ^^^^^^^^ ^^^^^^

Or in C++14, you can drop ::type and typename :

template<typename T>
using to_string_t = std::result_of_t<decltype(std::to_string)(T)>;
^^

Fine ?

main.cpp:5:68: error: decltype cannot resolve address of overloaded function

Right, std::to_string is overloaded, so we need to disambiguate it by casting it to one of its overloads.

template<typename T>
using to_string_t = typename std::result_of<decltype(static_cast<

Hold on. We need its return type to express the destination type of the cast. We're back to our starting point.

std::result_of can't deal with overloaded functions, because the function has no definite type until the overload is resolved. decltype is the only solution here, because it does apply overload resolution.

If you're wondering how std::result_of can be useful, given the above limitation : it's used for overloaded functors, i.e. classes that overload the () operator several times. As the type of the class is known, and does not depend on the call arguments, std::result_of works.

... But shouldn't std::to_string always return a std::string ??

decltype, result_of, or typeof?

Since whatever result you obtain depends on the template parameter, typedef typename is necessary.

decltype is a standard C++11 feature. It is an "operator" which takes an expression and returns a type.

typedef typename decltype( T().toCPD() ) D; // can't use T:: as it's nonstatic

If T() isn't a valid (T not default-constructible) you will want declval which is a function that takes a type and returns a meaningless, invalid value of that type. declval can only be used in unevaluated contexts such as decltype.

typedef typename decltype( std::declval<T>().toCPD() ) D;

Before C++11, decltype was a non-standard extension by Microsoft's MSVC compiler. Its behavior might have been changed slightly by standardization.


typeof is GCC's equivalent pre-C++11 extension like decltype, which was also cloned in other compilers. Here is its documentation from GCC. That page provides no comparison between the features, but it notes that typeof must be called __typeof__ when using a standard mode (-std=c++YY, which you should always do), and it is available in C as well as C++.

For the sake of C compatibility, __typeof__ will not resolve a reference type from a glvalue expression. So, it's really only suitable for C. This probably explains why the C++ feature didn't inherit the more self-explanatory name: GNU was unwilling to sacrifice backward compatibility, whereas Microsoft cares less about C and perhaps needed fewer changes.


result_of is a C++11 metafunction (previously standardized in the ISO TR1 library from 2006). It is a template which takes a callable type (such as a function int(void), function pointer int(*)(void), functor class implementing operator(), or pointer-to-member-function &T::toCPD) and an argument type-list for that type, and provides the return type if the call would work.

To use result_of with a pointer to member function, you must include the parent object type in the argument list as a surrogate for this.

typedef typename std::result_of< decltype( & T::toCPD ) ( T * ) >::type D;

This is very brittle, though, because &T::toCPD cannot be resolved if there's any overloading, such as a non-const version. This is true despite the fact that T * or T const * must be explicitly written out! In most cases, you're better off with decltype and declval.

Confusion: decltype vs std::function

You cannot store functions, but you can store function pointers:

std::queue<decltype(&fun)> q1;
std::queue<int (*)(int)> q2;

Demo

  • decltype(fun) is int (int)
  • decltype(&fun) is int (*)(int)

std::packaged_task as std::function expects signature,

so both std::packaged_task<int(int)> and std::packaged_task<decltype(fib)> are ok.

std::function<int(int)> is a type, not a signature, so std::packaged_task<std::function<int(int)>> is invalid (not defined in fact), so unusable.

How to convert std::result_of to decltype in a template argument

You might use

decltype(std::declval<F>()())

instead of

typename std::result_of<F()>::type

which are similar, but have subtle differences



Related Topics



Leave a reply



Submit