Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Result of

Introduction
Usage guidelines for boost::result_of
Usage guidelines for the TR1 result_of protocol
Known differences between boost::result_of and boost::tr1_result_of
Known differences between boost::result_of and C++11 result_of
Reference
Acknowledgments

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:

  • You can use boost::tr1_result_of, which is also defined in <boost/utility/result_of.hpp>.
  • Alternatively, you can define the macro 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:

  • Deficient compiler: If your code requires 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.
  • Deficient legacy code: If your existing TR1 function object advertises a different type than the actual result type deduced by 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.
  • This implementation of 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.

Usage guidelines for boost::result_of

The following are general suggestions about when and how to use boost::result_of.

  1. If you are targeting C++11 and are not concerned about portability to non-compliant compilers or previous versions of the standard, then use std::result_of. If std::result_of meets your needs, then there's no reason to stop using it.
  2. If you are targeting C++11 but may port your code to legacy compilers at some time in the future, then use 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.
  3. If compiler portability is required, use 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&);
};

typedef boost::result_of<
    functor(int)
>::type type1; // type1 is int &

typedef boost::result_of<
    const functor(int)
>::type type2; // type2 is int const &

typedef boost::result_of<
    functor(float&)
>::type type3; // type3 is float &

typedef boost::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.

  • When the return type does not depend on the argument types or the cv-qualification of the function object, simply define result_type. There is no need to use the result template unless the return type varies.</li>
  • Use the protocol specified type when defining function prototypes. This can help ensure the actual return type does not get out of sync with the protocol specification. For example:
struct functor {
    typedef int result_type;
    result_type operator()(int);
};
  • Always specify the 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;
};
  • Use type transformations to simplify the 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&amp; x) const;
};

Known differences between 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

Known differences between 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.

Reference

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.


PrevUpHomeNext