I think you mean aren't full of those problems yet. :)
There's a lot of nasty corners in C++ but almost all of them are because it effectively has 40 years of wild success. (I say 40 because C++ inherits C's baggage and success.)
It's totally reasonable to pick a younger language with fewer warts. Become an expert in it! Be productive! Ship lots of apps.
Then, one day, decades and several versions hence, you may find yourself now dealing with all of the warts that language has accrued over time. At this point, you're too invested in it to jump ship. The marginal cost of learning a new corner case is smaller than the cost to start over from scratch and learn 2046's new hot language.
Alas, expertise is hard. Everything you learn makes you better at X but slightly disincentivizes you to learn Y, which may not even exist yet.
That's my opinion, maybe I'm just too used to high level programming to be "good" in C++ but my experience is C++ breaks the "principle of least astonishment" every single time I use it. For example, a simple operation like clearing a stringstream is... not straightforward.
...Or maybe I'm just not smart enough.
Still, here's a few for you: the design of Cloneable is considered to be a mistake, as are exception specifications. These have real negative consequences. File IO was laughably bad and completely unintuitive needing several classes for the simplest of tasks (perhaps this was fixed more recently, but funny you should mention streams).
Resource management was quite tricky to get right before try-with-resources and it still is if one wants performance. Memory leaks and resource leaks are still possible. Boxing and unboxing. The particularly weak type-erased generics.
Java's saving grace is that it's a very programmer-friendly language. No matter how much one screws up, the worst that's probably gonna happen is a null pointer exception with a nicely formatted stack trace attached. The tooling is top class. All of this means that anybody with a pulse can program in Java and get a mostly working program. It won't be a joy to use, unless one is particularly impressed by classes, methods, interfaces in abundance, class hierarchies and factories.
C++ is not like that. If one doesn't use the safer modern idioms and makes a mistake it will crash (if lucky),or act in weird ways or corrupt something. Sometimes there is no way to avoid dangerous code.
There is no nice stack trace, you have to work for it. Probably have to stare at assembly and work with a fully featured but thoroughly painful to use command line debugger that's twice as old as Java.
But it's powerful and really fast. It lets one design creatively, because it supports more abstraction possibilities. And it's not owned by any corporation, it's an international standard that runs on most platforms.
I understand why people use Java. I use C++ because it's not just a tool that gets the job done, but also a way to challenge myself to make something that requires attention and skill. If I want to, it's me and the machine without framewerks, factories, fancy IDEs, dependency injection, package managers and virtual machines.
I'm not impressed by "classes, methods, interfaces in abundance, class hierarchies and factories." I find Java a joy to program in because I can just Get Shit Done™ and things do what I expect them to do.
In my old age I just want to get shit done, challenging myself is no longer very appealing. That's a personal preference though. There was a time that wasn't the case.
Obviously managed code is not for every application but its good enough for most. I am eternally grateful for C++ programmers for the applications where C++ (or other lower level languages) are needed because I wouldn't want to be doing that. I think C++ programmers, as a group, are so used to C++ idiosyncrasies that they don't see them as idiosyncrasies. That goes for any group though.
For what its worth my first language was C++ but C++ always "astonished" me.
(I've never had a problem with boxing and unboxing. Java has had autoboxing for a really long time so those days of manual boxing/unboxing are ancient history)
"Several classes" Are you taking about input/output streams, not file I/O?
Half the age of C++ (and it's ancestor C which is it largely compatible with).
> It has absolutely stood the test of time.
Sure, it's held up really well. It takes advantage of the fact that baking in GC, memory safety, and platform independence sweeps a lot of C++'s nastiness under the rug. Of course, those choices are also what generally makes Java unsuitable for many of the things you can do in C++.
Even so, Java has certainly acquired it's share of cruft. Primitives, type erasure, checked exceptions, Uri.equals() hitting the network (!), nested classes, covariant arrays, arrays and lists being different and arrays getting all the nice syntax, etc.
I realise that i am not exactly disproving your point about cruft here.
Why are checked exceptions cruft?
Most new libraries don't use checked exceptions. They were a good idea in theory but not in practice.
It turns me off go and Haskell altogether because they are languages suitable to applications. I think it's going to lead to a lot of apps in the wild ignoring errors.
Rust is a bit different because it's a systems language, it makes sense there.
Like in Go I can do:
f, err := os.Open("foo.bar")
I am then free to ignore err or do something with it, but the type system forces me to at least acknowledge that it exists. It's the "compiler forces you to acknowledge the possibility of an error" that makes them pretty similar to checked exceptions in my opinion.It seems similar to JavaScript. There were some initial good ideas and a bunch of early bad ideas and we're stuck layering improvements over that very uneven base.
What languages are getting even close to being as bad as C++ in this respect though? Java, Python and JavaScript have been around for a while now for example and they seem more opinionated in a way that stops too many warts from appearing.
C++ has undefined and compiler dependent behaviour in the core of its design which I think is the real problem. Its complexity just multiplies at an exponential rate as more features and undefined behaviour is introduced.
C++ has something called "unspecified behavior" also, which is stronger than undefined, because it gives requirements. It denotes some circumstances when an implementation can behave in one of a finite possible ways (none of which fail) and must choose one of them without documenting which choice is made. For instance the expression f(a(), b(), c()) can call the functions a, b and c in any of the six possible permutations of the order: no particular order is specified. (This arises due to the order of evaluation of function arguments being unspecified, together with the sequenced evaluation of function calls.)
The specifications of C and C++ contain large number of places in which they carefully state that no requirements apply to some situation. This is not a good thing; it's basically a myriad holes punched in the requirement spec, any one of which could be a pitfall.
Few languages are so chock full of holes in their requirements.
But it is precisely specified how to invoke undefined behavior. For example when to numbers are added and they overflow the size of the type. This is carefully specified when this is or isn't undefined behavior. The C++ standard goes into excruciating detail but they have not covered everything
Compare that to many of the newer scripting languages. Elsewhere on this page I described how to recover from a segfault by setting string conversion function, which I don't think was specified anywhere. You can break some newer language by overriding innocuous function like #hash on some objects. These languages have at least as many pitfalls as C++ they are just less well described.
> unspecified is stronger than undefined because it gives requirements.
I do not understand what you are saying.
"undefined behavior" refers to situations in which no requirements apply at all. (Truly unspecified in the regular sense of the word.)
Unspecified means the implementation has some leeway.
Undefined means the implementation can do anything.
In the CRuby/MRI interpretter when a segfault happens it calls the #to_s method on the current execution context, to get the some memory address and technical details. Ruby happens to be flexible enough that you can override that method. Then you can call whatever you want and you have effectly "recovered" from a segfault. The parser is totally in an inconsistent state and could crash at any moment for any reason but it works often enough people have done it live in front of audiences for talks.
It is not officially specified that I am aware of that overloading #to_s on anything allows recovering of segfaults it is intended for converting things to_strings.
For more on what I mean start reading the Ruby standard, which was about 1,500 pages last I checked. Then check the C++ standard which is about 3,000 for C++11 and then another 2,500 for the standard library. C++ with its standard library is much smaller than Ruby (in terms of the functionality provided) but has at least 3 times the specificity.
Imagine your surprise when you discover via dmesg that your have generates hundreds of segfault per second but a string conversion functions hid this bug.
So I just went and look this up the C++14 standard, or at least the nNvember draft is 1368 pages and the Ruby ISO standard is 313. I was off by a fair amount.
But this made my point more extreme. C++ has mauch smaller feature list than Ruby and the spec is more 4x the size C++ is much more specific and precise. Ruby and many other languages will use the behavior of some de-facto implementation as their standard.
It wasn't that long ago that we had different pointer for small fast memory and large slow memory, back when more that 64k was a real big deal. Kind of like how now video memory is a real big deal. Now we have a bunch of weird C APIs for putting things there (I presume these can be accessed from JNI or something similar), but these are temporary ugly hacks.
When video ram becomes a more common thing to refer to specifically, what will that look like in Java? That weird memory that C++ has is ready, because it has done exactly this in the past a few times. I am also looking forward to C++17 and its newer way to run std algorithms on heterogeneous executors (like GPUs).
Do you really find JavaScript, Ruby, Java and Ruby harder to master than C++?
If you want to discuss programming languages in general then that is a totally separate discussion. C++ is not in the same class of languages as JS or Ruby, so why would you use them interchangeably? C++ is meant for low level system programming, when performance or size are constraints on the problem you're trying to solve. The problem domain itself entails a certain amount of care.
Also, JS and Ruby are indeed just as hard to master as C++. Heck even Bash is hard to master. Case in point.
http://codegolf.stackexchange.com/questions/tagged/code-golf
> Also, JS and Ruby are indeed just as hard to master as C++.
I have to call Bull on this part, with apologies for the tone (but I don't know any better way to put it). How on earth you can claim JS is harder than C++? C++ is doubtlessly a much more complicated language than JS, and for good reasons (albeit a good portion of those are historical). However sub-optimal JS is (for it was designed with hardly any gestation time and on a ridiculously tight schedule), it can be taught, almost completely in a matter of hours (sans the DOM). Add a couple more hours for teaching browser security models and pointers on where to find docs for everything JS.
Because I didn't? Please re-read. I said any language is just as hard to master since the earlier context was obscure dark corners of C++ syntax (Which was the general topic of the article. The vast majority of C++ programmers would never have run into that syntax bug). JS has obscure syntax and my argument is that you need to expend equal effort to master it.
> C++ is meant for low level system programming, when performance or size are constraints on the problem you're trying to solve. The problem domain itself entails a certain amount of care.
Agree with this but my view is you should avoid C++ at all costs if practical because of its productivity and bug risk impacts compared to other solutions. If low level is required though your options are limited.
> Also, JS and Ruby are indeed just as hard to master as C++.
We'll have to agree to disagree then because C++ is an order of magnitude more complex in my eyes. Code golf isn't a good metric to me as that's all about using obscure tricks to write as few characters as possible which isn't how you code normally. I would rather measure how easy it is to write code with minimal bugs and C++ gives you a million ways to shoot yourself in the foot.
To me, mastering a language is knowing how to use it in a way to solve a problem in the fastest development time with minimal bugs and maintainable code which does not match up with code golf. C++ gives you lots of ways to introduce weird bugs which isn't something code golf demonstrates.
> So lemme ask you, can you name books (or courses) that teach C++, and devote a significant chunk (or any) of the material to cover such arcane syntax topics?
The C++ Programming Language book and Effective Modern C++ are good and they're full of "don't do this or something bad will happen!" parts. I'm not really concerned about a few arcane syntax problems but I am concerned that C++ has a lot of undefined behaviour that lets you shoot yourself in the foot in a way that other languages avoid.
Granted, I mostly work on embedded image processing systems, but would use C++ in any context where long-running processes and/or low latencies are required.
On a more subjective note, I'm waiting for .NET Core to mature a bit, since C# seems to have a balanced selection of features from managed and native worlds, plus a very pleasant syntax and tool support.
I beg to differ https://www.google.com/webhp?sourceid=chrome-instant&ion=1&e...
“The object of life is not to be on the side of the
majority, but to escape finding oneself in the ranks
of the insane.”
― Marcus Aurelius, Meditations