Decltype, Result_Of, or Typeof

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.

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;
};

Difference between decltype and typeof?

There is no typeof operator in c++. While it is true that such a functionality has been offered by most compilers for quite some time, it has always been a compiler specific language extension. Therefore comparing the behaviour of the two in general doesn't make sense, since the behaviour of typeof (if it even exists) is extremely platform dependent.

Since we now have a standard way of getting the type of a variable/expression, there is really no reason to rely on non portable extensions, so I would say it's pretty much obsolete.

Another thing to consider is that if the behaviour is of typeof isn't compatible with decltype for a given compiler it is possible that the typeof extension won't get much development to encompass new language features in the future (meaning it might simply not work with e.g. lambdas). I don't know whether or not that is currently the case, but it is a distinct possibility.

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 ??

On the various ways of getting the result type of a function

result_of yields the return type of std::invoke when applied to the given types - it's even used in invoke's declared return type. However, invoke covers far more scenarios than just ordinary non-member function calls. The expression in decltype(…) you showed is just one of many that result_of examines.

Thus your options are only equivalent if F is guaranteed to be a function object type. Otherwise result_of<…> may be valid in cases in which decltype(…) is not:

struct A {int i;};
decltype(std::declval<int A::*>()(std::declval<A>())) // error
std::result_of_t<int A::*(A)> // = int&&, treated as a member access

Demo. (Note that result_of is SFINAE friendly as of C++14, i.e. invalid types cause a smooth deduction failure as for decltype.)

What's the difference between result_of<F(Args...> and decltype<f(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.

Why isn't std::result_of<int(int)>::type valid?

As stated in the link you have already posted, the first part of the argument to result_of must be a callable type or a reference to a function.

Suppose you have a

struct Callable
{
int operator()(double);
void operator()(int);
};

then result_of helps you determining the return type, if you know the type of the arguments. For the example above:

result_of<Callable(int)>::type == void    ... per definition
result_of<Callable(double)>::type == int ... per definition
result_of<Callable(char)>::type == void ... the int-overload matches better
result_of<Callable(float)>::type == int ... the double-overload matches better

In order to find the return type of the function foo you would have to go via a function reference:

result_of<decltype(foo)& ()>::type == int

But this seems a bit twisted as you could directly write

decltype(foo()) == int

using std::result_of to determine the return type of a template argument

The function you have is int f(int i) but you are calling F() which is unknown. std::result_of<F()>::type should be std::result_of<F(T)>::type.

Live Example



Related Topics



Leave a reply



Submit