
On Sat, Sep 8, 2012 at 6:59 AM, Andrey Semashev <andrey.semashev@gmail.com>wrote:
On Tuesday 04 September 2012 11:07:39 you wrote:
Jeffrey have suggested a good solution in his reply. I think it can be described in the result_of docs to resolve the confusion in the future. I can update the docs if noone objects.
I've added a clarification note to the result_of docs in trunk. Here is the added section:
<quote> The result template must be specialized for every valid calling signature of the function object. If the operator() accepts arguments by (possibly const) reference and/or is const qualified,
My first pass over this read the "const qualified" as applying to the arguments to operator(), but it actually applied to operator() itself. That indicates that it may be good to reword to clarify.
the result specialization must take this into account. Type traits and more generic specializations may help to reduce the number of result specializations.
On a second reading through, I think something like the following may be a bit clearer: -------- The result template should be specialized to account for all possible valid call signatures of the function object, including any const-qualification of the function object itself as well as reference- or reference-to-const-qualifications of the argument types. One may infer the set of valid call signatures based on what combinations of (possibly reference- or reference-to-const-qualified) arguments are accepted by the set of operator() overloads. Type trait metafunctions (such as Boost.TypeTraits' remove_const [linked(?)] and remove_reference [linked(?)]) and appropriate specializations can often reduce the number of result template specializations to a minimum while ensuring that all valid call signatures are covered. -------- Hmmm...actually, the more I'm reading your original paragraph, the more it's really saying the same thing as above, just with more words, so I don't know what was unclear to me before. Take from what I wrote above what you think might be appropriate (if anything) without complicating things too much. This way result_of users will be able to
specify argument types exactly according to the function object call expression. For example:
struct functor { template<class> struct result;
// Use template parameter F to match the function object. This will allow result deduction for both const and non-const functor. template<class F, class T> struct result<F(T)> { // When argument type is matched like above, remember that the type may be a (const-qualified) reference. // Use type traits to transform the argument type. typedef typename remove_cv<typename remove_reference<T>::type>::type argument_type; typedef argument_type type; };
// The operator can be called on both const and non-const functor. The argument can be lvalue or rvalue. template<class T> T operator()(T const& x) const { return x; } };
// All following result_of uses are valid and result in int typedef boost::result_of< functor(int) >::type type1; // the argument is rvalue functor f; type1 r1 = f(10);
typedef boost::result_of< const functor(int) >::type type2; // the function object is const const functor cf; type2 r2 = cf(10);
typedef boost::result_of< functor(int&) >::type type3; // the argument is lvalue int a = 10; type3 r3 = f(a);
typedef boost::result_of< functor(int const&) >::type type4; // the argument is const lvalue const int ca = 10; type4 r4 = f(ca); </quote>
You can find the complete page tere:
http://svn.boost.org/svn/boost/trunk/libs/utility/utility.htm
If noone objects I will merge it to release branch in a few days
- Jeff