diff --git a/include/boost/utility/detail/result_of_iterate.hpp b/include/boost/utility/detail/result_of_iterate.hpp index 5cf30b6..4d35fc0 100644 --- a/include/boost/utility/detail/result_of_iterate.hpp +++ b/include/boost/utility/detail/result_of_iterate.hpp @@ -58,17 +58,79 @@ struct result_of namespace detail { +#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1700)) + template -struct cpp0x_result_of_impl +class is_callable { + typedef char (&pass)[1]; + typedef char (&fail)[2]; + + template + struct sub {}; + template + struct stub {}; + + template + static pass test(sub + , stub< + decltype( + boost::declval()( + BOOST_PP_ENUM_BINARY_PARAMS(BOOST_PP_ITERATION(), boost::declval() BOOST_PP_INTERCEPT) + ) + ) + >* x = 0); + static fail test(...); + +public: + const static bool value = sizeof(pass) == sizeof(test(sub())); + typedef typename boost::mpl::bool_::type type; +}; + +template +struct cpp0x_result_of_impl + : lazy_enable_if< + is_callable + , cpp0x_result_of_impl + > +{}; + +template +struct cpp0x_result_of_impl { typedef decltype( boost::declval()( - BOOST_PP_ENUM_BINARY_PARAMS(BOOST_PP_ITERATION(), declval() BOOST_PP_INTERCEPT) + BOOST_PP_ENUM_BINARY_PARAMS(BOOST_PP_ITERATION(), boost::declval() BOOST_PP_INTERCEPT) ) ) type; }; +#else // BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1700)) + +template +struct cpp0x_result_of_impl()( + BOOST_PP_ENUM_BINARY_PARAMS(BOOST_PP_ITERATION(), boost::declval() BOOST_PP_INTERCEPT) + ) + )>::type> { + typedef decltype( + boost::declval()( + BOOST_PP_ENUM_BINARY_PARAMS(BOOST_PP_ITERATION(), boost::declval() BOOST_PP_INTERCEPT) + ) + ) type; +}; + +#endif // BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1700)) + } // namespace detail #else // defined(BOOST_RESULT_OF_USE_DECLTYPE) diff --git a/include/boost/utility/result_of.hpp b/include/boost/utility/result_of.hpp index 93b7264..222c9c9 100644 --- a/include/boost/utility/result_of.hpp +++ b/include/boost/utility/result_of.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #ifndef BOOST_RESULT_OF_NUM_ARGS # define BOOST_RESULT_OF_NUM_ARGS 16 @@ -59,7 +60,22 @@ namespace detail { BOOST_MPL_HAS_XXX_TRAIT_DEF(result_type) template struct tr1_result_of_impl; -template struct cpp0x_result_of_impl; + +#if BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1700)) + +template class is_callable; +template struct cpp0x_result_of_impl; + +#else // BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1700)) + +template +struct result_of_always_void +{ + typedef void type; +}; +template struct cpp0x_result_of_impl {}; + +#endif // BOOST_WORKAROUND(BOOST_MSVC, BOOST_TESTED_AT(1700)) template struct result_of_void_impl diff --git a/test/result_of_test.cpp b/test/result_of_test.cpp index 3f6da9e..0c2834f 100644 --- a/test/result_of_test.cpp +++ b/test/result_of_test.cpp @@ -129,6 +129,27 @@ struct no_result_type_or_result_template #endif }; +// sfinae_tests are derived from example code from Joel de Guzman, +// which demonstrated the interaction between result_of and SFINAE. +template +typename boost::result_of::type +sfinae_test(F f, Arg const& arg) +{ + return f(arg); +} + +template +typename boost::result_of::type +sfinae_test(F f, Arg& arg) +{ + return f(arg); +} + +int sfinae_test_f(int& i) +{ + return i; +} + struct X {}; int main() @@ -268,5 +289,10 @@ int main() #endif #endif +#if defined(BOOST_RESULT_OF_USE_DECLTYPE) + int i = 123; + sfinae_test(sfinae_test_f, i); +#endif // defined(BOOST_RESULT_OF_USE_DECLTYPE) + return 0; } diff --git a/utility.htm b/utility.htm index 73bd692..c46c515 100644 --- a/utility.htm +++ b/utility.htm @@ -143,7 +143,7 @@ void f() {

Class template result_of

The class template result_of helps determine the type of a - call expression. Given an lvalue f of + call expression. For example, given an lvalue f of type F and lvalues t1, t2, ..., tN of types T1, T2, ..., @@ -163,12 +163,16 @@ void f() {

If your compiler's support for decltype is adequate, result_of automatically uses it to - deduce the result type of your callable object.

+ 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.

-
#include <boost/utility/result_of.hpp>
-
-struct functor {
+                
struct functor {
     template<class T>
     T operator()(T x)
     {
@@ -249,7 +253,7 @@ typedef boost::result_of<
                 result_type and
                 result<> members accurately
                 represent the return type of
-                operator().

+ operator() given a call expression.

This implementation of result_of @@ -266,6 +270,321 @@ typedef boost::result_of< 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. + +
  3. 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 result_of.
  4. + +
  5. If compiler portability is required, + use boost::result_of with the TR1 protocol.
  6. +
+ +

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 &
+
+ + +

Usage guidelines for the TR1 result_of protocol

+
+ +

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.
  • + +
  • 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& x) const;
    +};
    +
  • +
+ + +

Known differences between boost::result_of and 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 result_of + specification. One known exception is that + boost::result_of does not implement the + requirements regarding pointers to member data.

+

Created by Doug Gregor. Contributions from Daniel Walker, Eric Niebler, Michel Morin and others

Class templates for the Base-from-Member Idiom