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)
isint (int)
decltype(&fun)
isint (*)(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
What Is a Reference-To-Pointer
C++ Sizeof(Array) Return Twice the Array's Declared Length
Why Using the Const Keyword Before and After Method or Function Name
App Does Not Run with VS 2008 Sp1 Dlls, Previous Version Works with Rtm Versions
What Is the Default Hash Function Used in C++ Std::Unordered_Map
Getting the MAChine Serial Number and CPU Id Using C/C++ in Linux
"Launch Failed. Binary Not Found." Snow Leopard and Eclipse C/C++ Ide Issue
Erasing Using Backspace Control Character
Stl Rope - When and Where to Use
Why Is #Define Bad and What Is the Proper Substitute
Heap Corruption While Using Createwindowexw
Why Do We Require Requires Requires
Recursively Iterate Over All the Files in a Directory and Its Subdirectories in Qt
Should I Delete the Move Constructor and the Move Assignment of a Smart Pointer