...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
The class template result_of
helps determine the type of a call expression. For example, given an lvalue
f
of type F
and lvalues t1
,t2
, ..., tN
of types T1
, T2
, ..., TN
,
respectively, the type result_of
<F(T1,
T2,
..., TN)>::type
defines the result type of the expression f(t1,
t2,
...,tN)
.
This implementation permits the type F
to be a function pointer, function reference, member function pointer,
or class type. By default, N may be any value between 0 and 16. To change
the upper limit, define the macro BOOST_RESULT_OF_NUM_ARGS
to the maximum value for N. Class template result_of
resides in the header
<boost/utility/result_of.hpp>
.
If your compiler's support for decltype
is adequate, result_of
automatically uses it
to deduce the type of the call expression, in which case result_of
<F(T1, T2, ..., TN)>::type
names the type decltype
(boost::declval<F>()(boost::declval<T1>(), boost::declval<T2>(), ..., boost::declval<TN>()))
, as in the following example.
struct functor {
template<class T>
T operator()(T x)
{
return x;
}
};
typedef boost::result_of
<functor(int)>::type type; // type is int
You can test whether result_of
is using decltype
by checking if the macro
BOOST_RESULT_OF_USE_DECLTYPE
is defined after including result_of.hpp
.
You can also force result_of
to use decltype
by defining BOOST_RESULT_OF_USE_DECLTYPE
prior to
including result_of.hpp
.
If decltype
is not used, then automatic
result type deduction of function objects is not possible. Instead, result_of
uses the following protocol to allow the programmer to specify a type.
When F
is a class type
with a member type result_type
,
result_of<F(T1, T2, ..., TN)>::type
is F::result_type
.
When F
does not contain
result_type
, result_of<F(T1, T2, ..., TN)>::type
is F::result<F(T1,
T2,
..., TN)>::type
when N >
0
or void
when N =
0
.
Note that it is the responsibility of the programmer to ensure that function objects accurately advertise their result type via this protocol, as in the following example.
struct functor {
template <class> struct result;
template<class F, class T>
struct result<F(T)> {
typedef T type;
};
template<class T>
T operator()(T x)
{
return x;
}
};
typedef boost::result_of
<functor(int)>::type type; // type is int
Since decltype
is a language feature standardized
in C++11, if you are writing a function object to be used with result_of
, for maximum portability,
you might consider following the above protocol even if your compiler has
proper decltype
support.
If you wish to continue to use the protocol on compilers that support
decltype
, there are two options:
boost::tr1_result_of
,
which is also defined in <boost/utility/result_of.hpp>
.
BOOST_RESULT_OF_USE_TR1
,
which causes result_of
to use the protocol described above instead of decltype
. If you choose to follow
the protocol, take care to ensure that the result_type
and result<>
members accurately represent the return type of operator()
given a call expression.
Additionally, boost::result_of
provides a third mode of operation, which some users may find convenient.
When BOOST_RESULT_OF_USE_TR1_WITH_DECLTYPE_FALLBACK
is defined, boost::result_of
behaves as follows. If the function object has a member type result_type
or member template result<>
,
then boost::result_of
will use the TR1 protocol.
Otherwise, boost::result_of
will use decltype
. Using TR1 with a decltype
fallback may workaround
certain problems at the cost of portability. For example:
boost::result_of
to work with incomplete
return types but your compiler's decltype
implementation does
not support incomplete return types, then you can use the TR1 protocol
as a workaround. Support for incomplete return types was added late
in the C++11 standardization process (see N3276)
and is not implemented by some compilers.
decltype
, then using TR1 with
a decltype
fallback will allow
you to work with both your existing TR1 function objects and new C++11
function object. This situation could occur if your legacy function
objects misused the TR1 protocol. See the documentation on known differences between boost::result_of
and TR1.
result_of
requires class template
partial specialization, the ability to parse function types properly,
and support for SFINAE. If result_of
is not supported
by your compiler, including the header <boost/utility/result_of.hpp>
will define the macro
BOOST_NO_RESULT_OF
.
For additional information about result_of
, see the C++ Library
Technical Report, N1836,
or, for motivation and design rationale, the result_of
proposal.
boost::result_of
The following are general suggestions about when and how to use boost::result_of
.
std::result_of
. If std::result_of
meets your
needs, then there's no reason to stop using it.
boost::result_of
with decltype
. When decltype
is used boost::result_of
and std::result_of
are usually
interchangeable. See the documentation on known differences
between boost::result_of
and C++11 std::result_of
.
boost::result_of
with the TR1 protocol
Regardless of how you configure boost::result_of
, it is important to
bear in mind that the return type of a function may change depending on
its arguments, and additionally, the return type of a member function may
change depending on the cv-qualification of the object. boost::result_of
must be passed the appropriately
cv-qualified types in order to deduce the corresponding return type.
For example:
struct functor { int& operator()(int); int const& operator()(int) const; float& operator()(float&); float const& operator()(float const&); }; typedefboost::result_of
< functor(int) >::type type1; // type1 is int & typedefboost::result_of
< const functor(int) >::type type2; // type2 is int const & typedefboost::result_of
< functor(float&) >::type type3; // type3 is float & typedefboost::result_of
< functor(float const&) >::type type4; // type4 is float const &
On compliant C++11 compilers, boost::result_of
can use decltype
to deduce the type of any
call expression, including calls to function objects. However, on pre-C++11
compilers or on compilers without adequate decltype support, additional
scaffolding is needed from function objects as described above. The following
are suggestions about how to use the TR1 protocol.
result_type
.
There is no need to use the result
template unless the return type varies.</li>
struct functor { typedef int result_type; result_type operator()(int); };
result
specialization near the corresponding operator()
overload. This can make it easier
to keep the specializations in sync with the overloads. For example:
struct functor { template<class> struct result; template<class F> struct result<F(int)> { typedef int& type; }; result<functor(int)>::type operator()(int); template<class F> struct result<const F(int)> { typedef int const& type; }; result<const functor(int)>::type operator()(int) const; };
result
template specialization. For example, the following uses Boost.TypeTraits
to specialize the result
template for a single operator()
that can be called on both a const
and non-const function object with either an lvalue or rvalue argument.
struct functor { template<class> struct result; template<class F, class T> struct result<F(T)> : boost::remove_cv< typename boost::remove_reference<T>::type > {}; template<class T> T operator()(T const& x) const; };
boost::result_of
and boost::tr1_result_of
When using decltype
, boost::result_of
ignores the TR1 protocol
and instead deduces the return type of function objects directly via decltype
. In most situations, users
will not notice a difference, so long as they use the protocol correctly.
The following are situations in which the type deduced by boost::result_of
is known to differ depending
on whether decltype
or the TR1 protocol is
used.
TR1 protocol misusage: When using the TR1 protocol, boost::result_of
cannot detect whether
the actual type of a call to a function object is the same as the type
specified by the protocol, which allows for the possibility of inadvertent
mismatches between the specified type and the actual type. When using
decltype
, these subtle bugs may
result in compilation errors. For example:
struct functor { typedef short result_type; int operator()(short); }; #ifdef BOOST_RESULT_OF_USE_DECLTYPE BOOST_STATIC_ASSERT(( boost::is_same<boost::result_of
<functor(short)>::type, int>::value )); #else BOOST_STATIC_ASSERT(( boost::is_same<boost::result_of
<functor(short)>::type, short>::value )); #endif
Note that the user can force boost::result_of
to use the TR1 protocol
even on platforms that support decltype
by defining BOOST_RESULT_OF_USE_TR1
.
Nullary function objects: When using the TR1 protocol, boost::result_of
cannot always deduce
the type of calls to nullary function objects, in which case the type defaults
to void. When using decltype
, boost::result_of
always gives the actual
type of the call expression. For example:
struct functor { template<class> struct result { typedef int type; }; int operator()(); }; #ifdef BOOST_RESULT_OF_USE_DECLTYPE BOOST_STATIC_ASSERT(( boost::is_same<boost::result_of
<functor()>::type, int>::value )); #else BOOST_STATIC_ASSERT(( boost::is_same<boost::result_of
<functor()>::type, void>::value )); #endif
Note that there are some workarounds for the nullary function problem.
So long as the return type does not vary, result_type
can always be used to specify the return type regardless of arity. If the
return type does vary, then the user can specialize boost::result_of
itself for nullary calls.
Non-class prvalues and cv-qualification: When using the TR1 protocol,
boost::result_of
will report the cv-qualified type specified by result_type
or the result
template
regardless of the actual cv-qualification of the call expression. When
using decltype
, boost::result_of
will report the actual
type of the call expression, which is not cv-qualified when the expression
is a non-class prvalue. For example:
struct functor { template<class> struct result; template<class F, class T> struct result<F(const T)> { typedef const T type; }; const short operator()(const short); int const & operator()(int const &); }; // Non-prvalue call expressions work the same with or without decltype. BOOST_STATIC_ASSERT(( boost::is_same<boost::result_of
<functor(int const &)>::type, int const & ::value )); // Non-class prvalue call expressions are not actually cv-qualified, // but only the decltype-based result_of reports this accurately. #ifdef BOOST_RESULT_OF_USE_DECLTYPE BOOST_STATIC_ASSERT(( boost::is_same<boost::result_of
<functor(const short)>::type, short ::value )); #else BOOST_STATIC_ASSERT(( boost::is_same<boost::result_of
<functor(const short)>::type, const short ::value )); #endif
boost::result_of
and C++11 result_of
When using decltype
, boost::result_of
implements most of the
C++11 std::result_of
specification. One
known exception is that boost::result_of
does not implement the requirements regarding pointers to member data.
namespace boost { template<typename F> struct result_of; template<typename F> struct tr1_result_of; }
Created by Doug Gregor. Contributions from Daniel Walker, Eric Niebler, Michel Morin and others.