
My experience developing Proto has taught me a hard lesson about the ResultOf protocol that I thought I would share. Maybe someone here can tell me if/where I've gone wrong. There's a part of Proto (proto::call<>) that takes function objects, passes arguments to them and returns the result. I use result_of to compute the return type. Fine. Proto defines a function object, make_foo, whose behavior depends on the lvalue/rvalue-ness of its arguments. So for instance: // Return type of make_foo when passed a rvalue // int and an lvalue float: result_of< make_foo( int, float & ) >::type Like a good citizen, I've written make_foo's function call operator to recognize reference_wrapped and non-const-ref arguments as representing lvalues and all others as rvalues. (This is because C++03 doesn't have rvalue references.) Fine. Now, given some function type F (which may or may not be make_foo) and some arguments a0...an (which may or may not be lvalues), how do I invoke F? The strictly correct thing to do would be to wrap lvalues in reference_wrappers before passing them to F. If I don't, make_foo will do the wrong thing. However, Proto needs to work with function objects in the wild, very few of which do the right thing with reference_wrapped arguments, in my experience. What to do? I don't know. This business of needing to reference_wrap objects to get correct lvalue/rvalue semantics is a deal-breaker for Proto transforms. If I needed to wrap each lvalue argument before forwarding it to the next transform, and unpack each argument within each tranform, it would: a) Make writing transforms extremely tedious. b) Kill compile times with lots of unnecessary mpl::if_, is_reference, reference_wrapper and unwrap_reference instantiations. For these reasons, I've decided that Proto's transforms will use a different function object protocol. None of that will be visible from the outside. Transforms will still look like TR1-style function objects. But transforms will provide a separate interface for internal consumption that doesn't have this rvalue/lvalue problem. The problem, as I see it, stems from the fact that the return type calculation is separated from the function invocation. struct blarg { template<typename This, typename Arg> struct result< This( Arg ) > { // OK, if Arg is a reference, then it's an lvalue! }; template<typename Arg> typename result< make_foo( ??? ) >::type operator()( Arg const & arg ) { // whoops, I don't know here if arg is an lvalue or rvalue } }; If function objects were instead written like this ... struct blarg { template<typename Arg> struct impl { // OK, if Arg is a reference, it's an lvalue typedef ... result_type; result_type operator()(typename add_const_ref<Arg>::type arg) { // The right thing can happen here. } }; }; ... the problem goes away. -- Eric Niebler Boost Consulting www.boost-consulting.com