Boost logo

Boost :

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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk