3 ms·
It took me some time to recognize the problem with range-for (after testing its implementation in VC 2012), and more time to work up the courage to write a Core
by StephanTLavavej 12y ago
It took me some time to recognize the problem with range-for (after testing its implementation in VC 2012), and more time to work up the courage to write a Core Language proposal. I also wish next-gen range-for had gotten into C++14, but that's mostly a formality - compilers can (and often do) ship features as soon as they're voted into the Working Paper.
Note that nothing's wrong with "const auto& elem", except that it prohibits modification. On the other hand, "const auto&& elem" will typically not compile (it'll insist on binding to rvalues, which only proxy iterators return).
- r-s 12y agoFirst off, its Great to see you on HN! For the few of you who don't know, Stephan maintains Visual Studio's C++ Standard Library implementation and he is extremely well regarded in C++ circles. Thats of course right about const auto &&.. It won't work, I did mean (auto &&elem : range).
- jzwinck 12y agoAs I was reading through your "for (elem : range)" proposal I became quite anxious to know how adding const would be handled. I was hoping that "for (const elem : range)" would be added to mean "for (const auto& elem : range)" because I very often find myself within a non-const method having a non-const container but wanting to iterate with no chance of modifying it. I was at first relieved to see this topic addressed at the top of Q&A, then disappointed to see "Just do it the old way if you want const." I feel that we don't use const enough in C++, and making it easier (yet still clear) to use would be of significant benefit. If you agree, perhaps there is still time to support "for (const elem : range)" - it seems to me natural and no more disruptive than the rest of the proposal.
- StephanTLavavej 12y agoI talked about this some more in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3994.htm http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n399... Q17. Many people have asked for such syntax, but I am wary of adding more complexity to the Core Language. Ultimately, I believe that this should be handled on the range side instead of the element side. What Evolution/Core needs to do is to fix the problem with nested temporaries in range-for loops - the problem (affecting both C++11's range-for and mine) is that range-for will keep temporary ranges alive if they're the topmost thing returned, but not if you have "const T& noop(const T&)" and you try to loop over noop(return_temporary_range()). They're aware of the problem, which is a start. If and when that's fixed, then what you want can be handled through library tech: "for (elem : as_const(range))".
- matt_d 12y agoOut of curiosity, how about making `const` the default and requiring `mutable` for mutation? There's already a Standard precedent in form of C++11 lambdas -- and const-by-default has some technical niceties (perhaps simplicity, too -- arguably making this the default removes some complexity from the language from the beginner's point of view, e.g., by preventing accidental mutation) that may make this worth it. // I see that you addressed the constness in A17, but I believe allowing the `mutable` opt-in, as in the lambdas, takes care of the "limiting" aspect; "confusing", OTOH, is a matter of taste -- after all, `std::for_each` operates on InputIterators as well (thus, perhaps the similarity with a non-modifying nature[1] of a Standard Library analog is arguably preferable from the consistency / least-surprise-principle point of view?), and, again, lambdas also have constness by default. Thoughts? // [1] -- in principle, at least (for completeness, there's an allowance for nonconstant functions w/ mutable iterators)
- StephanTLavavej 12y agoI love const, but I am strongly opposed to adding constness here - as I mentioned, the non-Standard "for each" extension did that, and it caused endless confusion. Note that the lambda precedent is not actually applicable, because it affects only value captures. Reference captures can always be written through. The purpose of next-gen range-for is to operate in-place, i.e. with reference semantics. for_each() does not add constness, and can modify elements in-place. The fact that it is grouped with the "non-modifying algorithms" is a confusing historical artifact (and was actually the subject of the first Library Issue I had a part in filing) - the algorithm itself does not modify things (unlike sort(), say) but the given functor can.
- matt_d 12y agoThanks for the reply! Interesting that the constness in `for each` caused confusion (did it offer a `mutable` opt-in, though?), would intuitively expect it to be the POLS behavior. I guess given that you were probably receiving feedback on that, I will take it as something to be acknowledged. I can see the reference semantics point, in this context the difference from the lambdas seems to make more sense. // Just to explore another avenue, again mostly out of curiosity :-), how realistic (from the impl. POV) would be to have value-semantic range-based `for` with _mandated_ copy elision whenever possible? True about `std::for_each`, that's what I've referred to as the "allowance" for the mutable iterators, wasn't aware about the grouping being merely a historical artifact, though. I've always felt a bit dirty using it for mutation[1], it seems that maybe unnecessarily so :-) // [1] -- perhaps due to the algorithm being specified in terms of the InputIterator concept; hm, that being said, I suppose that while it only guarantees that we can read (dereferenced) `it`, it doesn't say that `it` _itself_ has to be immutable (right?), so it could be that I should think of a better metaphor to internalize. How do you think about InputIterators?