Boost logo

Boost :

Subject: Re: [boost] [range] [general] making member functions SFINAE-friendly
From: Jonathan Wakely (jwakely.boost_at_[hidden])
Date: 2013-02-18 14:06:48


On 18 February 2013 18:30, Andrey Semashev wrote:
> On Monday 18 February 2013 15:05:11 Jonathan Wakely wrote:
>> On 18 February 2013 14:33, Andrey Semashev wrote:
>> >
>> > Yes, I'm aware of type traits. It's one thing to do tests/transforms
>> > on types and another to test for methods presence and behavior. It's
>> > doable but it is much more fragile and dangerous, as you have already
>> > discovered with iterator_range.
>>
>> It's only fragile because iterator_range defines a member which can't be
>> used.
>
> No, it broke with iterator_range. It can break with other types with different
> signatures and/or semantics of size().

Given that I'm already requiring the type can be used with
std::begin() and std::end(), i.e. is range-like, I'm happy to not
support types that have a non-range-like size(). If your type quacks
like a duck but swims like a fish it doesn't meet my requirements for
a duck.

>> >> Apparently you've missed that std::list::size() is required to be O(1) in
>> >> C++11.>
>> > Hmm, you're right, I've missed it.
>>
>> Please think about what it takes for the committee to make such a
>> breaking change to C++03, and whether that says the "don't define
>> size() if it can't be done in constant time" principle is considered
>> important or not.
>
> Not sure what you mean.

You're arguing that size() should be provided even if it's O(n). I'm
pointing out the committee disagreed so strongly they changed the
standard even though that broke existing C++ implementations.

>> > vector::size() doesn't provide any benefits compared to std::distance.
>>
>> The fact it exists tells you it is constant time.
>
> How so? This is true for vector as it is required by the standard but is it
> true for other types? And why it should be true for iterator_range in
> particular?
>
>> If I wrote a container that could not implement empty() in O(1) then I
>> would not define empty().
>
> Great. So we only implement operations if they are possible to be implemented
> in O(1) now?

You asked about empty, which as you pointed out can be tested in other
ways. I wasn't talking about *all* operations, don't put words in my
mouth then make silly assertions. And I said if I wrote it. You can
do what you like with your own code.

>> >> boost::iterator_range has size() but it results in a compile-time
>> >> error. This is the worst of all combinations. It would be better to
>> >> not define it at all, since users can always use std::distance on its
>> >> iterators, which will be optimal for RA iterators anyway.
>> >
>> > Again, it's a matter of convenience. Compare:
>> > r.size();
>> >
>> > and
>> >
>> > std::distance(r.begin(), r.end());
>> >
>> > Of course, the convenience is ruined in generic code if you have to
>> > dispatch between the two variants depending on the iterator type. My
>> > point is to always use the first one and be happy.
>>
>> Except for std::forward_list. So much for that rule.
>
> Too bad for those trying to write a generic optimized size() implementation,
> yes. But I was referring to iterator_range, specifically. It will support
> size() in all cases.

Great, I look forward to that change in future versions of Boost.


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