
On Mon, Feb 18, 2013 at 5:17 PM, Jonathan Wakely <jwakely.boost@kayari.org> wrote:
I challenge you to implement something like std::allocator_traits without "introspecting" the template arguments. What I want to do is a fairly normal generic programming technique, maybe you've heard of the <type_traits> header, what do you think it's for?
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.
Well, the post doesn't give any rationale behind the choice, just that it was decided that way. Personally, I don't think that O(N) size() is invalid, however slow it may be. You do have list::size(), after all.
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.
I agree that it may seem strange that iterator_range::size() is present when it's not working but it is no less stranger that it doesn't work when it could. IMHO, the right solution in this case would be to fix iterator_range::size() to work in terms of std::distance.
Do you also suggest that std::vector::push_front() should be defined, because it can easily be implemented in terms of v.insert(v.begin(), x) ?
I don't.
There is a general design principle in the STL that members functions are only defined on containers if they are more efficient than doing the same thing using the more general interfaces. So vector has push_back() but not push_front(), deque and list have both. forward_list doesn't have size() or back().
vector::size() doesn't provide any benefits compared to std::distance. Should it be removed as well? Is empty() also excessive because the same can be achieved by begin() == end()? I understand your point regarding other container operations but acquiring the size of a range is a fairly typical operation and not having it for some iterator types is rather limiting. It's a matter of a common interface for all ranges and convenience. Having to dispatch between std::distance and T::size() in the user's code is not an upside of the iterator_range interface.
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.