
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.