for (auto const & ess : esses) {
...
}
from my cold dead hands.Also, you can fight me if you want to take
dynamic_cast<Derived> (base_ptr)
and force me to implement my own typing system every time I need to upcast.Basically, stick with C and leave C++ programmers alone. I haven't seen a less useful article about C++ in a long time, and as an HN reader, that's really saying something.
And that utility library (there are dozens of them) is just as subject to debate and issues as libstdc++
I am not going to implement my own C versions of 90% of the stuff std:: provides, sorry.
And there lies the problem with C++: to be sure, you have to check. C++ code can't be taken at face value -- the most innocuous-looking code could be a ticking bomb.
C++ folks are very much into their language, and can't seem to understand that most folks don't want to dedicate significant amounts of mental resources purely to language details.
It's really hard to take your comment serious because of generalization like this. Maybe they're not usable for your particular usecase but that doesn't mean they suck. Just like there's a 'million' ways that C++ sucks in your book, there's a reason there's millions of lines of code out there where these containers are valid usecases and hence work without issues whatsoever nor a need to replace them with something else.
Only the really big ones, e.g. here is Emscripten's allocator that focuses on small binary size and is implemented in about 1.5 kloc (ignoring comments and whitespace it's actually under 1 kloc), and that allocator is perfectly fine for most use cases (especially C code bases which typically don't have a high allocation frequency):
https://github.com/emscripten-core/emscripten/blob/main/syst...
...and Seb Aaltonen's offset allocator (used for allocating GPU buffers in his Vulkan API wrapper) has under 500 lines of code:
https://github.com/sebbbi/OffsetAllocator/blob/main/offsetAl...
Right tool for the job etc... big general-purpose allocators like jemalloc or mimalloc are usually a bandaid to somewhat salvage a failed memory management strategy.
In C++, you can overload the comma operator to do shit. I've seen it done. There's no reason to do it, and no reasonable person would ever expect it in a code base they're unfamiliar with. To find bug in that ultimately roots back to that implementation, you have to go eliminate every other whack-job possibility before it even occurs to you that maybe the weirdo who wrote this code chose to overload the comma operator.
I'm not going to argue with anyone who wants to use C++ in their own projects, you do you. But let's be real about what C programmers are complaining about. It's not line count. It's syntactic obfuscation. I don't just level this criticism at C++ either. Basically every major new language has its own byzantine syntactic constructs to some degree.
This is allowed by Orthodox C++
> dynamic_cast<Derived> (base_ptr)
This isn't because it requires RTTI, but dynamic_cast is also a typical code smell.
Orthodox C++ isn't generally against new C++ features, it only advices to wait about 5 years (or at least one C++ version) for stabilization and to apply some common sense before adopting them.
The notes about not using RTTI, exceptions and stdlib features that allocate under the hood are all justified by painful experience with those things in the context of game development.
In general, the restrictions outlined in the post make a lot of sense when considering that Branimir (of BGFX fame: https://github.com/bkaradzic/bgfx) is coming out of the game dev hemisphere, and from that PoV none of the restrictions are controversial - on the contrary, it would be highly controversial to suggest going all in on Modern C++ features ;)
I can see no rationale for this whatsoever. It is nothing but syntactic sugar.
> Branmir (of BGFX fame
Appeals to authority don't really work for me.
I've been writing a cross-platform DAW (0) for 25+ years, in C++, and what a game dev has to say about the language in their own work might be of passing interest but not much more.
Being aware of the pitfalls of particular features of a language is an important task for anyone programming in that language. But that doesn't mean that the language is fundamentally broken or that programmers cannot make their own choices about which features to use.
(0) on at least the same level of complexity as a modern game
The problem with this is that whoever is reading the code as-is does not know what type "ess" is. Sometimes you get the definition somewhere nearby, in which case it is probably fine - assuming it is close enough that it'll be included in a diff - but more often than not you don't know.
Yes, an IDE can probably tell you (probably, depends on the IDE and assuming everyone uses one) but even that requires some extra action like moving the mouse over the definition and hoping it'll give you something. However this wont show up in diffs, PRs, code reviews, etc.
IMO `auto` is one of those C++ features that really needs discipline to use - and when in doubt, i'd rather ban its use (except where you cannot do otherwise) than rely on everyone doing the right thing.
Yes, just use and IDE. This is a problem in Rust as well. And C#, Java, and others.
IMO you should use auto as much as possible. If the code can be written with auto, it should be. There’s no reason to repeat type definitions.
If you can use auto, what that means is the type is already statically known. C++ is a statically typed language; the compiler and tools know what type things are. So, just ask the tools, because they’re not wrong.
Moreover, what's even more beautiful? You can change the type of things in the container "esses", and the code doesn't need to change.
If you have the experience, this construct tells you everything you need to know: it's an iteration over a container, visiting every element in order, without copying it, and without modifying it.
You don't need to know any more.
Of course some discipline is required - as with just about everything in programming, especially in C++ - but developers in just about every other statically-typed language lean on type inference (including far more extensive type inference) and don't wring their hands about it. It's hard not to see this as a case of Blub - if you learned about typing with `Foo foo = new Foo()`, anything different might seem scary.
...anyway, in this case the real win probably is the range-based for loop, rather than the auto. `for (const Foo& foo : foos)` isn't so bad, but `for (std::vector<Foo>::const_iterator it = foos.begin(); it != foos.end(); ++it)` is pretty rough.
As important as the code to be readable.
> The odds are very good that it's clear from the context
As i wrote, if the actual type can be seen somewhere nearby (close enough to be included in diffs) then that's fine - it is already in the context. Though that is not usually the case and i personally had to work with code with which i was not familiar and which used `auto` all over the place and had to go hunting for the actual container declaration to see what it is (Visual Studio was not helpful in its tooltips).
So my actual experience is that i'd rather see the actual type.
However...
> anyway, in this case the real win probably is the range-based for loop
...yes, the range-based for loop is often the better choice when it comes to readability. And when compared with the iterators, it is pretty much always more readable than them :-P. `for (const Foo& foo : foos)` is basically what i'd prefer to see. It is the use of `auto` i pointed out.
Or if you're even lazier, you can easily make a more automatic RTTI system with some templates / macros that works much faster than dynamic_cast! (See https://github.com/royvandam/rtti)
E.g.:
https://github.com/floooh/oryol/blob/eb08cffe1b1cb6b05ed14ec...
(don't use that project though because it's been archived, I've switched back to plain old C in the meantime)
Templates are really amazing, once you learn the patterns there's basically a template version and a runtime version of things (policy types, etc.). All of that is great.
Legibility and understanding are in my opinion the most important aspect of any programming language, so Orthodox C++ is superior for maintainable code.
I'm curious what languages you're comparing to here. Feels like it's only slightly more expressive than pure generics, but I admittedly haven't done much template metaprogramming myself. How does it compare to, say, Zig's comptime?
spot on, in my experience. nothing in zig triggers my lite OCD.
Template metaprogramming is sometimes very useful to get around C++'s language restrictions, but I tend to use it sparingly.
With concepts and constexpr-if and consteval it's increasingly less of a problem
There are many more things to avoid than just iostream. HFT university has a good recap: https://hftuniversity.com/post/the-c-standard-library-has-be...
The point on exceptions I think is also misleading. Compilers typically make throwing an exception the expensive part, and the happy path inexpensive (not more expensive than a branch checking for errors, which should be the baseline for comparison, not an implementation with zero error checking.) So to say that they are "expensive" doesn't really make a useful argument.
And there are more things that could be done in this camp, like proposing a set of compiler flags, and a linter to enforce the subset you are subscribing to. Unfortunately the post offers none of that.
> <deque>: Needs a major performance overhaul", acknowledging that the standard's mandated block size is too small and the design needs to be rebuilt at the next ABI break
Except of course the standard does not mandate a block size. That's purely msvc picking a wrong block size and being stuck with it.
The rant about lists is also nonsense.
And why is the latter nonsense? lists have had terrible cache performance for decades now and vector is the better default choice of container.
But even "avoiding iostream" is stupid. The author presumably really means "avoid operator>> and operator<< for I/O". Even using type-safe printf-like stuff ultimately still sits on top of iostream.
You are also wrong.
The author used Claude to generate it and instructed it to intentionally insert mistakes. They had comments posted on Reddit that discussed doing this and their account is a slew of AI generated responses. The short responses where it appears they didn't use AI don't reflect the behaviour you'd expect from somebody that claims to have over 30 years of experience in HFT but rather point toward them being a teenager or somebody rather immature. They also make a number of wild claims that when put together, are unlikely to be true.
Sorry if you can't recognise it for what it is and shame on you if it's your website, given that you've previously submitted content from there.
I would sure love a good coroutine runtime, and first-class support for defer. You can do these manually, but language/toolchain/debugger support is nice to have.
(Pragmatically, I will be retired by the time they would be useful)
As far as avoiding things... avoid basically everything you don't need. Don't add language features that don't actually help you, just because they're there. Keep the subset you use small. But pick that subset to match your problem well, rather than out of dogma.
Being able to write "async" code essentially in-line is a superpower.
On the other hand, if you control your own destiny and care about velocity and code quality, many of these choices eventually become self-evident.
If you are messing around with the latest and greatest esoteric C++ stuff in 2026, bless you, you beautiful nerd. But it may be time to start evaluating where you are in life, and how you got here. (And if you're on a C++ committee, I revoke those blessings.)
For those who remain: if you have a C++ code base yet somehow have enough time and energy to write opinionated blog posts, it's really hard to imagine why you think you'd have a better take on this than Google.
Sounds like you hate exceptions, right? In which case why do you handle them at all? Just leave them all unhandled and suddenly every exception is a crash. Which is really no different from someone choosing to terminate. Which you have to worry about even without exceptions.
> if you have a C++ code base yet somehow have enough time and energy to write opinionated blog posts, it's really hard to imagine why you think you'd have a better take on this than Google.
"Given that Google's existing code is not exception-tolerant [...] Our advice against using exceptions is not predicated on philosophical or moral grounds, but practical ones. [...] Things would probably be different if we had to do it all over again from scratch."
One of the problems with exceptions is it’s utterly impossible to know if a given function call can return exceptions and if so what they are.
My code DOES want to handle errors. Exceptions are, imho, a very very poor way to report errors.
Python is the bloody worst because I never effing know what the hell any damn function can throw or return. It’s so so frustrating.
Error handling is a hard and unsolved problem. But I’ll take Rust results over exceptions 10,000% of the time. Not even a question.
Maybe the tooling finally caught up.
Do you mean this as an appeal to Google being the home to great talent, or more as an endorsement of the specific guidance provided by this specific style guide, Google or no?
Because if the former, I think I do almost everything better in my context than Google would. It would be hard not to considering the difference in organizational scale.
> C-like C++ is good start, if code doesn’t require more complexity don’t add unnecessary C++ complexities.
C is almost obsolete nowadays. Not to mention that C++ is effectively a strict superset of C (nearly 99% of the C standard is in C++) and the few features that aren't are included as compiler extensions (VLA, restrict keyword, nested functions). There are a handful of C features that aren't in C++, and for very good reason (most of them suck). When was the last time you ran into a C library that a pure C++ compiler couldn't compile? Only if someone decided to spam the new keyword all over the codebase (or something similar).
> In general case code should be readable to anyone who is familiar with C language.
Most C++ already is? Even very template heavy C++.
> Don’t do this, the end of “design rationale” in Orthodox C++ should be immedately after “Quite simple, and it is usable. EOF”.
A lot of the methods in that document are necessary to make C++ shine, especially template metaprogramming.
> Don’t use exceptions.
Optional but irrelevant.
> Don’t use RTTI.
.. Why? Reimplementing RTTI in C will give you almost the same overhead.
> Don’t use C++ runtime wrapper for C runtime includes (<cstdio>, <cmath>, etc.), use C runtime instead (<stdio.h>, <math.h>, etc.)
.. Why? Those wrappers all include the "raw C runtime" under the hood (literally they do #include <stdio.h|xx>. Near 0 compiletime overhead?
> Don’t use stream (<iostream>, <stringstream>, etc.), use printf style functions instead.
This is a design decision.
> Don’t use metaprogramming excessively for academic masturbation. Use it in moderation, only where necessary, and where it reduces code complexity.
There are many programs that are _impossible_ to write in a finite time without metaprogramming. How will you (with zero runtime overhead) dispatch a function with a variable arity of random types to a handler that requires exactly that type of function? Arbitrarily? In C++ it's possible, in C it isn't.
In C, you can use goto to jump over a variable declaration, and you can't in C++. I understand why this is, but it's the thing I see the most often that makes C code not compile as C++.
It was the next step from Turbo Pascal in terms of safety, with added benefits from cross platform.
Nowadays all C compilers that matter are written in C++ anyway.
> A lot of the methods in that document are necessary to make C++ shine, especially template metaprogramming.
So? Is your goal to make C++ shine, or is it to produce useful, understandable code? My goal is good code, not being a showoff.
But, one of the annoying habits of WG21 (the C++ committee) is sending stuff to WG14 (the C committee) to have them make it part of their language rather than accept that it's a C++ problem. Even the stupid type qualifiers are actually C++'s fault, K&R doesn't have this abomination but the pre-standard C++ did so too bad now it's in C89.
And namespaces suck too, so much noise for little gain. You know what, a big part of programming is naming. You just have to come up with good names. Namespaces don't magically make names better, if anything, they make them worse. And they add a lot of syntax noise.
For example, the Windows MFC framework had classes whose data members were all public. Some of reasons were;
1) MFC was a wrapper over lower-level Windows API/structs and therefore unnecessary getter/setter methods were avoided. The C++ class could be a simple wrapper over the underlying C-style POD.
2) The framework classes were supposed to be used by implementation inheritance to build one's skeleton application which led to tight coupling between base and derived classes. Hence the designers decided to make all members public which meant that users were not limited by any omissions in the basic design.
I actually used these ideas in a project where i implemented a C++ api over C state machines for H.323 protocols.
https://news.ycombinator.com/item?id=40445536 (2 years ago, 63 points, 66 comments)
https://news.ycombinator.com/item?id=25554018 (5 years ago, 70 points, 102 comments)
https://news.ycombinator.com/item?id=13751244 (9 years ago, 29 points, 14 comments)
Looks like the page was moved from a GitHub gist to a github.io page in October of last year.
Printf/scanf are implemented as variadic functions without type checking and rely on the compiler to perform its own internal metaprogramming to inspect and warn about format mismatches.
Anyone advocating the use of the old cstdio as a primary design decision about which C++ language features to use is not serious.
No exceptions or RTTI make sense in an embedded system that needs to ensure determinism, but are arbitrary and unnecessarily hobbling for high-level systems and application programming. How do you do runtime method dispatch without creating vtables and RTTI from first principles? How do you propagate a runtime error deep from the bowels of a component all the way to some top-level event loop? The "orthodox" approach would be a mess of integer return codes with associated enums (and none of that enum class nonsense!). No Thanks. It's clear the author has no idea what he's talking about.
> Orthodox C++ (sometimes referred as C+) is minimal subset of C++ that improves C, but avoids all unnecessary things from so called Modern C++. It’s exactly opposite of what Modern C++ suppose to be.
"Modern C++" is usually considered to mean the significant changes to the language in 2011, or 2011 and later. The thing is, that a "small subset improving over C", and without "unnecessary things" will not necessarily avoid 2011-and-later language features, and splurge with pre-2011 features. And this becomes clear as you read the recommendation. So, it's recommand to avoid:
* exceptions
* STL objects which allocate memory
* C++ streams
all C98 features. On the other hand, it's not recommended to avoid constexpr, and it is in fact hinted it is useful.
-----
C++ is a multi-paradigmatic language. It has lots of features, in the language and via the standard library. It is perfectly reasonable and legitimate to pick feautres which are well-tested enough; or well-regarded by, say, embedded or game developers, or doesn't seem too outlandish coming from C. Of course, different people will quibble over what exactly to adopt or discard, but I'm sure that different flavors of "orthodox C++", "sane C++", etc. are in fact used by many groups of developers.
Smart pointers are good. People were doing them outside the standard in the late 90s.
Lambdas are a good feature.
That's the core feature of the language. Not using it doesn't make any sense.
> Don’t use stream (<iostream>, <stringstream>, etc.), use printf style functions instead.
printf is type-unsafe and bug-prone. Also modern C++ standards have better formatting utilities.
> Don’t use anything from STL that allocates memory, unless you don’t care about memory management.
In 99.9% of the time one should not care. Using something like std::vector is perfectly fine.
Overall I find such "Orthodox" C++ harmful. I call it "pure C heresy".
That's exactly how democratic governments make their decisions… you might think it's stupid, and you'd be right, but that's democracy. It's the majority that counts, not what's right. At least you can have a little fun with their arguments, they're pretty inventive you know.
Nim became the obvious choice, and I wasn't a fanboy before. Simple semantics, in a very functional style oriented around data's value. References and identity have to be trapdoored. Everything is single-owner unique lifetimes by default, no annotations or best-practices required. You end up writing extraordinarily functional/procedural code that produces very fast and memory-safe binaries, it fits right into C++'s niche.
The only objection I could steel man was that the standard library and most packages are composed of relatively pure functions that return new values, so allocations are happening there. But when types as complex as data frames can be semantically used as just values, and you know they have scoped lifetimes by default, the benefits are obvious.
With all of C++'s insanely specific, subtle, implicit, compiler- and platform-dependent behaviors, I've often wondered when the industry will finally consider its dominance an artifact of first-mover inertia and simply move on. There are vastly better ways to do all of the things it does, while easily exposing levers for the the things it's considered to do exceptionally well.
The problem of course is that no one agrees on which subset that is.
Hard to take seriously.