Hacker News new | ask | show | jobs
by hackingthenews 2117 days ago
Even as a C++ programmer with too much spare time, it has become obvious that learning and using all of C++ is beyond impractical. To the point where I find that my C++ code more than ever before looks like C.

Nowadays I am using C++ mainly for the high-performance libraries, and only if I can't archive the same in jit-ed python, cython or rust.

The Java strategy of being very conservative about what you add to the frontend would have done C++ a lot of good after C++11 (or even before that).

11 comments

I think my objection along those lines, if I were searching for one, is not with the quality of what is being added but with the fact that it takes effort to keep up with them.

C++11 introduces hugely useful stuff. I would much rather have it than C++03, c++98, etc.

Whenever I come across things introduced in 14 or 17, I usually evaluate it and say, that addition makes sense, maybe it should have been there sooner. But I am not actively seeking out that information. So I am never up to date.

It is a big contrast from a few decades earlier, where you would expect to use very few new features from the standard for a period of many years. That is also the expectation the C standard gives: it hasn't changed substantially since 1999, and even that you could call relatively minor since 1989. But I still think they are doing a good job with modern c++, even if these old expectations are no longer the case.

>The Java strategy of being very conservative about what you add to the frontend would have done C++ a lot of good after C++11 (or even before that).

Java lost a lot of mindshare to C# and Kotlin due to that "strategy".

Just for the HN crowd.

C# still doesn't deliver on all platforms where there is a JVM available, and has yet to even offer something as portable as Swing across all those platforms.

Kotlin really only matters on Android.

In fact right now we are having issues with .NET RFPs, because everything that comes through the door are Java related RFPs.

Not really - Java designers were just plain wrong and it took forever to admit it - things like local type inference (var) made the language pointlessly verbose for ages, and simple features like lambdas made the standard libraries terrible.

There was a 8 year gap where C# users could use stuff like enumerable operators while Java users were stuck in the stone age of writing loops for collection manipulation in a high level language ...

It's a feature, not a bug.

Java is stable, and evolves with a huge amount of thought (they employ theorists to model the impact of changes to the Java language even.)

The benefit of this is a long-term platform, and an excellent implementation. Due to this Java has become the platform where the majority VM research is done, which means Java has many excellent state-of-the-art garbage collectors and a JIT a level beyond that of C#.

Java has a long history of saying “we don’t support that feature because it’s d-a-a-a-angerous.” Personally, I think that’s an insult to Java programmers, but Java programmers don’t seem to take it the same way. They’re happy that somebody at Sun or Oracle is able to keep all the sharp corners away from them.
You know, coming from Java and seeing new languages like Go say the same thing except for features that Java had essentially forever is just maddening.
So...if somebody hands you responsibility for a codebase, you're happier when the language has sharp corners and bleeding edge scars?
>which means Java has many excellent state-of-the-art garbage collectors and a JIT a level beyond that of C#.

Unfortunately this isn't enough to make up for the latency hit from not being able to directly stack allocate things like C# allows (especially with its recent addition of Span<T>).

> make up for the latency hit from not being able to directly stack allocate things

Java does better than direct stack allocation - it does automatic scalar replacement instead.

Java has state of the art garbage collectors because java programs are state of the art garbage generators.

JVM is not the best in all areas.

Java designers don't follow fashion, they rather wait to see what actually works when the dust settles and how to keep those binary only 25 year old jars running on modern JVMs.

I have been working with Java, .NET and C++ since they exist, and C# being more feature rich than Java doesn't help if the libraries or OS support that a customer needs isn't there.

C# doesn't follow fashion either, it does things that actually work before the dust settles on them.

I think you'll find many people outside the HN crowd also appreciate their approach over trying to appease the kinds of customer needs that involve keeping 25 year old binary only jars running.

To me .NET Core is a shining example of that

> over trying to appease the kinds of customer needs that involve keeping 25 year old binary only jars running

C# doesn’t break too many things while it evolves. Example: https://docs.microsoft.com/en-us/dotnet/api/system.collectio... That class was introduced in .NET 1.1 way before generic, still present in the most recent .NET Core 3.1.

They sometimes deprecate higher-level stuff, but very conservatively so. E.g. asp.net web forms (2002) is deprecated, while windows forms (also 2002) is still supported.

.NET Core has been so good managed, that I a project back in the .NET Core 2.0 days to port an application to Java, because the customer wanted to move the application to UNIX (not just Linux flavours), and not all necessary features could be done in Core.

To this day there are plenty of libraries that are yet to run on either Core or outside UNIX.

