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
Quick Sort at Compilation Time Using C++11 Variadic Templates
Code Browsing, Refactoring, Auto Completion in Emacs
Template Tricks with Const Char* as a Non-Type Parameter
Performance Degradation Due to Default Initialisation of Elements in Standard Containers
C++ Const Keyword - Use Liberally
Qthread Emits Finished() Signal But Isrunning() Returns True and Isfinished() Returns False
Understanding Boost::Disjoint_Sets
Qapplication in Non-Main Thread
Const Unsigned Char * to Std::String
What Are _Mm_Prefetch() Locality Hints
How to Include Correctly -Wl,-Rpath,$Origin Linker Argument in a Makefile
How to Declare Array with Auto
What Is the Modern, Correct Way to Do Type Punning in C++
How to Call a Template Member Function
Implementing the Derivative in C/C++
Stl Map Should Use Find() or [N] Identifier to Find Element in Map
Brute-Force, Single-Threaded Prime Factorization
How to Overload a Function That Can Tell a Fixed Array from a Pointer