From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2008-04-05 20:23:07


On Sat, Apr 5, 2008 at 6:21 PM, Eric Niebler <eric_at_[hidden]> 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