Plus not everyone is happy how the whole Core, .NET 5, .NET Native, Reunion, UWP, CoreRT, Xamarin, MAUI, Blazor is being managed.

A language alone isn't enough.

C# has been moving much more quickly since then, async/await comes to mind as a feature that didn't work out
I've never had anything but problems trying to run Java apps. Maybe in server land it's been great but in desktop land, outside of Minecraft, it's always been hell for me.
> "Java designers don't follow fashion, they rather wait to see what actually works when the dust settles and how to keep those binary only 25 year old jars running on modern JVMs."

Yup, for better or for worse, Java will be the COBOL of the 21st century.

Apparently 21st century developers don't have much issues dealing with UNIX and C, just about 10 years younger than COBOL, speaking of which, both about 30 years older than Java.
> local type inference (var) made the language pointlessly verbose for ages

I guess that this is a question of taste.

What for you is "less verbose" for me is more confusing to read. I like to see the types as they complement variable naming. To avoid typing a few letters the code will for ever require me to double check the types with help of the IDE.

I have worked in medium sized corporate companies. The code base is quite big and one of the 20+ development teams may get transferred a project from another team (does not happens super-often, but it happens) or they may create pull-requests for bug fixes (this is more common).

Clear and easy to understand code is life saving. In one of the companies I worked for a Javascript team send a -1 instead of a "-1" the cost ramped up the hundreds of thousands of dollars and our clients were not happy about it. Rollback mechanisms were used as fast as our clients detected revenue problems on their own customers.

And the tests did not got the error as the values is used at the integration layer between our clients and us.

I see safety an increasing value as our programs control more and more money and more and more services. And, I have to admit, I feel more comfortable with more verbose code.

You just need to use var properly. I.E. only when you can tell the type from the rvalue.

var a = new ArrayList<T>();

not:

var a = o.getThings();

> You just need to use var properly.

That is a very good point. I find the diamond operator more of my taste as it requires you to think if you want to expose ArrayList or List.

List<String> list = new ArrayList();

But, as you point, the second example removes information that will require to spend time to check types each time that someone reads the code. I do not like that.

I use auto-complete and I type quite fast, so, I do not see to write code as a problem. I spend most of my time understanding the functional needs, looking for better patterns or algorithms to implement performance-critical sections or finding names for exposed APIs that state clearly its function when I "write code". But, most of the time, I am just reading my old or other people's code to just change a few lines or decide on a local refactoring.

^This

Here, the first is much less verbose, but still clear:

    var x = new Dictionary<string, MyComplexType>();
    Dictionary<string, MyComplexType> x = new Dictionary<string, MyComplexType>();
But here, it’s ambiguous what the type of `x` is without Intellisense:

    var x = y.GetStuff();
Did you spend any significant time working with C#?
> Did you spend any significant time working with C#?

I have no experience with it at the corporate level. There, I have seen, it mixes a lot with .NET. So, I guess that the corporate equivalent to Java is "C#/.NET".

I spend some time several years ago with C# in Unity3D, thou.

I really liked the C# language. I found the Auto-Implemented Properties a neat compromise between encapsulation and verbosity (Verbosity has no value if does not add information).

Java is trying to be everything to everyone and that is a mistake. I liked Java more in the past, and I would have added just a few things from the past iterations of the language (e.g. Modules is a good idea that actually simplifies the language and moves much code to "frameworks" instead of being part of the core language).

C# seemed more focused on its initial style were Java is stretching all over the place.

Pointless typing and verbose code has nothing to do with writing code - it's all about code being readable.

Java type declarations can be 20+ characers - just scanning through the code and having to skip all that junk makes my eyes more tired reading through. Types are implicitly deducible when you know the codebase 90% of the time (and should be added when they are not), and if you don't know the context you will be slow no matter what.

> when you know the codebase

Yes. When I was younger I worked in solo projects. I knew my code almost line by line.

In my last decade, in middle sized companies, nobody knows all the hundreds of micro-services code. And code changes while on vacation, that can be 6 weeks of the team working without you. That is not ideal, but in such a big code base it is difficult to have everyone reviewing all the changes on a single micro-service, impossible to have all 20+ teams reviewing all of each others code.

Different problems need different solutions and code styles, I guess.

Even then they fumbled the execution. Why on earth would they make streams without support for native types?
Yes, but that seems like a clunky add-on. Something as basic as iterating the characters in a string should not require special constructs like this IMO.
Swing is a horror show from both a developer and an end-user perspective, don't know why you would even mention that. Windows desktop apps based on Forms or WPF are in another league both in terms of ease of development and user experience.
Swing is only an horror show for those that don't bother to read books like "Filthy Rich Clients", or don't want to spend money on a proper design team.

