I can't help but wonder if developers are simply off-put by the complexity of the language itself.
Way back in 1989 I was fortunate enough to get Borland's C/C++ compiler, the K&R C book, and Stroustrup's C++ book - which, at the time, was about the same size as the K&R C book. The C++ language was relatively simple to learn at that time: no STL, no odd pointer types, no template metaprogramming, etc.
Fast-forward to 1997 and I'm listening to a talk about C++ compilers and hear that templates are now a Turing-complete language, and how the compiler developers used the template language to coerce the compiler into printing out the prime numbers.
Fast forward again to 2022 (with trips to Java, Kotlin, Scala, and Haskell in between) and I've got a project that requires JNI and Python bindings to a core C engine. Great, I'll use C++! I proceed to start searching for containers/constructs and realize that at least 50% of the C++ language and standard library is unreadable to me. I only have a guess as to what a `shared_ptr` or `unique_ptr` type is, and I certainly don't know when to use them, and concepts/traits outlined in the STL documentation seem to be some sort of generalizations that the STL might provide default implementations for, but might not? What exactly is the syntax and/or type of a lambda function and how do they interact with C function pointers? etc., etc.
I get the sense that C++'s evolution has been driven by esoteric corner cases that a handful of developers have encountered and the language & STL design has been driven by those cases. I'm still surprised by how many problems I can easily address in something like Scala or Haskell that C++ simply does not provide the facilities to express easily.
> I can't help but wonder if developers are simply off-put by the complexity of the language itself.
I certainly fall into this category. At one point I could have almost been considered a C++ language lawyer.
But then two things happened:
(1) I got stuck working on codebases that assumed C++11. Even now, I'm using Python3 and C++ (mostly 11, with a little 14/17 sprinkled in).
(2) The C++ language spec got significantly more complex after C++11. Without writing C++14/17 code on a regular basis, I just couldn't justify the time spent trying to keep up with it.
So for me, the cost/benefit ratio of trying to stay "current" with C++ is no longer worth it. I expect to prefer Rust over C++ for new projects.
> I only have a guess as to what a `shared_ptr` or `unique_ptr` type is, and I certainly don't know when to use them, and concepts/traits outlined in the STL documentation seem to be some sort of generalizations that the STL might provide default implementations for, but might not? What exactly is the syntax and/or type of a lambda function and how do they interact with C function pointers? etc., etc.
And that's exactly my point though! `shared_ptr` and `unique_ptr` solve (or, at least, simplify) a lot of the memory management problems endemic to all C code and old C++ code. And they've been around for over a decade now.
I would absolutely be willing to spend some time with you to teach you what new things are, what they do (and what problems they solve), and how to use them without introducing new problems. I think several one-on-one teaching sessions would help you a lot with that. But one-on-one teaches you and not the thousands of other old-experienced C++ developers also in your shoes.
> I get the sense that C++'s evolution has been driven by esoteric corner cases that a handful of developers have encountered and the language & STL design has been driven by those cases.
While some of the C++ evolution fits that category... I would also add that a lot of changes in modern C++17, C++20, or C++23 are aimed directly to the masses of C++ developers doing "everyday" work. shared_ptr and unique_ptr are definitely not esoteric corner cases: they're driven to specifically solve memory management problems that have directly contributed to some very significant CVEs in many thousands of products.
A series of expert-level blog articles guided by your experiences teaching a pre-2010 C++ guru would probably be well received by other such "ex-gurus." They would need to be definitive, detailed, verbose and bring the reader up to C++ lawyer in the relevant new feature to be interesting.
A trip through the graphics pipeline has the correct level of detail:
I'm afraid I haven't read any C++ books. I'm sure that's where some of my strong biases come in; I'm all self-taught with some cppcon videos and attendance sprinkled in for topics that I know I'm weak in.
I can recommend a Slack [0] or Discord [1] though. There are plenty of other C++ people who can make such recommendations.
C++ has fantastic docs, books, videos, so much. You shouldn't need to read the std library code, and shouldn't read it to know what is safe to do. There are many implementations and they differ in details but all have really good standards compliance, except where they document deviation (like EASTL omits some slow stuff).
If you want to iterate over a container, know promises about algorithmic complexity, have strong guarantees about type safety, or know what smart pointers promise to do then you can get all that without digging into the stdlib's code.
I know this wasn't the main point of your comment but shared_ptr can be thought of as a reference counted pointer. It cleans up the pointed to object when all the pointers to that object go away. It doesn't need to be reference counted, there are implementations that do goofy ring lists under the hood but all the operations on it are cheap O(1) operations and it is only slightly slower than a raw pointer, looking at the code might cause someone to miss the promises of computation complexity. For object you only want one of there is unique_ptr. You give it a pointer or constructor to make an object and when that pointer leaves scope it cleans up the object. Both are great for managing things like memory, connections, file handles, anything you want automatically cleaned up when the pointers leave scope.
Not all languages have the robustness that comes with a 40 year history, so learning last year's hotness and C++ are going to be different.
I'm with you mostly, although as the mostly sole maintainer of an 22 years old and still evolving C++ codebase (among some other responsibilities) I do try to keep up with the language. But if I didn't feel this was necessary for my job I probably wouldn't do it or at least won't sink too much time into it.
I've also programmed in Haskell and I've had the same reaction as you, as to the things I can easily express there and not in C++. However I think the 2 cultures (Haskell and C++) are similar in that they both attract people who like to produce clever code which is unreadable to other people, even though the base language is not that complicated.
As for your questions:
1) shared_ptr / unique_ptr: Personally I liked this presentation. Watch it on x2 speed until it gets to parts that are difficult for you.
This is more dense than the previous one. As for your question "how do lambdas interact with C function pointers": rough explanation is they are not the same, since they can possibly allocate memory if they capture. But if they don't capture anything they are just like a (static) function, and can be converted to a function pointer.
As for concepts / traits, rough answer is that you don't need to know about them for everyday work, unless you write a library / API for consumption by other C++ developers. In that case, they help the compiler provide better/shorter error messages in case template functions/classes aren't used properly.
You can do almost anything in C++ at least three different ways. Most places that care about engineering will not have you do abusive template metaprogramming or go crazy with macros, even though it’s possible to do in the language.
The std library is filled with bad implementations that can’t change because of backwards compatibility. So it’s not your fault that they look confusing. The pointer types you mentioned are actually quite simple, think of them as thin wrappers over raw ptrs with constructors/destructors that implement counting (for shared ptrs) and whose destructors do the delete operation (for unique ptrs, and for shared ptrs once they reach 0 copies).
Working with C++ can be easy and fun as long as you aren’t in a codebase where people went overboard with anti patterns or “cleverness”.
Way back in 1989 I was fortunate enough to get Borland's C/C++ compiler, the K&R C book, and Stroustrup's C++ book - which, at the time, was about the same size as the K&R C book. The C++ language was relatively simple to learn at that time: no STL, no odd pointer types, no template metaprogramming, etc.
Fast-forward to 1997 and I'm listening to a talk about C++ compilers and hear that templates are now a Turing-complete language, and how the compiler developers used the template language to coerce the compiler into printing out the prime numbers.
Fast forward again to 2022 (with trips to Java, Kotlin, Scala, and Haskell in between) and I've got a project that requires JNI and Python bindings to a core C engine. Great, I'll use C++! I proceed to start searching for containers/constructs and realize that at least 50% of the C++ language and standard library is unreadable to me. I only have a guess as to what a `shared_ptr` or `unique_ptr` type is, and I certainly don't know when to use them, and concepts/traits outlined in the STL documentation seem to be some sort of generalizations that the STL might provide default implementations for, but might not? What exactly is the syntax and/or type of a lambda function and how do they interact with C function pointers? etc., etc.
I get the sense that C++'s evolution has been driven by esoteric corner cases that a handful of developers have encountered and the language & STL design has been driven by those cases. I'm still surprised by how many problems I can easily address in something like Scala or Haskell that C++ simply does not provide the facilities to express easily.