It is indeed an attempt to extend C while maintaining backwards compatibility, which can be a bit of a mess on it's own. This is probably one of the main reasons that spawned D's creation, a language that does what C++ "should have" done and breaks backwards compatibility while still
maintaining the 'power' of C++. I guess calling it a hack is acceptable, at least to me, maybe not to C++ developers though who feel it is more than just a hack.
Thank you, I appreciate the link even more. This is my second language, and I appreciate any opportunity to improve my writing, even though it is my second language I've learned it is still my main language I use.
> Not only does C++ have no notion of interfaces, it actually allows subclasses to hide public methods from the superclass.
C++ doesn't have "methods"; it has member functions (static or nonstatic). Nonstatic ones can be virtual. Virtual member functions are what corresponds to OOP methods.
To use virtual functions as methods, you call them through a base class pointer or reference to an object. That's when you're doing OOP dispatch.
Now a derived class in C++ can define member functions of the same names as a base. These functions lexically shadow the ones in the base. If you use derived objects as their actual derived type (not through a base reference), then you're using those functions. This is "statically clear" in a static language, and it makes sense. If I open up "program.cpp" in my text editor and edit some function so that a "Foo f" object is replaced by a "DerivedFoo f", then I want all occurrences of f.func() to now be calling DerivedFoo::f rather than Foo::f, if DerivedFoo::f exists. I don't want the compiler to complain that the situation is ambiguous, and I don't want it to keep calling Foo::f, which is nonsensical.
Lastly, access is orthogonal to visibility in C++; it is not accurate to say things like "allows subclasses to hide public methods". Identifiers declared in the derived class scope simply shadow like-named definitions in base classes, period. That is regardless of access. Access specifiers do not alter visibility (or any program semantics). If you manipulate the access specifiers in a C++ program, at best you can make some diagnostic go away, or introduce the need for one. All permutations of access specifiers which don't require a diagnostic will behave the same way (and should produce identical code, really).
> One of the increment operators takes a dummy int parameter in order to allow overloading. Can you tell which without googling?
Yes, I can tell without googling as can any reasonably competent C++ programmer. Why would you need to answer the question in this form? The feature is there for when the situation arises that you're writing an overloaded ++ operator and would like it to be postfix. If you don't care about overloading ++ or making it postfix, you don't have to be aware that there is an int hack for this.
> Without adding another layer of indirection via the PIMPL idiom, changes to the internal structure of the class propagate to all it's users, making compilation painfully / unecessary slow.
The class declaration being fully visible to all places which use it allows C++ to be efficient in its handling of data types. For instance, when you have a local variable "FooClass x", the compiler knows exactly how large x is down to the byte and can give it a fixed-size place in a fixed-size stack frame. It also allows for inline functions which compile to code that accesses the object directly.
This low-level nature is a conscious design trade off in C++, which makes it what it is. If you want to eliminate the dependencies, you use that PIMPL idiom explicitly. In some higher level languages, the equivalent of PIMPL is implicit everywhere. (For instance, everything is a "heap object"). C++ is one language you can reach for in situations when you don't want everything to be a heap object, yet still have some halfway useful abstractions.
> C++ did not have any standard multithreading implementation until C++ 11.
That was five years ago. C++ users did threading in C++ code for at least twenty years before that, in platform-specific ways, or using cross platform frameworks like ACE. Because C++ doesn't hide a lot of behaviors under the hood, you can let platform threads loose directly into C++ code and the effects are predictable and manageable, for the most part, modulo issues like "does unwinding happen if the thread dies" and such.
> Yes, I can tell without googling as can any reasonably competent C++ programmer.
As far as litmus tests of "reasonable competence" in C++ go, "having the signature of every overloaded operator memorized" is among the worst I can possibly think of.
> If you don't care about overloading ++ or making it postfix, you don't have to be aware that there is an int hack for this.
Overloaded operators do not have "canned" signatures that you have to memorize.
You generally design the signature. Some are unary and some are binary (or either), but you pick the argument types as well as return value.
The increment and decrement ones (++ and --) have a hack whereby an extra dummy argument of type int indicates "this is postfix" and isn't actually an argument which is making the operator binary! The argument isn't given a name.
Anyone halfway knowledgeable in C++ can be expected to know this piece of trivia, IMHO.
It's a huge hack, but the argument that it's something hard to remember that you have to Google for doesn't hold water.
> Anyone halfway knowledgeable in C++ can be expected to know this piece of trivia, IMHO.
I disagree. Let me put it this way: You will not increase the quality of your C++ programmer hires by quizzing them on whether they know whether prefix or postfix gets the extra argument, or even how to overload those operators at all.
> It's a huge hack, but the argument that it's something hard to remember that you have to Google for doesn't hold water.
So basically you agree with the the fact that it's a hack, but feel that it's necessary to attack me personally (with a well placed "ad hominem" fallacy) for not being able to remember such trivia.
Let's make this clear: this isn't a test on how well you know C++. It's a rhetorical question, aimed at people how don't know C++, or only have a basic idea of how operator overloading works. Would they be able to guess which is overloaded how? No. Because it's an unintuitive hack.
That's the purpose of the question.
> Anyone halfway knowledgeable in C++ can be expected to know this piece of trivia, IMHO.
You can have your "humble" opinion (first one so far). I'll just have to humbly disagree with it.
Breaking compatibility with C isn't such a bad thing. Also I think C, C++ and D are easily made compatible with each other (I would not bet on that too much though).
I've been hearing about D for at least ten years. I don't think it's going to really break out at this point - it's been sort of lapped in the trendy, new language corner by Go and Rust and whatnot.
Worse, it's been lapped into the 'who cares' corner. D had a burst of buzz for a while, about 10 years ago, but at the time nobody really wanted alternative systems languages. IT got a little support, but mostly people ignored it. Now, you have Go/Rust/Nim jumping into the space and getting lots of Buzz, but if D is mentioned then one of two things will happen:
A) "Oh yeah, that language from the mid 2000s. I didn't want to use it then, why would I use it now?"
B) "D? Oh it's from the mid 2000's? I wonder why nobody used it? It probably sucks."
Even if those statements are false, it's hit the same problem that a 50-year old programmer in the job market is going to hit, age discrimination.
Most of the OP's points are valid, no denying that. But, looking at the myriads of properly working, fast, not 'exploding in your face at runtime', often even beautiful code produced in C++ despite these shortcomings: doesn't really seem like a poblem if one knows how to deal with it.
> But, looking at the myriads of properly working, fast, not 'exploding in your face at runtime'
Does remote code execution due to use after free fall in the category of "properly working" and "exploding in your face at runtime"? I would think it does, and C++ is vulnerable to that like few other languages are.
That was mostly put in there as a transition from C. Modern C++ users shouldn't be using new / free at all, since shared_ptr (and weak_ptr, its counterpart) and unique_ptr basically cover all the use cases.
Whether you actually call "free" doesn't matter. You're falling victim to the "smart pointers/modern C++ is memory safe" fallacy. It is not, for reasons I've elaborated on at length numerous times before.
Technically, that's a iterator invalidation and those are indeed hard to keep track of. There are runtime libraries that keep track of pointer-invalidation for you however.
In any case, a std::vector<shared_ptr<std::string> > would actually avoid the issue in this particular case.
Use after free... is more about use of raw pointers IMO. I wouldn't describe that code to be a "use after free" at all. In most languages, you are simply not allowed to change the container at all. Iterator invalidation is a hard issue in any language.
Iterator invalidation does not result in remote code execution in other languages. Regardless of what you call it (and you are in the minority by not considering that code UAF) the result is the same when combined with vtables and attacker-influenced heaps: arbitrary code execution.
shared_ptr is also not memory safe when combined with references, which you need to use to call methods on the shared_ptr'd object or to pass those objects to most functions.
Tecnically that's reference invalidation and no pure library is going to help you. You need compiler/runtime help (i.e. static analysis or some sort of sanitizer).
But if "Foo" was composed of unique_ptr (or shared_ptr), then those smart pointers would be nulled out after the std::move (due to the default move constructor)
So again, unless Foo f was composed of "raw pointers", even that kind of code is safe from "use after free" vulnerabilities.
Does remote code execution due to use after free fall in the category of "properly working" and "exploding in your face at runtime"? I would think it does
Not exactly. Use after free, while indeed problematic, is not what I would consider proper code, and I think you're well aware of that :]
Yeah, after fifteen years of C/C++ I can say most of those complaints are minor, have easy workarounds, or can be ignored when doing general-use programming.
I think CSS is overall well designed, though with plenty of flaws. It's remarkable, though, that everyone agrees that it's bad, but every proposed replacement I've seen has been significantly worse.
Those mistakes are legitimate complaints. But it's interesting that most of the complaints about CSS do not fall under that list.
Isn't name mangling a standard thing done by just about every language except for C that compiles to object code? The lack of standardization between compilers sounds potentially problematic, but name mangling itself doesn't seem like a bad thing. The alternative is to force the programmer to do their own "name mangling" a la C.
Name mangling is a sad old way of using antiquated object formats while moving forward with a language. Better solutions are obvious - a new object format that stores constraints/annotations/scope info would be a good one.
"[C++] actually allows subclasses to hide public methods from the superclass."
That one doesn't even make sense. A base class (superclass) in C++ never knows anything about any of its descendants. So it can only ever know about methods declared in the base class, and what their visibility is in the base class. Secondly, it's true that a derived class could (for example) override a public base class method and make it private in the derived class, but that won't change the ability of the base class to call that method.
You misunderstood. When the author wrote "from the superclass" they were trying to say "declared on the superclass". It has since been updated to be clearer.
I actually tested this one since I had never encountered it in the wild. You can indeed call and get the expected results from the derived classes (now private) member function.
Just to be clear, that's not in conflict with what the author claimed. You can't call that method using a pointer to the derived class. That would be an access violation, because it was declared private. You need to use a pointer of the superclass type.
struct I_A {
virtual void foo() {}
};
struct A : I_A {
private:
virtual void foo() {}
};
int main() {
A a;
// a.foo(); // compile error
I_A& ia = a;
ia.foo(); // ok
}
To be honest, I find this behaviour useful so it doesn't upset me. It's a decent way to mark off functions that you needed to implement to satisfy an internal interface as not-for-users.
Ok -- but I don't think anyone legitimately holds on to C++ as an example of a great, well-designed language. C++ has existed for a long time, and the corporate world has increasingly moved away from C++ because it doesn't offer enough object-oriented goodness while tying you down to a certain memory management style with a difficult to use typing/inheritance system.
Pick any modern OO language: Java, Ruby, Swift, hell, even Python. They are all far easier languages to write in than C++, have much better library support, and don't make you dive into memory management hell.
IMO C++ is syntactically very difficult, and the language too far out of touch with the architecture patterns and styles used by programmers today.
Java is pretty viable if you are performance sensitive but not HFT or video game performance sensitive. It's certainly far ahead of Ruby and Python, at least.
It's not as fast as C++, but C++ isn't as fast as assembly, either. They're at different points on the tradeoff curve - HOW performance sensitive are you? It's not a binary choice.
Java is totally viable for nearly anything; it just doesn't work well for games because they require low-level access to graphics hardware (which is definitely not something Java supports).
Also, the state of the JVM on the desktop is kind of sad - also from the graphics perspective. But Java is probably the most performant server-side language out there at this point (if you exempt the masochists who write network code in C/C++).
Are you sure you don't mean semantically difficult. With the exception of a few dark corners (meta programming, function pointers) the syntax is basically the same as another language. Learning that && denotes an rvalue reference is easy. Trying to understand what that means though is a lot more difficult
Actually, C++ syntax is so complex that you cannot use parser generators to parse it as it's grammar is not context-free. There are even some informal proofs that C++'s syntax is undecidable (as is Perl's and Common Lisp's, where it's obvious).
Visual Studio has barely implemented a rename refactor into their flagship C++ IDE. Last time I checked, it was still in a sort of 'beta / try at your own risk' stage. Compared to this, IntelliJ IDEA is light years ahead.
C++ is a multi-paradigm language. Of course that is going to be messy. There are domains of applicability where you want everyone to use the same model, the same patterns, the same paradigms. That yields true benefits for some teams.
But there are other areas where you really want to have a Big Bag Of Tricks to get things done, because they are areas where efficiency or fine-grained control are required or it just plain makes sense.
I should note that most successful C++ projects I've come across seem to only use a subset of what that Big Bag O' Tricks provides. Yet in some corners, if need be, some specialized trick can be used because it makes sense there. And it ends up being much less of a hack than if it had to be done in the mono-paradigm language.
I appreciate it being multi-paradigm as well. There are 3-4 languages in there with _great_ interoperability. As well as the ability to pull in a C library to a project with little fuss.
I learned C++ around 1990. It was a hack then, too. It began life as a weird preprocessor for C, and some of the seams from that beginning have shown the entire time.
My team is about to embark upon a C++ project. The compiler will be gcc 4.8.x, and supports C++11. So I've bought Stroustrup's "C++ Programming language 4th Edition", and Scott Meyer's "Effective Modern C++".
I've got some reading to do.
What's the state of tools related to encouraging / enforcing modern C++ style?
"It's a hack" is also an insufficient reason to avoid using C++, although I think it's both true and a useful thing to know before getting involved with C++.
The very nature of a hack is that someone needed it at some point. Even if C++ is a hack, that doesn't prohibit it from being the right answer in some situations.
I'm getting annoyed of being 'politically correct' on the internet all the time. The title is obviously a metaphor, and it doesn't need to be defensive.
Many of these are legitimate, but a couple aren't:
- "Dependency hell"—Yeah, it's annoying for sure. But what's the solution? You can't reasonably have value types and ABI stability in the presence of adding and removing fields from those value types: it's impossible.
- "Name mangling"—The legitimate complaint here is that the name mangling schema is not standardized (though Itanium is a de facto standard in the open source world). But the fact that name mangling exists isn't something you can blame C++ for: it's really Unix that deserves the blame for not supporting namespaces in their object files.
Object files are not standardized either (as far as C++ is concerned). Microsoft has it's own format. I can blame C++ for using a hack in order to keep using the same obj files as C.
I've been involved in this debate, professionally, for some 28 years now, and while I don't claim to have any deep or unique insights into it, I would like to state that I find almost amazing that it still rages at pretty much the same level as it did back in 1988.
As I've mentioned before, my first professional gig was writing real-time games and business apps for the MajorBBS, a fabulous pre-WWW multi-line BBS, in C.
Since the MajorBBS DevKit was shipped as C source and compiled as protected-mode DLLs (to of course overcome the 640k memory limit of DOS...yeah that was a thing at one time), there were several devs that wanted to use C++ "goodies" to help speed development or whatever.
The debate started when Galacticomm, the makers of the MajorBBS flat-out refused to consider it and used many of the same rational as the OP listed as reasons why it was not going to ported.
Reading over this list jogged many old memories, which is why I felt compelled to write this rather long-winded post.
My local opinion? Use whatever gets the job done and can be maintained and supported at a reasonable cost long after you are gone.
C is a hack. x86 is a hack. Unix is a hack. Windows is a hack. Products with a "do what it takes" attitude appear to outrun projects driven by purism in the short to medium term. Often its too late for a better designed product to overcome the early traction lead of such projects.
I've used C++ for many major projects, and as a compiler writer, I understand the language at a high level.
C++ requires an unprecedented degree of organizational discipline to use successfully. For most projects, in most organizations, it's a terrible technological choice -- bloated, inconsistent, dangerous, poorly understood, and still rapidly changing.
The programming world needs a language with some of the features of C++ (strong typing, easy interoperation with C, polymorphism, destructors, and control of memory ownership). But it also needs a language that doesn't perpetuate every single language design flaw that has accumulated since the Nixon administration while dramatically changing in ways that alter "best practices" across revisions of the language standard and too frequently take steps backward in readability.
And I disagree that C++ is "evolving". A process isn't really evolution unless old things are dying.
89 comments
[ 2.8 ms ] story [ 136 ms ] threadEdit: Grammar correction.
http://www.elearnenglishlanguage.com/blog/english-mistakes/s...
Still, it's all a big mess.
Well, that and getting multiple compilers to use the same name-mangling standards.
> Not only does C++ have no notion of interfaces, it actually allows subclasses to hide public methods from the superclass.
C++ doesn't have "methods"; it has member functions (static or nonstatic). Nonstatic ones can be virtual. Virtual member functions are what corresponds to OOP methods.
To use virtual functions as methods, you call them through a base class pointer or reference to an object. That's when you're doing OOP dispatch.
Now a derived class in C++ can define member functions of the same names as a base. These functions lexically shadow the ones in the base. If you use derived objects as their actual derived type (not through a base reference), then you're using those functions. This is "statically clear" in a static language, and it makes sense. If I open up "program.cpp" in my text editor and edit some function so that a "Foo f" object is replaced by a "DerivedFoo f", then I want all occurrences of f.func() to now be calling DerivedFoo::f rather than Foo::f, if DerivedFoo::f exists. I don't want the compiler to complain that the situation is ambiguous, and I don't want it to keep calling Foo::f, which is nonsensical.
Lastly, access is orthogonal to visibility in C++; it is not accurate to say things like "allows subclasses to hide public methods". Identifiers declared in the derived class scope simply shadow like-named definitions in base classes, period. That is regardless of access. Access specifiers do not alter visibility (or any program semantics). If you manipulate the access specifiers in a C++ program, at best you can make some diagnostic go away, or introduce the need for one. All permutations of access specifiers which don't require a diagnostic will behave the same way (and should produce identical code, really).
> One of the increment operators takes a dummy int parameter in order to allow overloading. Can you tell which without googling?
Yes, I can tell without googling as can any reasonably competent C++ programmer. Why would you need to answer the question in this form? The feature is there for when the situation arises that you're writing an overloaded ++ operator and would like it to be postfix. If you don't care about overloading ++ or making it postfix, you don't have to be aware that there is an int hack for this.
> Without adding another layer of indirection via the PIMPL idiom, changes to the internal structure of the class propagate to all it's users, making compilation painfully / unecessary slow.
The class declaration being fully visible to all places which use it allows C++ to be efficient in its handling of data types. For instance, when you have a local variable "FooClass x", the compiler knows exactly how large x is down to the byte and can give it a fixed-size place in a fixed-size stack frame. It also allows for inline functions which compile to code that accesses the object directly.
This low-level nature is a conscious design trade off in C++, which makes it what it is. If you want to eliminate the dependencies, you use that PIMPL idiom explicitly. In some higher level languages, the equivalent of PIMPL is implicit everywhere. (For instance, everything is a "heap object"). C++ is one language you can reach for in situations when you don't want everything to be a heap object, yet still have some halfway useful abstractions.
> C++ did not have any standard multithreading implementation until C++ 11.
That was five years ago. C++ users did threading in C++ code for at least twenty years before that, in platform-specific ways, or using cross platform frameworks like ACE. Because C++ doesn't hide a lot of behaviors under the hood, you can let platform threads loose directly into C++ code and the effects are predictable and manageable, for the most part, modulo issues like "does unwinding happen if the thread dies" and such.
...
So C++ has methods.
> If you use derived objects as their actual derived type (not through a base reference), then you're using those functions
I can use objects through a reference to derived, which still requires dynamic dispatch.
> Identifiers declared in the derived class scope simply shadow like-named definitions in base classes, period. That is regardless of access.
Which is a hack.
As far as litmus tests of "reasonable competence" in C++ go, "having the signature of every overloaded operator memorized" is among the worst I can possibly think of.
> If you don't care about overloading ++ or making it postfix, you don't have to be aware that there is an int hack for this.
... And it's still a hack.
You generally design the signature. Some are unary and some are binary (or either), but you pick the argument types as well as return value.
The increment and decrement ones (++ and --) have a hack whereby an extra dummy argument of type int indicates "this is postfix" and isn't actually an argument which is making the operator binary! The argument isn't given a name.
Anyone halfway knowledgeable in C++ can be expected to know this piece of trivia, IMHO.
It's a huge hack, but the argument that it's something hard to remember that you have to Google for doesn't hold water.
I disagree. Let me put it this way: You will not increase the quality of your C++ programmer hires by quizzing them on whether they know whether prefix or postfix gets the extra argument, or even how to overload those operators at all.
If someone put C++ on their resume but doesn't know how to overload operators, they are failing on honesty.
So basically you agree with the the fact that it's a hack, but feel that it's necessary to attack me personally (with a well placed "ad hominem" fallacy) for not being able to remember such trivia.
Let's make this clear: this isn't a test on how well you know C++. It's a rhetorical question, aimed at people how don't know C++, or only have a basic idea of how operator overloading works. Would they be able to guess which is overloaded how? No. Because it's an unintuitive hack.
That's the purpose of the question.
> Anyone halfway knowledgeable in C++ can be expected to know this piece of trivia, IMHO.
You can have your "humble" opinion (first one so far). I'll just have to humbly disagree with it.
- Bjarne Stroustrup
Breaking compatibility with C isn't such a bad thing. Also I think C, C++ and D are easily made compatible with each other (I would not bet on that too much though).
A) "Oh yeah, that language from the mid 2000s. I didn't want to use it then, why would I use it now?" B) "D? Oh it's from the mid 2000's? I wonder why nobody used it? It probably sucks."
Even if those statements are false, it's hit the same problem that a 50-year old programmer in the job market is going to hit, age discrimination.
Most of the OP's points are valid, no denying that. But, looking at the myriads of properly working, fast, not 'exploding in your face at runtime', often even beautiful code produced in C++ despite these shortcomings: doesn't really seem like a poblem if one knows how to deal with it.
And that is the downfall of all programming languages/paradigms!
Does remote code execution due to use after free fall in the category of "properly working" and "exploding in your face at runtime"? I would think it does, and C++ is vulnerable to that like few other languages are.
That was mostly put in there as a transition from C. Modern C++ users shouldn't be using new / free at all, since shared_ptr (and weak_ptr, its counterpart) and unique_ptr basically cover all the use cases.
In any case, a std::vector<shared_ptr<std::string> > would actually avoid the issue in this particular case.
Use after free... is more about use of raw pointers IMO. I wouldn't describe that code to be a "use after free" at all. In most languages, you are simply not allowed to change the container at all. Iterator invalidation is a hard issue in any language.
shared_ptr is also not memory safe when combined with references, which you need to use to call methods on the shared_ptr'd object or to pass those objects to most functions.
So again, unless Foo f was composed of "raw pointers", even that kind of code is safe from "use after free" vulnerabilities.
Not exactly. Use after free, while indeed problematic, is not what I would consider proper code, and I think you're well aware of that :]
[1] https://wiki.csswg.org/ideas/mistakes
Those mistakes are legitimate complaints. But it's interesting that most of the complaints about CSS do not fall under that list.
That one doesn't even make sense. A base class (superclass) in C++ never knows anything about any of its descendants. So it can only ever know about methods declared in the base class, and what their visibility is in the base class. Secondly, it's true that a derived class could (for example) override a public base class method and make it private in the derived class, but that won't change the ability of the base class to call that method.
Pick any modern OO language: Java, Ruby, Swift, hell, even Python. They are all far easier languages to write in than C++, have much better library support, and don't make you dive into memory management hell.
IMO C++ is syntactically very difficult, and the language too far out of touch with the architecture patterns and styles used by programmers today.
It's not as fast as C++, but C++ isn't as fast as assembly, either. They're at different points on the tradeoff curve - HOW performance sensitive are you? It's not a binary choice.
Also, the state of the JVM on the desktop is kind of sad - also from the graphics perspective. But Java is probably the most performant server-side language out there at this point (if you exempt the masochists who write network code in C/C++).
Are you sure you don't mean semantically difficult. With the exception of a few dark corners (meta programming, function pointers) the syntax is basically the same as another language. Learning that && denotes an rvalue reference is easy. Trying to understand what that means though is a lot more difficult
C++ semantics is completely another can of worms.
- great idea
- great design
- great execution
I think C++ was great idea that was masterfully executed, but suffers from a chaotic overall design.
But there are other areas where you really want to have a Big Bag Of Tricks to get things done, because they are areas where efficiency or fine-grained control are required or it just plain makes sense.
I should note that most successful C++ projects I've come across seem to only use a subset of what that Big Bag O' Tricks provides. Yet in some corners, if need be, some specialized trick can be used because it makes sense there. And it ends up being much less of a hack than if it had to be done in the mono-paradigm language.
I've got some reading to do.
What's the state of tools related to encouraging / enforcing modern C++ style?
Edit: grammar.
clang-tidy: http://clang.llvm.org/extra/clang-tidy/
clang-format: http://clang.llvm.org/docs/ClangFormat.html
C++ Core Guidelines: https://github.com/isocpp/CppCoreGuidelines
C++ Core Guidelines Checkers for VS 2015 Update 1: https://blogs.msdn.microsoft.com/vcblog/2015/12/03/c-core-gu...
http://www.metafilter.com/156134/The-internet-has-made-defen...
http://yosefk.com/c++fqa/
How is expanding the standard library a hack?
- "Dependency hell"—Yeah, it's annoying for sure. But what's the solution? You can't reasonably have value types and ABI stability in the presence of adding and removing fields from those value types: it's impossible.
- "Name mangling"—The legitimate complaint here is that the name mangling schema is not standardized (though Itanium is a de facto standard in the open source world). But the fact that name mangling exists isn't something you can blame C++ for: it's really Unix that deserves the blame for not supporting namespaces in their object files.
As I've mentioned before, my first professional gig was writing real-time games and business apps for the MajorBBS, a fabulous pre-WWW multi-line BBS, in C.
Since the MajorBBS DevKit was shipped as C source and compiled as protected-mode DLLs (to of course overcome the 640k memory limit of DOS...yeah that was a thing at one time), there were several devs that wanted to use C++ "goodies" to help speed development or whatever.
The debate started when Galacticomm, the makers of the MajorBBS flat-out refused to consider it and used many of the same rational as the OP listed as reasons why it was not going to ported.
Reading over this list jogged many old memories, which is why I felt compelled to write this rather long-winded post.
My local opinion? Use whatever gets the job done and can be maintained and supported at a reasonable cost long after you are gone.
Richard Gabriel has captured many related issues in a series of essays with far greater eloquence than I am capable of - https://www.dreamsongs.com/WorseIsBetter.html
C++ requires an unprecedented degree of organizational discipline to use successfully. For most projects, in most organizations, it's a terrible technological choice -- bloated, inconsistent, dangerous, poorly understood, and still rapidly changing.
The programming world needs a language with some of the features of C++ (strong typing, easy interoperation with C, polymorphism, destructors, and control of memory ownership). But it also needs a language that doesn't perpetuate every single language design flaw that has accumulated since the Nixon administration while dramatically changing in ways that alter "best practices" across revisions of the language standard and too frequently take steps backward in readability.
And I disagree that C++ is "evolving". A process isn't really evolution unless old things are dying.