This is a meta comment Andrei and I discussed earlier today.
C++ tries to make 99.9% of the use cases doable, but they do it at the cost of 90% of C++ programmers being unable to implement a usable iterator or container. If you've ever peeked under the hood at the implementation of STL iterators and containers, you know what I mean. If you've ever wondered what an rvalue reference was and why, you know what I mean. If you've noticed the dearth of C++ reusable libraries, while such grow like weeds for Java/C#/Python, you know what I mean.
I think what we're doing with struct ctors, dtors, postblit and opAssign is in the right direction. In C++, if you provide a copy constructor, but forget the opAssign, almost certainly the default opAssign generated by the compiler will be wrong (it's a memcpy). Even worse, this error will often generate corruption at runtime.
D, on the other hand, builds a copy constructor out of a postblit, meaning that the compiler generates for you most of the mechanics of the copy constructor. The postblit only deals with where it differs from a memcpy. Best of all, a *correct* default opAssign is automatically generated out of the postblit and destructor.
You might be able to hand write a more efficient opAssign, but if you don't, at least you'll get a logically correct one, rather than the C++ default one which is almost certainly wrong.
Applying the same idea to ranges suggests that missing functionality should be either automatically and correctly generated, or if that is not possible, issue a compile time error.
Additionally, if D range/container implementations start looking anything like C++ STL implementations in terms of complexity, we've failed.
