Boost logo

Boost :

From: Thorsten Ottosen (tottosen_at_[hidden])
Date: 2006-02-03 06:09:48


David Abrahams wrote:
> Thorsten Ottosen <tottosen_at_[hidden]> writes:
>
>
>>David Abrahams wrote:
>>
>>>Thorsten Ottosen <tottosen_at_[hidden]> writes:

> In that case, this should *not* be integrated with the algorithms, but
> should instead be provided (if it's needed) as a separate layer that
> can adjust the endpoints of a range:
>
> drop_front(r) -> new view of r with the begin iterator incremented
> grow_front(r) -> new view of r with the begin iterator decremented
> drop_back(r) -> new view of r with the end iterator decremented
> grow_back(r) -> new view of r with the end iterator incremented
>
> Okay, I see from your points below that the "grow" variants can be
> dangerous.

Well, all versions can be dangerous: just remember that r can be empty.

More importantly, to make them safe, you need to supply the original
range as a second input. This implies that you need to store a temporary
--- this what we are actually trying to avoid.

At least for me this *proves* that we can't extract the adjustsments of
the return value from the algorithm itself. I agree it would have been
nice if we could.

>>>>The advantage of the latter approach is
>>>>
>>>>- flexibility/power
>>>>- safety (we can tjeck that next(found) and prior(found) stays in range)
>>>>
>>>>The disadvantage is that it will require twice as many overloads in
>>>>C++03 (since we can't put defaults template arguments in function
>>>>templates).
>>>
>>>
>>>And it's complex.
>>
>>implementation wise or interface wise?
>
>
> Yes. And documentation-wise. And comprehension-wise.

well, implementation wise it consist of 9 partial specializations
and twice as many function overloads (remark: in C++0x 4 functions
reduce to one function)

documentation-wise you don't have to explain the mechanism for every
function, just
show an interface that supports it.

comprehension-wise, well, all new things take time.

>>>I'm wary of introducing any such complexity without a strong catalog
>>>of use cases.
>>
>>basically it makes splitting of ranges way easier and safer.
>>
>>assume I want to find the first occurence of 5 and then copy the
>>range [begin,found] (that is, [begin,next(found)) ) somewhere:
>>
>> std::vector<int> v = ...;
>> std::vector<int>::iterator i = find( v, 5 );
>> if( i != v.end() )
>> {
>> ++i;
>> copy( v.begin(), i, out );
>> }
>>
>>vs
>>
>> copy( find<return_begin_next>( v, 5 ), out ); (*)
>
>
> copy( drop_front( find( v, 5 ) ), out );
>
> it's even shorter.

and it will lead to undefined behavior of the range was empty.

>>If found == v.end(), the returned ranges are empty and so you completely
>>avoid forgetting about checking for the end iterator.
>>
>>Having only (2) we can still, however, do a little better than above:
>>
>> copy( find( c, 5 ).advance_end(1), out )
>>
>>but now we can't be sure we don't iterator past the end.
>
>
> Okay, that's a different case. Why would you ever want to advance the
> *end* of a find? It's really beside the point, though.
>
> Feh.

right, stupid example.

> If this is an important case (still not convinced),

generally it is hard to say what is important to all people. I do think,
however, that splitting a range into different sub-ranges is a fairly
common activity. Some ranges are more common:

[begin,found)
[found,end)

for example, take unique:

erase( rng, unique(rng) ); // unique should return[found,end)
copy( unique<return_begin_found>(rng), out );

but I'm sure that sometimes you want to include the found value in the

> I would consider
> generalizing find with this overload:
>
> find( -1, v, 5 )
>
> That is, "find the element before the one that's equal to 5". And I
> would *certainly* make it work for forward ranges with somewhat
> reduced efficiency.

I'm not that keen on providing to many new algoriths.

The return-value mechanism is a more like a layer on top of the
algorithm, not a new algorithm.

> That's consistent with the spirit of generic
> programming.

well, as soon as we have to implement different algorithms for
different iterator categories, some of the benefits and beauty
goes away IMO.

> I don't think it's necessary to have the policy be a
> compile-time thing. But if it were, I'd still pass it by by value.

it is necessary if you want the algorithm to be able to return
either an iterator or a range. if only ranges are supported, using
a parameter seems like the thing to do.

the iterator version may of course be emjulated easily by calling
foo<return_begin_found>(rng).end() etc.

> Anyway, how do you check for success? What if 5 is the first element?
> Do you get an empty range or do you just get v back?

you get an empty range back if nothing is found:

if( find( v, 6 ) )
{ ... }

>>Thus we would
>>be forced to create a temporary.

yes, but only if you're not passing the range directly to another algorithm.

-Thorsten


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