Yes, it's true. && might do something different than you might expect if you're a C programmer. If you're a C++ programmer, then you know somebody might have overloaded the && operator. Yes, they could do it in some weird and unexpected way. Just like somebody can declare a weird and unexpected macro in C.
Yes, people who define C++ classes you use can screw you with dangerous or incorrect behavior, like throwing exceptions from destructors. Just like people who write C libraries can screw you with dangerous or incorrect behavior.
auto_ptr is not useless. At least, I assume it's good for something. Who knows? If not, who cares? boost::shared_ptr is "poor man's garbage collection," not auto_ptr. If you cite the documented behavior of a type as a huge and unwelcome surprise, then gosh, I know exactly how to avoid surprises like that: read the freaking documentation before you use the type.
Oh, and again with the "I can't tell what this code does." Here's a crazy scenario in C: call a function. Do you know what the code does? No, you don't. How awful! Unless you look at the function you just called, or maybe just glance at its documentation.
Half of these criticisms of C++ boil down to, "It's okay for functions to have unknown behavior that I have to learn about before using them, because I'm used to that, but it's not okay for types to have any kind of unknown behavior, because I'm used to C, where that is not the case."
> It's okay for functions to have unknown behavior that I have to learn about before using them, because I'm used to that, but it's not okay for types to have any kind of unknown behavior, because I'm used to C,
It is not an issue of behavior but of understanding.
In C++, a large number of things can be happening in a single line: an operator being called, a type conversion, a template instantiation, an overriden function in one of a dozen of classes, a constructor called in some part of the hierarchy, in some namespace... who knows. You have to be smart enough to figure out what is going on in each of these cases. As you code base grows, this makes it increasingly hard to understand what is going on.
By that logic, you shouldn't use any dynamic language, because someone could monkey-patch every function and you'd have no guarantee what exactly was being called.
Also, you forgot macros in your list of gripes, as they can stomp on literally ANYTHING indiscriminately, despite being declared far away from the actual invocation point.
+1 on dynamic languages, not a fan for this and many other reasons
Haskell seems to go a long way towards solving this problem in my humble opinion. Meaning it can have very dense code but as long as you follow the types it is often fairly straightforward to figure out what a piece of code does.
The worst part about all the things that can happen implicitly in C++ is that they can all have side effects! Oh fun. This is not just a pitfall of C++ either, Java/C# can have the same issue - one statement can trigger a whole plethora of side effects making a large brownfield development project one hell of a nightmare.
Haskell seems to go a long way towards solving this problem in my humble opinion. Meaning it can have very dense code but as long as you follow the types it is often fairly straightforward to figure out what a piece of code does.
As a Haskell fan, I have to say that there is a bag of hurt here as well: performance in the lazy evaluation regime. It can be very hard to predict/understand when thunks are evaluated and the effect it has on heap use.
I am still hoping for a strict, pure language with monads to go mainstream enough to use it.
The next Haskell will be strict - Simon Peyton Jones
#define FALSE 1 is not that uncommon in C because C in its infinite wisdom doesn't have true/false literals so people have to invent those by themselves.
Glib goes into great lengths to avoid namespace conflicts so it defines the GBoolean type and G_TRUE/G_FALSE. Whether that really is better than #define TRUE/FALSE, I don't know.
In C99 #include <stdbool.h> has the type bool and the constants true and false (even in the "freestanding" flavor). Of course this doesn't save you if you are dealing with old/non-conformant compilers and/or legacy codebases.
So then what are they supposed to do, require a library X which has already invented them, while your app is using library Y which already does the same thing as X?
It is true that the pre-processor can create a lot of confusion, however it is easy to generate a -E version of the file that shows all substitutions. It is not even close to the confusion created by templates, for example.
operator[] adds a member to a map if it does not already exist
The main theme of this article seems to be "C++ gives you too much flexibility," but this is a case where C++ is enforcing a default behavior and limiting your flexibility. In Python, for example, understanding code that queries for a key in a dictionary requires you to know some context about whether that dictionary has a default value for missing keys. For all the quibbling the OP does about operator overloading, I'd expect him to be grateful for the predictable nature of map's operator[].
Firstly, half of the time the guy bitches about flaws with the compiler, and not the languages (Page 0 to 6).
Most of the bitching is about language features which exist. If you don't like auto_ptr then DON'T use auto_ptr! C++ at least allows you to ignore features without paying a penalty. Which is why it has a virtual keyword; and does not make every method virtual like Java.
The at() versus operator[] is also an example of the above: bounds checking can get expensive in a tight loop.
It seems that the author has never seen a large, well-written C++ project. In fact, that is the case with many such C++ bashers (I used to be one too). Once one sees how C++ makes it easy to manage a 500000 line project, they will probably think again.
> Firstly, half of the time the guy bitches about flaws with the compiler, and not the languages (Page 0 to 6).
That's pretty unfair. It's the language's complexity that makes the compiler task almost impossible. Before clang came around, most C++ compilers had really shitty error messages, gcc, icc, and microsoft's indiscriminately.
What I meant was that unless you're writing a C++ compiler, these reasons should not longer bother you. Even if you are, you're free to leverage off clang's lexer and parser.
Otherwise it is a lot like saying Java is bad because it is hard to write a good VM.
Exception safety is achievable (pthread_mutex_lock example) if you wrap resources with RAII classes. If he called this "difficult to use C libraries while retaining exception safety" it would be more accurate.
The iterator criticisms are applicable to C iterators too (represented by pointers or user objects).
The various things `baz = foo->bar(3)` could mean are interesting. Some of them are reasonable, if you assume some sort of consistency. It may make audits hard, but the fact it might use implicit cast converters there should not make it harder to read if you are allowed to make the most basic assumptions about what an automatic cast operator would do.
> With C++, you don’t see the writes, because it says
`some_func(whatever,the_int,SOME_FLAG);` and it can still write the_int if it’s a reference. In C, it would be &the_int, which is easily recognizable.
I don't understand why people do this. C++ has not abolished pointers; it's still possible to require the &, and it increases readability considerably; why ever use references (to value types; objects are different) as function parameters?
Of course it is, with the minor syntactic addition of callers passing in the memory location. References aren't a _necessary_ feature of function parameters and if the reference is going to be modified, a pointer is more clear and should be used.
But there is a utility in allowing references to exist in the language for overloaded operators, so you can overload a subscript in a collection and assign to it, such as
foo[x] = bar, where foo is a collection of bar objects.
They could have restricted references to just these sorts of use cases and when using foo[x] as an argument, require the user to type &foo[x], but once references are in the language allowing them as parameters seems like a reasonable enough decision to me.
I tend read all "C++ is (bad|good)" lists and it never occurred to me to look at operator overloading as something inherently bad, something that sacrifies the clarity of the code for its brevity (see page 23 and onward). This is an excellent angle... but not that I would prefer C++ to not support overloading.
Many of the complaints have become irrelevant or at least much less relevant with C++11 and huge progress also on the compiler side (most notable clang but for example GCC also has improved many of the unreadable error messages).
I don't use nightlies, but last time I checked even clang didn't have great template error messages. C++11 doesn't address his complaint of ever-changing standard (though I don't think that's really serious). Does C++11 support lazy extensions? (Last I checked it didn't. Use case: "Unless someone declared operator&&, then both are evaluated.")
I think most of the complaints are still valid even with C++11. Whether the complaints are worth bothering about is another issue: C++ is hard, yeah, it's not going to get easier. Personally I wouldn't ever start a new project in C++, I'd rather use C or Go or Obj-C if I need low-level speed and there's a plethora of high level languages (that can still connect with C if needed) if I don't. I nevertheless have to know C++ (non-11) for all the older projects out there, most of which probably won't move to standardize on 11 making 11's benefits moot.
"I nevertheless have to know C++ (non-11) for all the older projects out there, most of which probably won't move to standardize on 11 making 11's benefits moot."
Yep, lots of projects out there that if I am lucky have moved up to Centos 5. When they get C++0x where all of these issues are fixed is just this side of never.
Oh wait a minute. Did I write C++0x? I mean when they get C++11 when all of these issues are fixed.
C++11 has made things more complicated, not less. All these criticisms are still valid and can be extended with some added new stupidities (for example needing to explicitly specify capture in lambdas, a new and entirely unique way to shoot yourself in the foot; or user defined numeric literals which brings the joys of parsing C++ down the stack making even tokenizing it difficult).
After seeing some of those rants, especially after the namespace-hatred, I wonder if they'd prefer a flavor of Assembly. Apparently they hate many common methods of simplifying and organizing code - clearly, things like virtual methods should always look different than non-virtual methods, namespaces are pointless when you can `gl_open`, and all macro-like functionality is simply obfuscation.
Most, if not all, of his arguments were already beaten to death. The rebuttal should be floating around somewhere on the Internet, but I couldn't find it with a quick search right now.
Anyways, right at the beginning of the presentation you see the quality of his arguments as he uses a very known problem of a particular compiler (GCC) to bash the language. Sort of a straw man fallacy.
His argument is that C++ is so complex that it's extremely hard to write a fast, reliable compiler that generates good error messages.
The fact that it took gcc programmers 10 years to get to a point where gcc doesn't use ridiculous amounts of memory when compiling few lines of tricky code is a fact in favor of the argument, not against it. Gcc programmers are smarter than an average programmer. If it was easy, they would have gotten it right on the first try. Accordingly, C or Java or C# or Go compilers didn't have such problems so C++ is more complex for compiler writers than pretty much every other known language.
The same goes for error messages. In the past decades we've had tens of C++ compilers. If clang is the first one that is able to generate decent error messages for templates, then it means it was a hard problem to solve.
None of that got any easier: if you want to write another C++ compiler, it'll still be hard for you to generate decent error messages or compile tricky C++ code reasonably.
A possible counterargument: it took gcc programmers 10 years to get to a point where gcc doesn't use ridiculous amounts of memory when compiling few lines of tricky code _in_C_.
Clang, written in C++, is faster, uses less memory, produces better error messages, and, I brink, took less calendar time and man hours to write.
Yes, it is a hard problem to solve, but from these two anecdotes, it seems solving it in C++ is easier than solving it in C.
And yes, I know the above probably is a fallacy; gcc hackers probably spent most of their time working on CPU ports and on getting gcc to produce better code. Begin hackers, and not having a C++ code base themselves, getting good C++ error messages just wasn't important enough for them.
I could use your argument to describe pretty much any modern compiler or framework. "It is extremely hard to write a fast, reliable Java SDK that generates good error messages.", for example.
It took years to GCC to arrive where it is because it contained A LOT of legacy code that have been accumulating since its first versions with C++ support. GCC is a compiler that evolved little by little through long years, and many features were added with little design, to fix punctual problems, which made it difficult to converge to a clean implementation.
Clang, on the other hand, had a good deal of proper design to write a clean, robust compiler. The fact that clang reached where it is in such a short time compared with GCC is a good evidence that it is possible to write a good C++ compiler with nice performance and error reporting if you employ good engineering efforts.
No Java compiler has ever generated the kinds of error messages that every single C++ compiler did for template errors. Feel free to give me an example of Java compiler error message that takes 2 screens and fails to give you useful information.
If you compared the complexity of clang C++ front-end with complexity of any Java compiler front-end, you would see a huge difference in complexity (size of the code, time it took to develop).
The first checkin in clang code base was in 2006-06-17. It took almost 5 years and more than 31 thousand checkins to get to current state which is: not done (http://clang.llvm.org/cxx_status.html).
A fully functional, fully compliant Java compilers have been written in fraction of this time by much smaller dev teams.
The facts do not support your claim that "proper design" is enough to write fully compliant C++ front-end in a reasonable amount of time. 5 years of effort of top-notch, highly paid developers to not achieve the goal is not reasonable and it's fully the result of C++ complexity.
Except that the problem of understandable error messages for C++ is not solved. That was the whole objective of the proposal of concepts, which were not included due to last minute problems.
Please flag this article. Why do we feel the need to discuss how much C/C++ rocks/sucks repeatedly? HN should frown upon bikeshedding more than it does.
Very true. I don't think articles like this fall under pg's definition of deeply interesting. They always draw crowds though. When I see one I have to summon all my strength not to read it.
Yes, it's true. && might do something different than you might expect if you're a C programmer. If you're a C++ programmer, then you know somebody might have overloaded the && operator. Yes, they could do it in some weird and unexpected way. Just like somebody can declare a weird and unexpected macro in C.
Yes, people who define C++ classes you use can screw you with dangerous or incorrect behavior, like throwing exceptions from destructors. Just like people who write C libraries can screw you with dangerous or incorrect behavior.
auto_ptr is not useless. At least, I assume it's good for something. Who knows? If not, who cares? boost::shared_ptr is "poor man's garbage collection," not auto_ptr. If you cite the documented behavior of a type as a huge and unwelcome surprise, then gosh, I know exactly how to avoid surprises like that: read the freaking documentation before you use the type.
Oh, and again with the "I can't tell what this code does." Here's a crazy scenario in C: call a function. Do you know what the code does? No, you don't. How awful! Unless you look at the function you just called, or maybe just glance at its documentation.
Half of these criticisms of C++ boil down to, "It's okay for functions to have unknown behavior that I have to learn about before using them, because I'm used to that, but it's not okay for types to have any kind of unknown behavior, because I'm used to C, where that is not the case."