Boost logo

Boost :

From: Eric Niebler (eric_at_[hidden])
Date: 2008-04-07 11:37:54

Peter Dimov wrote:
>> shunsuke wrote:
>>> Eric Niebler wrote:
>>>> 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
>>>> };
>>> Let me clarify.
>>> This identity implementation is simply wrong (if you pass an rvalue to
>>> identity.)
>>> because of inconsistency between decltype and result_of.
> Yes, it's wrong, result<> and operator() are out of sync.
> result_of<identity(X&)>::type returns X&, whereas identity()(x), where x is
> an lvalue of type X, returns X const&.
> Arg& operator()( Arg& ) const;
> Arg operator()( Arg const& ) const;
> maybe? I still don't remember how these overloads are resolved for a const
> lvalue. Either way, this seems the best we can do in C++03.

I don't (yet) buy that it's wrong. Can you write what the identity
function object would look like in C++0x? And is there a plan in C++0x
to require std::result_of to be implemented in terms of decltype?

First, I don't think there is anything wrong with a function that takes
a T const& and returns the same T const&. Even if it is passed an
rvalue, the rvalue will live until the end of the full expression
containing the function call, so the object is still alive. (Expression
template libraries rely on the lifetime of temporaries, which is why I'm
stuck on this point.)

Second, the result<> and operator() may be out of sync, but not in any
meaningful way (that I can see). And it seems that doing it the way I
showed allows for forward compatibility with rvalue references. Consider:

int i = 0;
int const j = 0;

// rvalue
result_of<identity(int)>::type k = identity()(1);
assert( 1 == k );

// lvalue
result_of<identity(int &)>::type l = identity()(i);
assert( &l == &i );

// const lvalue
result_of<identity(int const &)>::type m = identity()(j);
assert( &m == &j );

IIUC, this is how we would want things to work in C++0x, am I right? If
so, we can have this today with the identity I showed. The one Peter is
suggesting will fail the final assert.

Eric Niebler
Boost Consulting

Boost list run by bdawes at, gregod at, cpdaniel at, john at