Why do I mention? Because until now .NET Core still doesn't have anything like Swing across all supported platforms.

MAUI might do it (Xamarin renamed), or you might just get Blazor running on WebWidgets.

I rather have Swing if the alternative is running Blazor that way.

Not counting community toolkits here, just what is available out of the box.

I worked a bit for a project that tries to make Swing Application to touch friendly. There's almost zero support for touchscreen.
So C# definitely benefited with the second mover advantage. It learned a bunch of lessons from Java such as:

- No checked exceptions

- Properties (I wish Java would add this to the core language instead of relying on things like Lombok as it shouldn't change the IR and really is just syntactic sugar)

- Partial classes. This really isn't in contrast to Java because Java has nothing like it. But it is a neat feature for partial code generation;

- LINQ. Java eventually added streams but last I checked it still had performance issues and IMHO LINQ is just cleaner;

- Conditional compilation. This is really a huge oversight. One of the huge benefits of the preprocessor in C/C++ was conditional compilation. It's great than C# included it. It's bizarre to me that Java hasn't.

- Async/await. Honestly I still find C#'s version of this more awkward than Hack's. Experience has taught me that whenever you spawn a thread, you've probably made a mistake as subtle mutlithreading bugs are the devil and cooperative multitasking like you have in Go, C# and Hack is usually far safer and sufficient most of the time. Still, C# is still better than Java here.

- C#'s reified generics vs Java's type erasure. I think it was the right decision to break backwards compatibility here (and I don't usually say this). This was pretty early too (IIRC generics were added in C# 2.0).

But all that being said, I still think Java has a large mindshare and install base than C# by a mile. it's not sexy so it gets less attention on HN but Java is still massive.

As for Kotlin? Much like Scala I see this as nothing more than a curiosity. Android developers seem to like it but I think it's a tiny fraction of Java still.

“One of the huge benefits of the preprocessor in C/C++ was conditional compilation. It's great than C# included it. It's bizarre to me that Java hasn't.”

The C preprocessor can make it way to easy to break code, for example when it is used for feature flags and/or multi-platform support. If you have N feature flags, you have to compile 2^N different programs. Multiply by M for supporting M platforms.

It also makes it impossible to check whether source code can be compiled. The text inside a

  #ifdef FOO
    ...
  #endif
block doesn’t have to be valid source code, but the compiler cannot know whether it has to be. I think that’s why Java ditched it, and I think that makes sense. Adding a more limited feature, like C# did, makes sense, too, though. I’m not sure C#’s variant is limited enough, though. it still is subject to that 2^N problem, but gets saved from its main problems because C# isn’t running on as diverse environments as where lots of C code evolved.
But the 2^N problem exists no matter you like it or not. Except now it's done at runtime with dependency injection instead of compile time.
That’s true, but it is done a lot cleaner than in the wild west days of the C preprocessor, where feature-specific, compiler-specific, and platform-specific #ifdef’s were sprinkled throughout the source code seemingly without much thought (but keeping things working must have taken lots of thought), nested #ifdef’s were common, and often not all cases had separate paths in the code.

Dependency injection can be made messy, too, but that takes more of an effort. You can also test injected code in isolation. That may take some effort, but those preprocessor messes only could be tested as part of the entire product.

As I said, I can see why they wanted to get rid of that. Not adding a same replacement may not have been the best choice, but I am not sure of that. Programming culture also had to change, and that sometimes requires drastic action. Apple also did that in the Mac by not providing any text mode (forces programmers to make windowed applications) and by removing cursor keys from the first keyboard (forces programmers to provide a good mouse interface)

LINQ is cleaner, but I find streams to be more elegant when considering the language as a whole (its just a pure library - no new syntax/rules/etc to learn).

I feel like a lot of languages these days trend towards "kitchen sink" languages that toss in everything and the kitchen sink in the name of clean looking code. IMHO this tends to sacrifice language elegance. This is probably why I tend to like languages like Java/Go/Clojure.

There is not new syntax for LINQ either -- it's just fluent-style via extension methods. I don't know many people that use the SQL-like syntax any more -- and it only covers a tiny amount of the functionality.
Honestly I've forgotten sql-style LINQ exists. Method-style is common.
> As for Kotlin? Much like Scala I see this as nothing more than a curiosity. Android developers seem to like it but I think it's a tiny fraction of Java still.

Java is massive for legacy reasons, but would be nice to have some statistics regarding new projects. The last two companies I've worked for had backend teams using Kotlin so I've been assuming this is also preferred by backend people. Could well be that this has was an anomaly though.

I mostly develop for Android so my own perception is obviously biased, as Java is pretty thoroughly erased from this world now.

Easy, JVM == UNIX == Browser, Java == C == JavaScript.

Anything else on the JVM is nice to have, but isn't where all the goodies are.

Google has a special interest in getting rid of Java, hence Kotlin.

In fact, it is going to be fun to watch how they will keep up with the pace of the JVM, because when the majority of key libraries move to modern Java, many of them will stop being compatible with D8/R8, forcing Android developers to use pure Kotlin libraries.

Then JetBrains needs to think how to make use of Java libraries that use the new FFI, virtual threads, inline classes, generics, SIMD vector types, while keeping the language compatible with what ART is capable of.

And now Java is benifiting from being a second mover on a lot of the features C# adopted.

See virtual threads vs async/await. Java's version is going to be better.

I always end up with a couple of Task.Run() calls, because cannot change the complete call chain to deal with async/await.
If you want to avoid changing the complete chain to Task, there’re workarounds.

You can block the caller thread waiting for the task. Deadlocks are possible with this approach due to synchronization context shenanigans, but they’re easy to fix, VS debugger is quite good at multithreading.

Another method, you can run whatever dispatcher on your main thread, in modern .NET that’s usually Dispatcher.PushFrame.

Also, if the async method returns no values, you don’t need to change the call chain, you just need to catch and handle exceptions.

Even that situation, you can still use await inside Task.Run, it should be useful than nothing.
Some circles rely on Lombok, I never used it nor plan to.

I rely miss checked exceptions on .NET, specially with libraries that provide zero documentation about their exceptions.

I've never seen the allure of lombok.

Most things shouldn't even have getters and setters apart from your @Entity classes.

But that is not inherently a bad thing.

If we look at programming languages as tools, it makes sense for them to get out of date and new ones taking their place, with all the lessons learned.

So its possible that programming languages like C++ that keeps extending their own life through adding features (while keeping weaknesses), will ultimately cost the community/industry more in the long run.

I'm not saying Java should have introduced ground breaking features - I'm saying they made design mistakes that they should have corrected far sooner (var/type inference) and refused to add some basic features (lambdas) that would have made the code a lot better for it.

They did add those features eventually (Java 8) - about 8 years behind C# (since C# 3.0)

>If we look at programming languages as tools, it makes sense for them to get out of date and new ones taking their place, with all the lessons learned.

Part of me thinks that this is how it should be. Extending a language constantly while providing backwards compatibility can lead to some awkward syntax too.

Agreed.

Just gonna drop this here: Nicolai Josuttis “The Nightmare of Initialization in C++” [0]

[0] https://youtu.be/7DTlWPgX6zs

The thing is that this is not a huge issue in practice. I stick to uniform initialization in 99% of cases and it works out pretty well. I have far fewer bugs from initialization screwups than from other things.
But what kind of an effect does this have on someone new learning the language? It seems to me like it would make it slightly harder to learn and get used to.
It seems to me that for many developers, all they want from a language is more and more features. I’m grateful for languages like Go and Lua that try to buck this trend.
Yeah, it is so liberating to write boilerplate libraries.
I think Java took it too far. If they'd innovated at even 1/2 the rate of C#, I think they wouldn't have lost as much mind share.

But of course, I'm sure Oracle is a big reason too.

Oracle is all the hate, but when it comes to java, Oracle really got the show rolling again. Prior to Oracle, little happened. After Oracle, a lot is happening.
Oracle haters seem to disregard that Oracle was the only company bothering to actually make an offer and keep it, without that offer, everyone would be busy maintaining Java 6 codebases, or porting code to something else.
IBM?
IBM withdrew their acquisition offer shortly thereafter.
> due to that "strategy"

People put scare quotes in seemingly at random these days - are you suggesting it's possibly not their strategy and actually they're lying? What do these scare quotes mean?

They are saying is not a very strategic strategy
C++ looks more like a union of many other languages than anything else I know of. Forget the old joke of mimicking Common Lisp, C++ is a hacky implementation of all other programming language.

It is quite marvellous in a way, almost a statement of "we don't want design, we just want something that compiles". It has worked out remarkably well in practice.

The notion of using C++ like "as C but with namespaces, std::vector, string and map" is way underrated.
When you add those to the mix you're dealing with destructors, ownership, etc. so I don't see anything wrong with using stuff like unique_ptr/shared_ptr.

And C tends to reinvent object oriented programming with each library so you might as well use language defined classes.

So I don't really see the point.

You don't need to use stuff like concepts, or "showing off how smart I can be with template libraries" (looking at boost), but C++ has a lot of features that make it much more productive than C.

Yeah that's basically how I use it for embedded programming c with classes, raii, better strings, and smart pointers :) . I generally avoid rolling my own pointers and anything much fancier that the simpler algorithms/sorts/etc
That’s your choice of C++ subset, but the problem with these huge languages is that everyone chooses a different subset to use.

Sticking to plain C has the advantage of probably being the subset of C++ (I know, it’s not technically a subset) that’s in widest use.

I’d add that deciding on the subset is a high cost operation. Design of such a smaller subset-language will likely not be on par with language that is small by design.
> everyone chooses a different subset to use

I prefer using C++ like "C but with cin and cout" and I informally call it C+ :)

