25 ms·
> Starting in C++17, a[b] always evaluates a before evaluating b. Okay, I'll bite. Why did C++17 specify this?
by cornstalks 4y ago
> Starting in C++17, a[b] always evaluates a before evaluating b.
Okay, I'll bite. Why did C++17 specify this?
- olliej 4y agoBetter question: why did C and C++ not define this as left to right (the answer is perf claims decades ago, that are not really valid now, and questionable even then). The example a[b] doesn't really exhibit the unexpected, but as I understand it f()->a[b()] could evaluate as temp = b() f()->a[temp] which it seems reasonable to consider unexpected. The lack of left to right sequencing here seems not-dissimilar to the lack of sequencing in call arguments, which was also realized to be a needless footgun and fixed.
- TremendousJudge 4y agoHalf of the new features of "modern" C++ editions leave me agreeing with the change but wondering what they were thinking before. Another example: std::map::contains was introduced in C++20; why did they go decades thinking it wasn't necessary to have such an operator (instead providing only count() even though a map has unique keys), and why did they change their minds only now? Did the old guard literally die off or something?
- olliej 4y agoThe historical lack of sequencing is because they were unsequenced in C, and C++ thus inherited it. And yes, it does seem that there were a lot more people hell bent on resisting correcting unnecessary UB in the past than today, but they’re still around.
- TremendousJudge 4y agoI get it, but then, why change it now? (or 6 years ago I guess)
- olliej 4y agoSorry, I had an unsaved edit above. It seems the resistance to removing unnecessary UB has reduced over the years.
- PaulDavisThe1st 4y agostd::map::find (item) != map.end() <=> std::map::contain (item)
- zimpenfish 4y agoI think the problem is that with `std::map::find(item)` you get the value back but you don't with `std::map::contain(item)` - which means a second lookup to actually retrieve the value, no?
- tubs 4y agoProblem with contains is we are now going to see more code like if (map.contains(foo)) { bob(map[foo]); } over the (vastly uglier) more efficient: if (auto it = map.find(foo); it != map.end()) { bob(*it); } Of course languages like C# manage this in a more elegant way with out parameters declarable at the call site.
- JonChesterfield 4y agoA pity the c++ compiler has no way to recognise calls to the c++ library in order to do rewrites like that automatically. It's CSE at the stdlib level, should definitely be possible to do that.
- eklitzke 4y agoHard coding behavior like for the STL that seems pretty questionable, especially given that std::map and std::unordered_map have poor performance compared to other alternatives (e.g. absl::btree_map and absl::flat_hash_map, and likewise folly has better implementations).
- im3w1l 4y agoif let is such a nice way to express this if let Some(x) = map.get(foo) { bob(x) } One subtle but noteworthy thing is that the idiom only mentions map by name once, which is nice if you have a very long map name, and prevents copy paste bugs where you only update one of the two mentions.
- olliej 4y agoThis pattern exists in C++ as well. The specific issue here is that all these STL APIs are in terms of C++'s iterator model, and can't be replaced with a more modern "optional" style that allows this cleaner coding style :-/
- paulddraper 4y agoScala: for (x <- map.get(foo)) { bob(x) } // or map.get(foo).foreach(bob) // or map.get(foo) match { case Some(x) => bob(x) }
- deleted 4y ago[deleted]
- eklitzke 4y agoBecause otherwise the evaluation order of the following construct is undefined, and you can get different results if you compile your code with different compilers: foo()[bar()]; In nearly all cases it won't matter, but it can matter if foo() and bar() have some kind of shared state (or affect each other's return values, but seriously don't do that). In general it's better to have a defined evaluation order unless there's some compelling reason not to.
- addaon 4y agoBut both C and C++ are comfortable with *(foo() + bar()) having undefined evaluation order, so foo()[bar()] potentially having order-dependent results is clearly not sufficient reason for this decision...
- Dylan16807 4y ago> In general it's better to have a defined evaluation order unless there's some compelling reason not to. That is not the stance C++ has taken in the past, so the question of why they changed it is still unanswered.
- ynik 4y agoTo allow for chaining. Consider a statement like: a.f(foo(1)).g(foo(2)).arr[foo(3)].h(foo(4)); In C++14, the foo() calls happened in unspecified order. C++17 guarantees that "a(b)" and "a[b]" evaluate "a" first, which effectively means the foo() calls will now happen in order (1 to 4).