6 ms·
std::vector<int> suffix{3, 4}; auto append34 = make_appender(suffix); // Version 1: test will work auto append34 = make_appender({3, 4}); // Version
by jstimpfle 4y ago
std::vector<int> suffix{3, 4};
auto append34 = make_appender(suffix); // Version 1: test will work
auto append34 = make_appender({3, 4}); // Version 2: test will fail
I was in a mood to type the examples but I accidentally made Version 1 because I had started with the author's first example. I wondered where the problem was and couldn't find any (neither running nor reading the code) until I noticed the author had changed to Version 2.
The problem is "obvious" in hindsight but one of the problems with C++ is that it makes certain things too implicit/convenient. I get that references are a staple feature in C++ but it's not at all obvious from the calling location that Version 2 is a bug. To see that, you have to navigate to the callee (finding which might be hard enough alone without a very solid IDE), and make sure that it's not capturing the argument by reference or not doing anything unsafe there.
It's often a problem when a seemingly "value" argument is turned into actually a pointer-to argument at the callee. There are other languages that have this too, and I've never liked it.
As someone who doesn't regularly code in C++ but has a solid understanding of the basics, I wonder why C++ ever allowed to have a reference parameter be called with a temporary? To me it feels like "References have value syntax but pointer semantics BUT you should program like it had value semantics"? Which to me would be exactly a premature optimization that is looking for trouble.
- benreesman 4y agoAgain, this isn't a right/wrong thing. Rust moves by default (and a lot of people find "=" a weird pun for that), C++ copies by default and has rvalue-references and an explicit `std::move`. If you want copy in your C++ lambda, you start it `[=](...) {...}`, if you want take a pointer in your C++ lambda, you start it `[&](...) {...}`, if you want something trickier you do trickier stuff. Rust opts you in to the nitpicky static analyzer and you have to opt out with `unsafe`, in C++ you have to opt in with e.g. `clang-tidy` or some annotations. They are remarkably similar, just with different defaults.
- tialaramex 4y ago> They are remarkably similar, just with different defaults. I'm going to lead with this, because I think it's most important: Culturally there's a world of difference. Safety is a part of Rust's culture. "Culture eats strategy for breakfast". Take sorting. In C++ the default sort is unstable, while in Rust the default sort is stable, that's just those defaults you mentioned (each has both kinds), although the choice speaks to culture. But look closer, in C++ the sort has undefined behaviour if your type isn't totally ordered. In Rust you can't sort the partially ordered types without saying how to order them fully. Still, in both languages we can write a custom order, so what happens then? In C++ if your custom order is nonsense you get... undefined behaviour. In Rust sorting won't necessarily work with a nonsense custom order but the behaviour remains well defined. > Rust opts you in to the nitpicky static analyzer and you have to opt out with `unsafe` Unsafe gives you a small number of dangerous "super powers" needed to write efficient low-level code, it does not opt out of the borrow checker's analysis, or indeed most other checks. This misconception makes me wonder how much of what you've written is conjecture rather than practical experience.
- benreesman 4y agoThe sibling says that C++ has the wrong defaults, full stop. Well in Rust the default `HashMap` uses a cryptographic hash, and you see it everywhere, it's the de facto community "default". In C++ the community "default" is `absl::flat_hash_map`/`folly::F14`, which use SIMD to compare a whole stripe of key-prefixes simultaneously. I want different defaults for different programs, but the idea that it's esoteric to ever want an associative container within arms reach that fucking demolishes the other one is, ugh, God I want to like Rust even more than I do but this "we're right and everyone else hasn't seen the light" routine is infuriating and pushes me at least away. My parent comment is trying to emphasize that this isn't a right/wrong thing, different tools for different jobs. And people are just like: "nope, everything but Rust is wrong".
- mattgreenrocks 4y agoI like and use both C++ and Rust. I also have plenty of bones to pick with both languages. However, I’ve never gotten the sense that Rust itself promotes the idea that “everything except for Rust is wrong.” I also don’t read much on the Internet these days, and I’m not doing so, probably avoid much of the hype that people are pushing about Rust. Since it has been established as Hot New Thing, there are huge social incentives tied up in promoting it.
- jjnoakes 4y agoWhy do you think that a language with better details would prevent you from opting in to whatever non-default behavior you need? Having the "right" defaults is better for everyone. Folks who don't know or care get a good, safe default with no undefined behavior or unexpected danger, and folks who know better can opt into something that fits their needs explicitly. Seems ideal to me.
- jjnoakes 4y ago> a language with better details Whoops - that should have said "a language with better defaults"...
- 4y ago
- pjmlp 4y agoThere lies the problem with C++ state of affairs, most defaults are wrong. It doesn't help that those of us that care about enabling the right defaults are a tiny minority as per C++ surveys. https://blog.jetbrains.com/clion/2021/07/cpp-ecosystem-in-2021/#Lets_talk_about_the_code_quality https://blog.jetbrains.com/clion/2021/07/cpp-ecosystem-in-20...
- snovv_crash 4y agoYou can also 'const' your std::vector when you declare it and it won't work. Any linter or having warnings turned on will catch these issues.
- jstimpfle 4y agoconst doesn't make a difference in this case. It's about the passed-by-reference object being destroyed after the function returns. That is because the object was passed as a temporary.