> (I know, it’s not technically a subset)

Is it not? I thought C++ was explicitly a strict superset of C?

It is not and never was.

Look at for ex. what const means in each, or auto. Or variable length arrays (feature that doesn't exit in C++). Or designated initializers that C++ didn't have for a long time.

Here is a good list of differences: https://mcla.ug/blog/cpp-is-not-a-superset-of-c.html

C++ is almost a perfect superset of old C before C99. There are odd corner cases where prograns are parsed differently, but these are quite contrived. The 3rd edition of The C++ Programming language lists a few cases. Since then, C has had may additions to the language that made it diverge from C++. Your linked article is almost entirely about these new C additions.
Even for the old C it wasn't a superset.

There are plenty of legal C programs that aren't legal C++ - if for nothing else, then because C++ has more reserved keywords which are perfectly legal identifier names in C.

E.g. this is legal C but not legal C++:

int template = 10;

Syntactically, it almost was; and even this is no longer true today with C11 and later,

More importantly, though - the syntax is not what matters. The languages are very very different in idiomatic use. You just don't write programs the same way with C and with C++. And the gulf between the two only expands with time. A decent C program is almost certainly a poor C++ program, in terms of idioms, utilization of library facilities, and sometimes even in terms of performance (!).

I think the niche of "more than C, but not C++" is going to be eaten by Rust. It's like C with modules, Vec, String, HashMap, and one standard sensible cross-platform build system.
Rust has plenty of advanced language features that can entertain the same kind of people who enjoy writing C++ template metaprogramming as a brain teaser.
In practice Go is making inroads in that area. Since Rust has similar complexity to C++ it's not really comparable to C or Go.
Go is a nice language growing in popularity, but it's not a C replacement.

Garbage collection, big runtime, and non-zero-cost FFI are acceptable trade-offs in many areas of programming, but they are deal-breakers for domains where C (or Rust) is necessary.

Rust is somewhat similar to C++ in the feature set, but not in complexity. Rust's features are more orthogonal, without duplication and backwards-compat warts, and you get hand-holding by the compiler.

C is "misused" for a lot of things and Go is a contender in those areas though.

Technically you could be right about complexity vs. feature set, but from the POV of someone that's using a simple language, the two don't look that different. One does help you avoid certain errors, although whether that counts as hand holding or hand slapping is in the eye of the beholder.

I don't know. Rust is still pretty complicated. I'm hoping Zig will have success here as a better C.
It might, but Rust still needs to grow a matching eco-system.
And templates, and standard algorithms, and move semantics, and lots of other things that are pretty much necessary in modern code. Well, not necessary, but by restricting yourself from using them you are needlessly:

* creating more work for yourself, and

* introducing bugs in your code.

Don't do that. Use C++ as C++.

If I want all the advantages of that stuff I'll just use rust.
Agreed. The bigger C++ gets, the more important it becomes to treat it as "C with some cool extras" and to consider any and all new features as optional extras that you only use if you legitimately need them. The moment you start seeing "C with classes" style as a bad thing, you will slowly wander off into the wilderness.
The vast majority of the real world code a C++ programmer writes day to day is just that.
Why does this post need to appear any time C++ is a topic? Do you learn and use all of any language you use?
Because there is this urban myth that other languages aren't as complex, yet drop someone with a beginners loved language like Python 3.8 and they will have fun throughout the almost 2000 pages of documentation plus all major libraries in use across the ecosystem, and Python implementations.
> yet drop someone with a beginners loved language like Python 3.8 and they will have fun throughout the almost 2000 pages of documentation plus all major libraries in use across the ecosystem, and Python implementations.

I would agree that the Python ecosystem is as complex as the C++ language.

I would not agree that the Python language is as complex as the C++ language, or that they Python ecosystem is as complex as the C++ ecosystem.

I will agree also that the Python language has very comprehensive first-party tutorial and reference documentation, but that doesn't add to the complexity.

Then it is time to learn some clever use of Python metaprogramming, metaclasses, how slots interact with old style attributes, old and new style classes, clever tricks with magical methods, stackless vs regular Python, multiple ways to format strings, arithmetic semantic changes,...
I was aware of all those things when I made the original statement, thank you.
> old style attributes, old and new style classes

pretty sure that's only a factor if you need to deal with Python 2

I expect any Developer X, to comfortably understand a random codebase of language X.
I think the two major c++ documentation websites are far better than the official Python documentation. They have more examples, the different variants are clear across which version of the language you are using, and I never need to go elsewhere. Python documentation is typically lacking many of the examples you'd really want, and you need to go elsewhere to find those.
Are you seriously arguing Python has about the same complexity as C++?

An extensive standard library is not the same at all as an large base language size. Adding a million books to a language doesn't make it more complex, adding a million words and verb conjugations does.

Yes I am, Python even changes stuff between minor releases.

Also I think many aren't aware how powerful Python actually is.

Totally agree. Look at something like asyncio and how many things have been improved or deprecated since 3.5. it makes it really hard to keep up. Sure, if you want to stick to basic python it's certainly easier to learn than C++, but the language as a whole isn't much simpler
I have spent quite a bit of time invstigating the metaprogramming capabilities of python and I have barely scratched the surface. And I hear they have added a fully parametric type system recently...
It's parametric, sure.

It's not "full" in that it can't express the types of many language features well enough that inference works. Something like the attrs package needs to use a kludge[1] to communicate types back to a validator.

https://github.com/python-attrs/attrs/blob/4f74fbaca3cc12911...

Other languages are definitely less complex — you generally don’t see language lawyering for Python or Java — but I think that there are many useful features in C++14/17/20 as well. Writing only C++11 will lead to suboptimal code.
> you generally don’t see language lawyering for Python or Java

But my experience for instance when having to do stuff in C#, Java or Python is that when I'm looking for advice on the internet, stackoverflow, etc etc... 80% is reaaalllly bad, in the sense that if it were C++ everybody would be screaming "wtf don't do that this is insane" and write 12 blog posts and 40 twitter flamewars about why this is a bad idea and you should never ever do this... but apparently the "tolerance to bad code" is much higher in other communities.

One way Herb Sutter looks at it is — most advanced programmers could probably write a Python or a Java interpreter. It might take them a few years, and they might struggle with some complex language features like metaclasses, reflection, or the type system, but the language spec is understandable enough that it’s feasible.

But give them five years and they would generally not be able to write a C++ compiler.

This is a side effect of those languages being much more accessible to beginners and more widely used than C++.

You won't find that as much for for Haskell or Clojure or Brainfuck but that doesn't make them better languages or less tolerant to bad code.

Being more accessible means being usable by less trained/skilled developers, which makes online communities less trained/skilled on average. I wouldn't use that as an argument to prefer to use worse tools and their built-in 'you must be this tall the ride' nature.
The problem is not just complexity, but complexity with tons of pitfalls.
There are plenty of opportunities for a Python Puzzles quiz.
Kind of ironic that I would agree with you. The reason I still wrote it is that my opinion just recently changed from liking C++ but understanding the pitfalls to a more pragmatic this is not worth the time or effort.
> Do you learn and use all of any language you use?

Generally, yes for Python (definitely for 2, async shook up 3 a bit), C (barring a few genuinely legacy things like trigraphs), Go, Lua, and I think probably at least 2-3 times a year for anything in Java 12 and earlier.

C++ is the only language I use regularly that I am certain I don't even know of, let alone know or use, all the features of.

> Do you learn and use all of any language you use? yes for Python

Have you really used all of these Python features (from https://twitter.com/raymondh/status/1280988785841278976)?

__class_getitem__, __init_subclass__, __prepare__, __qualname__, __slots__, __spec__, __annotations__

__subclasshook__, __abstractmethods__, __text_signature__, __index__, __loader__, __mro__

I've been writing Python for years, but I've never even heard of half of them.

Because they are extension points that all have sensible defaults (in most cases, "nothing to see here") but are readily available when one needs to write something magical, not because there's unnecessary complexity in the language.

All Python-like languages have the same or similar mechanisms (for example, a method resolution order whenever there is multiple inheritance) and they are usually not explicit and not customizable.

But the common argument against C++ is that you need to know a large portion of the language because someone somewhere might be using it, and you may have to read and understand their code. So even things that are optional in python should count against python, just like they do in C++.
The difference is that C++ features very much interact with one another in subtle and detrimental ways, so if you are using feature A and a codebase you leverage uses feature B, the interaction of the two can genuinely cause issues. For Python, the extension points are generally hooks into very explicit operations (and some of the symbols you list aren't even extension points).

Hell, the very defaults of C++ will interact badly with other features and proceed to shoot you in the foot, as Chrome somewhat recently reminded us (https://chromium-review.googlesource.com/c/chromium/src/+/16...).

You need an 8x6 matrix just to know what the compiler will or won't generate (https://i.stack.imgur.com/b2VBV.png) and then you need to know exactly what the defaults will do and whether that breaks your object entirely and you need to override those defaults.

There is a big difference between "optional because I don't need do to the thing in 99% of projects" and "optional because I can use one of three alternate features with subtle incompatibilities to do the thing."
The only one I had not heard of before is a relatively recent addition, __class_getitem__. To Python's credit, I immediately (and correctly) guessed what it did from the name. I have used over half of them - if you have even written anything that needs to show docstring/typing information or a plugin loader you already cover nearly of them. I've used __index__, __slots__, and __mro__ more times than I can remember. The ones I haven't used are mostly because they are Python 3.4+.

But, critically, you won't find anything other than __slots__ and __index__ (and maybe __subclasshook__ if the code you're working on was from someone who drank the ABC koolaid back in the day) in "normal code" - they're there for people who want to hook into language internals, the kind of thing you'd usually find #pragma'd in C++.

With C++, on the other hand, you run into this kind of complexity just trying to solve "ordinary" problems. And it bears repeating, the "safe subset" between projects is different. It's not the only language with this problem (Common Lisp suffers greatly from it), but it's the one that offers the least in return and breaks the hardest when you get it wrong.

> C++ is the only language I use regularly that I am certain I don't even know of, let alone know or use, all the features of.

I'm still holding out some hope that Stroustrop releases a new edition of "The C++ Programming Language" with the features from C++14 onward.

> it has become obvious that learning and using all of C++ is beyond impractical.

Doesn’t really make sense why you’d want to anyway. Why would you want to use all of any language? C++ offers so many features because of its extraordinary flexibility and application to a wide range of domains. Unless you’re writing an app that is a game that also trades high frequency transactions on the financial exchange, while performing physics simulation and 2-D vector rendering, all while serving up a Web server, it would not make sense why you need to use all of the language. It’s perfectly fine to find those parts of a language that you are personally interested in.

I find I need to learn it all because I write code that interacts with other code. I find myself reading example code that uses advanced C++ constructs, or sleuthing though a 3rd party library to understand it. Every time I find a library with a "modern C++" take on a problem (json parsing, etc.) I know I am going to have to dust off my knowledge and even learn something to make use of it. Very high cost. It is just a tough ecosystem to exist in as somebody who isn't 100% c++.

I remember starting out with Python and only knowing a subset of it, but as soon as I started writing real apps that interacted with the larger Python ecosystem, I found myself having to learn the full language (or at least a lot more than I did writing my own isolated code).

All the template SNIFAE stuff hidden in highly generic libraries for is going to become quite simplified with C++ 20 concepts. Also, to be honest, it wasn't _all_ that complicated as long as you kept a few patterns in mind.
The set of features a language provides is part of an unwritten contract:

We write our code such that it minimizes the amount of effort to read it, under the assumption the reader knows the language.

This way, it makes sense not to use a library for something that can already be cleanly expressed by the language because this introduces additional mental overhead for the reader.

Yet is doesn't make sense to avoid modern, simplifying language constructions, as we can assume these are known by the reader.

This unwritten contract is jeopardized by an overload of language features, as suddenly we may want to avoid certain features considered complicated. Hence, the notion of optimal code becomes more subjective.

It becomes a problem when using code written by other people. Since everybody is using a different subset of C++, a project with external dependencies quickly becomes a hodgepodge of different idioms, coding practices and language subsets and as a result becomes harder to maintain. Some of the language features are incompatible with others (e.g. using exceptions for error handling vs disabling exceptions for performance).
Pretty close to none of the things added to C++ over the years are specific to certain kinds of problems. It's not like you can't write a game/hft/physics/svg rendering perfectly well in C.
All of the work on compile time programming in the last few versions is definitely for performance-oriented domains (certainly others may choose to take advantage of it as well). If you don’t need that kind of performance or don’t need to have complicated logic run at compile time to save on those runtime costs, those features may not be so necessary for you.

And there are definitely things that these features provide that are impossible in C.

C++ is not a "language for all things". It's a tradeoff of features, and that's ok.

However, the idea that you should learn and use "all of C++" is a mistake to begin with. It's something that sort-of happens with C, but doesn't really make sense with larger languages.

Specifically, remember that C++ is _multi-paradigmatic_ - you can write more object-oriented code, functional code, highly templated generic imperative code, or, well, C-style code. The standard library also now has something for everyone, and there's no reason to know _all_ of it.

On the other hand, many things which used to be difficult to write and required some deep "voodoo" or a lot of boilerplate are easier and more straightforward these days.

> learning and using all of C++ is beyond impractical

I agree in general, but still, some of the newly introduced features are actually awesome. Here’s ones I’m using almost every day.

C++/11: initializer lists, range-based for loops, strongly-typed scoped enums, raw and UTF8 string literals, thread_local, thread synchronization classes including atomics, alignas

C++/14: binary literals

C++/17: if constexpr, std::string_view, better static_assert

And in C++/20, pretty sure I gonna use std::span and std::bit_cast.

In C++ we don’t pay for features we don’t use. I’m totally fine with other people getting the features they want (despite finding them unreadable and overcomplicated), I simply don’t use the stuff I don’t like.

My objection is lot of features are being added as library features in ad-hoc ways. The old "if it can be library, it should be library" fallacy.

The classic example is C++ ranges' pipe operator. If it was implemented in language, like OCaml and Elixir do - There would have been no need to write range adapters - the error messages and debugging could be much better than 100000 line template implementation. - debug build would be fast as well - compile times would not suffer

Implemet-it-as-library is often a leaky, brittle abstraction. That's the reason LISP, with its proclaimed magic capabilities, didn't take off.

The C++ committee seems quite academic at moment. Last time I read C++ performance report, all I could find was some old advice like "use list if you want insertion"™. They didn't mention difficulties in using realloc in vectors. They didn't mention inefficiencies in stl HashMap implementations due to iterator invalidation constraints. They didn't mention exceptions requiring dynamic storage and RTTI. Note that many of these problems are not yet fully solved with move constructors.

At the same time they are also less academic than required. Given how they managed to put pipelines into an ugly template library without any of the considerations I mentioned above.

I can't help but I feel similarly towards all standards committees. The JS standards people are still unable to deliver a standard library, and a "use strict"-like flag that enables easy tree-shaking.

If anyone here is in C++ standard committee, you owe us an answer.

> Last time I read C++ performance report, all I could find was some old advice like "use list if you want insertion"™. They didn't mention difficulties in using realloc in vectors. They didn't mention inefficiencies in stl HashMap implementations due to iterator invalidation constraints. They didn't mention exceptions requiring dynamic storage and RTTI. Note that many of these problems are not yet fully solved with move constructors.

The TR18015 (Technical Report on C++ Performance) goes into details on most of the above (and much more) and it was written in 2006. The only thing missing is a discussion of hash_map as it wasn't part of the standard yet.

>Nowadays I am using C++ mainly for the high-performance libraries, and only if I can't archive the same in jit-ed python, cython or rust.

Is anyone aware of a library for another language that's as fast as Eigen?

This is a hard comparison because pretty much all of these libraries call a BLAS implementation under the hood, so what you might be comparing is actually the cost of stuffing the array with your data in each language (which is obviously task specific).

I think it'd be difficult to argue one way or another: Eigen gives you flexibility one way (custom kernels) and Python gives you another (interactive debugging and easy visualization). Cython/Numba make it even more muddied.

I can definitely takes the cake for an easy to use syntax for doing all kinds of linear algebra. It's only able to do that because the extensive operator overloading that C++ allows. However, don't want that for you for a speed. Using something like MKL directly is always going to be faster than eigen. The trade-off is the API is much more complex.
Drop someone in front of C# 9, Java 15, Python 3.8, Typescript 4.0, Ada 2012… and watch how they manage.
Java 15?! I hardly realised they'd moved past 8.
New version bumps after 8 are not necessarily major.
Yep, some people love their outdated GC algorithms and JIT implementations.
(I think all they meant was "non-breaking" not "non-useful.")