
Peter Dimov wrote:
Eric Niebler:
So where have I screwed up my logic?
Nowhere. But I don't think that this problem is somehow caused by result_of. If you replace every result_of occurence with <magic happens here> the result would be the same.
Can you give an example in which compose<identity,make_tuple>, implemented within your new framework, is able to avoid the problem?
OK, scratch everything and lets get back to basics. I'm finding I just don't know how to write a TR1 function object in C++03. I need help. == Background == My case study is proto's transforms. There are function objects usable within transforms for (a) creating new expressions (rvalues) and (b) accessing the parts of nodes (which may be rvalues or lvalues). So for instance there is make_plus, which creates a new plus node and returns an rvalue. And there is _ which returns the the current expression (rvalue or lvalue). These are function object types. Proto defines a DSEL that lets users compose function object types using "round" lambdas: function types. So Proto treats this make_plus(_,_) as a composite function object type. There is a piece of code in Proto that interprets "make_plus(_,_)" as a function object. Ostensibly, if the current expression were an object "e" of type "E const &", then Proto would compute the return type as: result_of<make_plus( result_of<_(E const &)>::type ,result_of<_(E const &)>::type )>::type And it would invoke invoke it as if it were: return make_plus()( _()(e), _()(e) ); So now how are _ and make_plus defined? _ is simple -- it is essentially identity which I showed earlier. make_plus is trickier. It must be optimal and safe. When passed lvalues, it should return a new node that holds the children by reference. When passed rvalues, it should return a new node that hold the children by value. == Problem == Here's the thing ... I have no idea how to implement make_plus as a TR1 function object. Obviously, it's operator() can't know in general whether it is being passed rvalues or lvalues. I can play games with reference_wrappers but that's really just an arbitrary rule, and not one in everybody's rule book, AFAIK. Proto's transform system is open -- user's can extend the system by writing their own function objects -- or simply by using existing function objects as-is. I don't want to force them to accept the reference_wrapper convention. In contrast, the following would Just Work: struct make_plus { template<typename Sig> struct impl; template<typename This, typename Left, typename Right> struct impl< This(Left, Right) > { // Left and Right are allowed to be reference types typedef plus<Left, Right>::type result_type; result_type operator()( typename add_const_ref<Left>::type left ,typename add_const_ref<Right>::type right ) const { return result_type(left, right); } }; }; struct _ { template<typename Sig> struct impl; template<typename This, typename Arg> struct impl< This(Arg) > { typedef Arg result_type; result_type operator()( typename add_const_ref<Arg>::type arg ) const { return arg; } }; }; Now, when Proto sees a binary composite transform like make_plus(_,_) it computes the return type as: make_plus::impl< _::impl<Expr const &>::result_type , _::impl<Expr const &>::result_type
::result_type
And to invoke it, it essentially does this: make_plus::impl< _::impl<Expr const &>::result_type , _::impl<Expr const &>::result_type
()( _::impl<Expr const &>()(e) , _::impl<Expr const &>()(e) )
This is very ugly, but it's hidden deep inside Proto so nobody has to see it. The benefit is that rvalue/lvalue-ness of arguments is not lost, and it is expressed in a very natural way -- with the presence or absence of a reference qualifier. I still can't see how to make this work with TR1-style function objects without forcing everybody in the world to write function objects that accept reference_wrapper to mean "lvalue". -- Eric Niebler Boost Consulting www.boost-consulting.com