Back in 2015 I read Effective Modern C++ by Scott Meyers [1], which is his latest entry in the saga of "Effective C++" books. It covers all the new stuff that got into the language with C++11 and C++14, and shows how modern C++ can be written.
My feeling during the read, and my definite conclusion afterwards, is that I didn't read a collection of new nice additions to the language, but a list of minefields and new fancy ways to crash your program. That there is no chapter which doesn't end up being a list of gotchas. Seemingly every new feature was added as an extension of the language's reputation of being a shotgun that loves your feet. "Oh, now we have lambdas! here are the 32 ways you can fail while using them, and the language will do nothing to help you prevent".
Effective Modern C++ is a very well written book and very instructive. Its flaws are none of the book or writer, but of the language itself.
Yeah, Effective Modern C++ has always read like a list of warnings. C++ is kind of like Python in that the design of the language does not steer you into making good, idiomatic decisions. No one is going to guess most of the stuff that is taught in that book without reading it first from working with the language or reading other people's code, and really a full list of such things for C++ could be many hundreds or even thousands.
I also agree that the new features seem to embrace and extend the "footgun-ness" of C++, rather than attempt to fix it.
> the design of the language does not steer you into making good, idiomatic decisions.
That's much easier to do when the tasks that the language is suitable for are bounded in some way.
There are not many common idioms that are going to be shared (beyong libstdc++) between a realtime audio processing application and a data visualization backend for the web and an embedded system control app and a payroll processing backend.
I don't agree. I can think of many languages that obfuscate their idioms that have bounded applicability. Obviously I already mentioned Python, which is obviously bounded to less performant applications (unless you call C code), but another one is SQL. For example, Azure SQL is extremely slow when using sub queries compared to CTEs, but the language does not make this apparent in its design.
An example of a language with a smaller domain which makes its idioms obvious through its design is Ruby.
An example of a high-performance language that can be used for all those things that makes its idioms obvious through its design is Rust. Another would be, imo, C.
> There are not many common idioms that are going to be shared (beyong libstdc++) between a realtime audio processing application and a data visualization backend for the web and an embedded system control app and a payroll processing backend.
I also just generally have to disagree with it, but you seem to be using a different definition of idiom to me. From what you said here, you seem to take idiom to be synonymous with "design decision". For me, an idiom is a specific semantic construct, possibly associated with specific syntax, that achieves a specific basic programming task. For example, the idiomatic way to loop with an index in Python is to use enumerate.
It's merely the way that most people do the basic things, in a way that is actually good to do. C++ is infamous for a huge number of people using the language dangerously, to the point where there are effectively no idioms outside of specialist communities, and to find them out you need to read books. A significant amount of the basic design of the language needs to be ignored for most purposes.
One of the main places I see idioms in other languages is in dealing with containers (and there are some very nice ones). Python in particular, but others too. Slicing, splicing, iterating, mutating, map-reduce etc. etc.
While I wouldn't say no to having such idioms available in C++ as language constructs, it is important that C++ continues to provide the lowest level that it can for such tasks (e.g. pointers into arrays or other buffers), and probably the level right above that (libstdc++).
Similarly for threads (or more generally, parallel and asynchronous programming) - some other languages make available some really nice idioms for expressing these things, but again it is important for performance and control reasons that C++ continues to provide the current lowest level of access for this (basically a wrapper around pthreads). While things like promises, futures and coprocessing are really useful, they are often inappropriate for highly performant code.
If rust can do it so well, I see no remaining argument about performance and control. It was long believed that C++ had to be bad in this way because such languages must be. Rust was the counter example that ended this myth
That's not clear to me. The examples I've seen from anywhere that Rust has to deal with hardware suggest that you have to throw out most of the safety guarantees of the language, and this applies also to dealing with OS-provided abstractions of hardware (like MMU-mapped registers and mmap-ed memory regions).
I don't have the impression that Rust's container idioms match those of, say, Python, but I may just not know Rust well enough.
You’re mistaken then, using unsafe does not mean throwing away the safety guarantees. This is a common misconception though, even among Rust users
OK, let me put it differently: you cannot reason about safety in those circumstances in the way you can when "unsafe" has not been used. And since reasoning about safety (or perhaps more accurately, not needing to) is one of the raison d'etres for Rust, that's a bit of a hit. Not huge, but for those of us close to metal of some sort, it's a bit of a hit.
E.g. https://github.com/glium/glium
Rust would be useless if you couldn’t have safety if you had any unsafe at any point. That’s the whole point of rust: you can build safe interfaces to unsafe actions