Boost logo

Boost :

Subject: Re: [boost] [result_of] decltype and function objects
From: Daniel Walker (daniel.j.walker_at_[hidden])
Date: 2008-11-13 23:16:55


On Thu, Nov 13, 2008 at 7:43 PM, Eric Niebler <eric_at_[hidden]> wrote:
>
> I've noticed test failures when compiling some of boost in C++0x mode due to
> the change to boost::result_of to use decltype. The problem comes from
> function objects like this:
>
> template<class T>
> struct identity
> {
> typedef T result_type;
>
> T const & // <== problem here
> operator()(T const &t) const
> {
> return t;
> }
> };
>
> In this (overly simplistic) example, we just want to return what is passed
> in. The computed type (result_type) is different than the return type of
> operator() ...

Not actually, no. For awhile now, the draft C++0x standard has
specified the computed or deduced std::result_of<F()>::type to be the
result type of a call to the function object F. A simple way to
implement C++0x std::result_of is using decltype. This is a great
improvement over the old intrusive protocol requiring function objects
to advertise (or falsely advertise ;) their return types. On platforms
that support decltype, boost::result_of behaves like the proposed
draft standard std::result_of. Actually, it may be the only/first
implementation...

> and for a good (IMO) reason: to avoid an unnecessary copy in
> some cases. The idea is to accommodate usage like this:
>
> result_of<identity(BigObj)>::type i = identity()(BigObj());

For completeness, the above line won't compile unless you include
BigObj as a template parameter to identity. So the line should be

result_of<identity<BigObj>(BigObj)>::type i = identity<BigObj>()(BigObj());

However, that's not actually what you want. You're not interested in
the return type of identity, you're interested in the type of the
template argument. With C++0x std::identity, for example, the template
argument type is std::identity::type. So you could write:

std::identity<BigObj>::type i = std::identity<BigObj>()(BigObj());

Or obviously, in this particular situation, you could just write:

BigObj i = identity<BigObj>()(BigObj());

> It is really very important that decltype(i) be BigObj, and not BogObj const
> &, which would cause a dangling reference. But with the change to use
> decltype in boost::result_of, the dangling reference is exactly what I would
> get.
>
> Am I now forced to change identity::operator() return by value here?

No. In general, if you want to remove references from the result type
of any arbitrary callable object F, you can write:

remove_reference<
    result_of<F()>::type
>::type i = F()();

This guarantees that i is not a dangling reference without requiring
changes to F.

Daniel Walker


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