
On Sat, Apr 5, 2008 at 6:21 PM, Eric Niebler <eric@boost-consulting.com> wrote:
Peter Dimov wrote:
struct make_tuple { template<class X> tuple<X> operator()( X const & x ) const { return tuple<X>( x ); }
template<class X> tuple<X&> operator()( reference_wrapper<X> const & x ) const { return tuple<X&>( x.get() ); } };
you'll see that it's relatively easy to write a result_of specialization for it. The references are an annoyance, sure. But the idea is that you start with the function object and then derive its result_of, not vice versa.
Maybe I'm missing something else.
Sure, in isolation this works, but let's look at how this composes. Here is a template that takes two unary TR1 function object types and composes them.
template<class F1, class F2> struct compose { template<typename Sig> struct result;
template<typename This, typename Arg> struct result< This( Arg ) > { typedef typename result_of< F2( typename result_of< F1(Arg) >::type ) >::type type; };
template<typename Arg> typename result_of< F2( typename result_of< F1(Arg const &) >::type )
::type operator()( Arg const & arg ) { return F2()(F1()(arg)); }
// And a non-const overload, too. };
I think that's right so far. Now imagine a simple identity function object, that just returns its argument unmodified:
struct identity { template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; };
template<typename Arg> Arg const &operator()(Arg const &arg) { return arg; }
// and a non-const overload, too };
Now take a look at what happens when I do this:
BigObject const obj; compose< identity, make_tuple >()( obj );
I've seen something like this before. Isn't the issue here that you want the functor to store a const reference to its argument in the tuple, but the functor implements call-by-reference using reference_wrapper? Then isn't that a job for cref? compose< identity, make_tuple >()( cref(obj) ); I don't know if this helps, because I don't exactly see what this has to do with lvalues/rvalues, but here's a make_tuple that respect the constness of the reference_wrapper. It plays nicely with result_of and apparently works with your identity and compose functors. (tested on gcc 3.4; the obj isn't copied) struct make_tuple { template<typename Sig> struct result; template<typename This, typename Arg> struct result<This(Arg const&)> { typedef tuple<Arg> type; }; template<typename This, typename Arg> struct result<This(reference_wrapper<Arg> const&)> { typedef tuple< typename unwrap_reference<Arg>::type & > type; }; template<class X> typename result< make_tuple(X const&) >::type operator()(X const& x) const { return tuple<X>(x); } template<class X> typename result< make_tuple(reference_wrapper<X> const&) >::type operator()(reference_wrapper<X> const& x) const { return tuple< typename unwrap_reference<X>::type & >(x.get()); } }; Is this anything like what you're looking for? Note that there are specializations for result<> corresponding to exactly the way the operator()s are overloaded, and the overloads use result<> to specify they're return types. In a situation like this, I find it easier to try to keep a specific one-to-one correspondence between result<> specializations and overloads. It's a bit of a manually convention, but as you both point out, without decltype return type deduction can be tedious by nature. Daniel Walker