|
Boost : |
From: Peter Dimov (pdimov_at_[hidden])
Date: 2001-10-06 07:32:05
From: "Jeremy Siek" <jsiek_at_[hidden]>
> The projection_iterator assumes that the result_type is not a reference.
... and that the return type is a reference. The two assumptions, taken
together, are the problem.
> Hmm, now to decide whether this was a good assumption or not...
> I'm looking at the definition of
>
> http://www.sgi.com/tech/stl/AdaptableUnaryFunction.html
> and some models of it like
> http://www.sgi.com/tech/stl/identity.html
>
> template <class T>
> struct identity {
> typedef T result_type;
> const T& operator()(const T& x) const { return x; }
> };
>
> the way identity is defined suggests that result_type is expected
> to not be a reference...
Function objects that define a result_type as a value but return a const
reference will (mostly) work fine, as if they've been defined to really
return by value. This is a minor speed optimization. Of course the non-const
case is different, and I think that it's not modeled at all by
<functional> - the design assumes pure functions AFAICS.
But no code can assume that a function object really returns a reference -
unless it says so with its result_type (there is no other mechanism
available.)
> Also, looking at plus, minus, etc. same thing.
plus/minus usually don't (and cannot) return a reference.
> Then looking at ptr_fun... I wonder if the designer anticipated
> it being used with function that return a reference type. The example
> there, fabs returns by value. I think the problem is really with
> the use of ptr_fun with functions returning references, not with
> projection_iterator.
I have a function that I want to use with projection_iterator. It has to
return a reference - this is a projection_iterator requirement.
projection_iterator wants an AdaptableUnaryFunction, and the standard way to
get one is via std::ptr_fun.
Or, to put it another way: you have a function object F. You need to write
another function object G such that G(x) is equivalent to F(x):
struct G
{
typedef F::result_type result_type;
typedef F::first_argument_type first_argument_type;
result_type operator()(first_argument_type x) { return F(x); }
};
Now consider what happens when F returns by non-const reference but defines
result_type as a value. G(x) is no longer equivalent to F(x).
Looking at the standard library, ptr_fun and mem_fun expose the real return
type, and bind1st and bind2nd retain it. boost::bind, boost::mem_fn,
boost::compose*, sgi::compose* follow this model.
sgi::select1st defines result_type to be a value and returns by const
reference; but note that there is no non-const variant.
Bottom line, make_projection_iterator should handle value_type (optional and
dangerous - for compatibility only; the function object may indeed return by
value), value_type &, and value_type const & as possible result_types, and
produce a const/non-const iterator accordingly.
Another bottom line, the whole Adaptable* concept is flawed and I wouldn't
base a future design on it, but that's another story. :-)
-- Peter Dimov Multi Media Ltd.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk