Remix.run Logo
lordleft 5 days ago

Great article. Modern C++ has come a really long way. I think lots of people have no idea about the newer features of the standard library and how much they minimize footguns.

sunshowers 5 days ago | parent | next [-]

Lambdas, a modern C++ feature, can borrow from the stack and escape the stack. (This led to one of the more memorable bugs I've been part of debugging.) It's hard to take any claims about modern C++ seriously when the WG thought this was an acceptable feature to ship.

Of course, the article doesn't mention lambdas.

TuxSH 5 days ago | parent | next [-]

Capturing lambdas are no different from handwritten structures with operator() ("functors"), therefore it makes no sense castrating them.

Borrowing from stack is super useful when your lambda also lives in the stack; stack escaping is a problem, but it can be made harder by having templates take Fn& instead of const Fn& or Fn&&; that or just a plain function pointer.

loeg 5 days ago | parent | next [-]

Convenience is a difference in kind.

Like, I'm not god's gift to programming or anything, but I'm decently good at it, and I wrote a use-after-return bug due to a lambda reference last week.

sunshowers 5 days ago | parent | prev [-]

Borrowing from the stack is definitely useful. I do it all the time, safely (truly safely), in Rust.

bluGill 5 days ago | parent | prev | next [-]

They can, but I find in practice that I never do this so it doesn't matter.

sunshowers 5 days ago | parent [-]

I'm glad, but my problem is with the claim that modern C++ is safer. They added new features that are very easy to misuse.

Meanwhile in Rust you can freely borrow from the stack in closures, and the borrow checker ensures that you'll not screw up. That's what (psychological) safety feels like.

fluoridation 5 days ago | parent [-]

Lambdas are syntactic sugar over functors, and it was possible all along to define a functor that stores a local address and then return it from the scope, thus leaving a dangling pointer. They don't introduce any new places for bugs to creep in, other than confusing programmers who are used to garbage-collected languages. That C++11 is safer than C++98 is still true, as this and other convenience features make it harder to introduce bugs from boilerplate code.

sunshowers 5 days ago | parent [-]

The ergonomics matter a lot. Of course a lambda is equivalent to a functor that stores a local reference, but making errors with lambdas requires disturbingly little friction.

In any case, if you want safety and performance, use Rust.

fluoridation 5 days ago | parent [-]

>making errors with lambdas requires disturbingly little friction

Not any less than other parts of the language. If you capture by reference you need to mind your lifetimes. If you need something more dynamic then capture by copy and use pointers as needed. It unfortunate the developer who introduced that bug you mentioned didn't keep that in mind, but this is not a problem that lambdas introduced; it's been there all along. The exact same thing would've happened if they had stored a reference to a dynamic object in another dynamic object. If the latter lives longer than the former you get a dangling reference.

>In any case, if you want safety and performance, use Rust.

Personally, I prefer performance and stability. I've already had to fix broken dependencies multiple times after a new rustc version was released. Wake me up when the language is done evolving on a monthly basis.

sunshowers 4 days ago | parent [-]

So you agree that modern C++ adds new ways to introduce memory unsafety?

fluoridation 4 days ago | parent [-]

Your question is too broad. I'd have to think about it, but intuitively I'd say no, I don't agree with that. More to the point, lambdas don't introduce any new avenues for memory bugs. Like I said, at the worst they trick inexperienced programmers coming from garbage-collected languages into thinking the platform will deal with lifetimes for them.

sunshowers 4 days ago | parent [-]

The person who introduced the bug was highly competent and had at least 10 years of C++ experience. He was sure he'd gotten it right and himself didn't believe how subtle the bug turned out to be.

The people coming from GC languages have the right expectations about the language taking care of lifetimes for them. I expect nothing less than technical excellence from my tooling.

fluoridation 4 days ago | parent [-]

To clarify, I'm not interested in debating the skills of any particular programmer. My sole point is that lambdas didn't bring any new ways of mismatching lifetimes. That programmer could have made the exact same mistake with a functor instead and it wouldn't have changed anything.

>I expect nothing less than technical excellence from my tooling.

Good luck with that.

sunshowers 4 days ago | parent [-]

I've been having great luck expecting technical excellence from Rust and its community, thank you!

account42 4 days ago | parent | prev | next [-]

Not an issue if you make use of the tools available:

https://godbolt.org/z/xW14hGeoj

sunshowers 4 days ago | parent [-]

It looks like (a) this is a warning, not an error (why? the code is always wrong) and (b) the warning was added in clang 21 which came out this year. I also suspect that it wouldn't be able to detect complex cases that require interprocedural analysis.

The bug I saw happened a few years ago, and convinced me to switch to Rust where it simply cannot happen.

im3w1l 5 days ago | parent | prev [-]

Why wouldn't it be acceptable to ship? This is how everything works in C++. You always have to mind your references.

sunshowers 5 days ago | parent | next [-]

Exactly! This is my problem with the C++ community's culture. At no point is safety put first.

Yoric 5 days ago | parent | next [-]

Yeah, it's great that the C++ community starts to take safety in consideration, but one has to admit that safety always comes as the last priority, behind compatibility, convenience, performance and expressiveness.

StillBored 5 days ago | parent | prev [-]

Its worse. The day I discovered that std::array is explicitly not range/bounds checked by default I really wanted to write some angry letters to the committee members.

Why go through all the trouble to make a better array, and require the user to call a special .at() function to get range checking rather than the other way around? I promptly went into my standard library and reversed that decision because if i'm going to the trouble to use a C++ array class, it better damn well give me a tiny bit of additional protection. The .at() call should have been the version that reverted to C array behavior without the bounds checking.

And its these kinds of decisions repeated over and over. I get its a committee. Some of the decisions won't be the best, but by 2011 everyone had already been complaining about memory safety issues for 15+ years and there wasn't enough politics on the comittee to recognize that a big reason for using C++ over C was the ability of the language to protect some of the sharper edges of C?

fluoridation 5 days ago | parent | next [-]

>Why go through all the trouble to make a better array, and require the user to call a special .at() function to get range checking rather than the other way around?

Because the point was not to make an array type that's safe by default, but rather to make an array type that behaves like an object, and can be returned, copied, etc. I mean, I agree with you, I think operator[]() should range-check by default, but you're simply misunderstanding the rationale for the class.

StillBored 4 days ago | parent [-]

Which goes to the GP's point, which is that security and robustness are not on the radar.

And my point in providing a concrete example, where a decision was made to prioritize unsafe behavior in a known problematic area, when they could just as well have made a half dozen other decisions which would have solved a long standing problem rather than just perpetuating it with some new syntactic sugar.

fluoridation 4 days ago | parent [-]

I didn't dispute that, I was simply addressing the point about std::array. The class is not meant to be "arrays, but as good as they could possibly be". It's "arrays, but as first-class objects instead of weird language constructs".

That said, making std::array::operator[]() range-checking would have been worse, because it would have been the only overload that did that. Could they have, in the same version, made all the overloads range-checking? Maybe, I don't know.

TinkersW 5 days ago | parent | prev | next [-]

std::array [] is checked if you have the appropriate build settings toggled, which of course you should during development.

The same applies to many of the other baseless complaints I'm seeing here, learn to use your tools fools.

mbac32768 5 days ago | parent | prev [-]

Good news! Contracts were approved for c++26 so they should be in compilers by like 2031 and then you can configure arrays and vectors to abort on out-of-bounds errors instead of corrupting your program.

Let no one accuse the committee of being unresponsive.

BeetleB 5 days ago | parent | prev [-]

This is like writing an article entitled "In Defense of Guns", and then belittling the fact it can kill by saying "You always have to track your bullets".[1]

[1] Not me making this up - I started getting into guns and this is what people say.

im3w1l 5 days ago | parent [-]

To me it's as if someone releases a new gun model and people single that gun out and complain that if you shoot someone with it they may die. Like it's a critique of guns as a concept not of that particular one.

In a complete tangent I think that "smart guns" that only let you shoot bullseye targets, animals and designated un-persons are not far off.

Symmetry 5 days ago | parent | prev [-]

I eagerly await the day when they do away with the distinction between ".cpp" and ".hpp" files and the textual substitution nature of "#include" and replace them all with a proper module system.

alextingle 5 days ago | parent [-]

Modules are a disaster.

It's hard enough to get programmers to care enough about how their code affects build times. Modules make it impossible for them to care, and will lead to horrible problems when building large projects.