That's what I do and it's great. I don't use move semantics or think about them, I avoid heavy templates, and I focus a lot of my work on the functional tools in C++ now and the more elegant ways of doing stuff at a high level. For when I need those extra optimizations, no need to change languages, I'm already there.
I read your option 3 and nodded - I'm using Julia for much the same reasons. I didn't expect C++ to be the answer, but it's cool that it can work out.
If you are working with (b|m)illions of particles or entities which are capable of changing ownership, you will have a hard time in any language. Whether that's the C++ ownership being unclear or the garbage collector defragmenting your memory (just because you don't see the issue doesn't means it's gone in GC languages).
That said, typically I find most algorithms have a computable upper-bound memory constraint and so you just pre-allocate a slab and then objects reference it. You will usually get better cache coherency and less wasted operations/sec. You're left with only a couple pointers you need to delete when you're done with the structure.
As for `functional programming`, it depends on what you're looking for. C++ has two killer features for this, in my opinion; expression templates can seriously improve performance of functional composition (see Eigen3), and compile-time elision of type-dependent code branches (like a static if).
Since I have learned to use `unique_ptr`, `shared_ptr`, `weak_ptr`, (`deferred_ptr`), and to prefer idiomatic accessors like `.at`, `.find`, `.insert` I cannot recall the last time Valgrind was angry at me. I think I end up writing `new` and `delete` maybe once a month(?).
If I was designing widgets or applications with hard to define user interaction, I imagine I would have more complaints to share.
An important extension to this trick is that unique_ptr and shared_ptr are specialized for arrays. So e.g. to allocate a dynamic array of size n, you can do:
auto a = std::make_unique<char[]>(100);
or auto a = std::make_unique<int[3]>(1, 2, 3);
and then use it mostly like an array - a[i] is overloaded and work as expected, for example.The reason why you'd want to do this rather than using std::vector is because you don't get the overhead of overallocation (since vectors distinct capacity and size) and default-initialization of the elements.
With these, it's quite possible to write >100 kLoC of C++ without a single new or delete in sight.
Julia has several cool performance-oriented constructs too: generated functions, loop fusion, JIT compilation (like your static if), and immutables (http://julialang.org/blog/2013/03/efficient-aggregates). It has a lot of potential, but then, so do other performance-oriented languages. It'll be interesting to do comparisons in a few years when the dust has settled a bit.
Fast chain of operations on an array was one of the primary motivations. You can take a look at Blitz++, I believe it was the first library to push that idea. But ET is not limited to numerical operations on an array, you could do query optimization, optimization of string concatenation, essentially any application where deforestation of parse trees have the potential to make things faster.
https://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html
https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenType...
If you limit the scope of your statement adequately enough, I would agree. What ET did was to remove the overhead that conventional style C++ brought to OOP matrix libraries. Fortran and C would beat C++ libraries hands down. So ET pulled C++ up to level 0: what a naïve (but verbose) implementation in, say C, would have got you. It is only after this the real stuff begins, optimizing for cache reuse, common subexpression elimination. Nowadays there are more advanced ET libraries that do this.
> linear algebra computations
To elaborate on this a bit more. We usually encounter two broad kinds of operations. One is of the linear algebraic variety, e.g. mat_vec, mat_mat etc. Here ET by itself wont buy you much. Here it is all about cach re-use. The other kind of operations are those chained operations (some times these are called array operations, as opposed to matrix or linear algebraic operations), it is here that ET will show its strength. A high performance library in C++ would typically use ET for these and fall back to BLAS for the former.
My experience... sometimes I have to pass through multiple reference frames to get to the correct affine transformation. I have three options: manually derive the transform (only works for a finite number of well understood transformations), waste BLAS ops on transform concatenation, or let expression templates fold numerical operations to the solution (often with numerical precision benefits btw).
Though 99/100 times it allows me to write the code I mean rather than the the syntax that is necessary. Of course concatenation of transforms explicitly can be equivalent in FLOPS... as long as there are no redundant calls to transcendental functions or vector operations which can be folded for better cache coherency.
> computation boils down to the same flops either way?
same number of operations, yes possibly, but that carries almost no weight at all. Write a straight 3 nested for loop for a matrix multiply and then right another one with some thoughts on cache re-use. The latter will beat the former by an order of magnitude if not two. The situation is a bit better now thanks to 'smart enough compilers'.
The prevailing rule in C++ is to try to avoid using pointers when you can, you should rather use references. It is surprising the amount of cases when this is enough.
A lot of people use smart pointers in C++, which implement reference counting transparently for a little overhead. shared_ptr was made official in C++11. It allows to make pointers on an object and delete it when all pointers were destroyed.
I personally prefer to do without smart pointers and make objects ownership explicit: If I need to have long-lived objects I will put them in a structure/array/container and comment about it being the original owners. I would then do allocation/deallocation manually, which actually makes me more comfortable, as I prefer it to be explicit.
But that is purely a matter of taste.
https://www.youtube.com/watch?v=JfmTagWcqoE
He has 3 guidelines:
1. Prefer scoped lifetimes by default (local members)
2. Else prefer unique_ptr and containers
3. Else consider shared_ptr
His whole talk gives examples that show the flexibility of unique_ptr and how modern C++ can express ownership.
See slide 5 here:
https://github.com/CppCon/CppCon2016/blob/master/Presentatio...
But I find it notable that the concern is still productivity versus performance. In this day and age shouldn't security and correctness have attained at least equal priority? I mean we still live in a world where every single year, every major web browser has at least one new critical "remote code execution" vulnerability reported. I think one thing that's being missed wrt the advent of C++11 is that it is now practical, when appropriate, to write memory safe C++ code that can be compared apples-to-apples with other modern languages. And outperform them. Except for maybe Rust. Maybe.
shameless plug for those interested in memory safe C++ programming: http://www.codeproject.com/Articles/1093894/How-To-Safely-Pa...
Not all of us have a huge runtime to support our language which runs on our target platform.
Not all of us write code which is 90% boring, UI or CRUD type stuff.
Not all of us are ok with delivering bloated applications that require hardware upgrades to run at the same speed they ran at 5 years ago.
But apparently you CAN goof around with KLOCs of useless boilerplate and the added defects they imply [1].
[1] http://programmers.stackexchange.com/questions/185660/is-the...
I am not fine with maintaining it unless it is easy to navigate to parts of code concerning what needs to be maintained.
I live in a 20 year old MLOC land and whenever we need to add or change a subtle behavior, we go debugging for half an hour at best.
At my company, three quarters of our codebase is written in C++ because performance is very critical for us, in addition portability! i.e we can compile to run the same code on different platforms without added layers of translation.
What about the other 80% of your time spent trying to get those things to inter-operate in a way that doesn't destroy the good parts of both?
That's actually C++ now. I moved to it from TypeScript earlier this year and have been very happy. (Using Google's Bazel for builds has been really nice, too—I hadn't realized how much I missed having a non-OS-specific package manager.)
The pedantic snob will usually hold some extreme position about topics such as (1) obscure language minutia; (2) endless "framework building"; (3) premature optimization; or (4) mindless design "best practices".
They will have high pride attached to whatever is it and they would add a flair of expertise to it, making you feel like you hired a genius. At the start, it looks like they are working on some super advanced wizardry, but you eventually find that they mostly waste time.
On the other hand, the good developers care more about delivering value.
The good developer will not waste time solving problems you don't have --thus slicing through your 90% problem like butter-- and at the same time will possess the sharp tools to solve the hard 10% problems your less than good developer is unable solve no matter the deadline.
For option 1, you hire a good developer. For option 2, you hire a snob.
If you hire your average cheap engineer, you get option 0: he/she will eventually do OK for 90% of the problems, but the last 10% (which is often critical) will be a mess.
I've been developing in C++ for more than ten year, and never did I feel the need to fight it. Actually, it was a nice travel companion.
And I miss it since I was forced to switch to C#: C++ made all it could to help me do what I needed, and it was effective and smart; C# always tries to convince me to change my needs in order to do things like he prefers.
In other word, C++ treated me like a professional partner; C# treats me like a newbie.
EDIT:
How many dollars have you been paid for template metaprogramming? How many contracts have you won because you used the proper const-correctness?
Folks, nobody gives two shits about how pretty your CPP (or any code for that matter!) is if it isn't solving their problem right now.
More value has been created by decent developers using the boring language features they know well than by people becoming master craftsman.
Alice starts cutting down a tree with a dull saw, Bob takes the time to sharpen the saw before he starts cutting. It's not sensible to criticize Bob on the basis that his sharpening time is time not spent delivering value.
https://en.wikipedia.org/wiki/Superiority_%28short_story%29
If you want to see your position taken to the extreme, go look at the JS ecosystem right now. Plenty Bob's sharpening their axes to the detriment of both their projects and the community as a whole.
As engineers and programmers we like to play with tools and pursue language purity and whatnot--but we cannot fall into the trap of conflating that with sound business practice.
If you want to see your position taken to the extreme, go look at 90% of programmers out there, who spend as little time as possible sharpening their saw and as much time as possible churning out awful, nearly worthless code.
Wordpress is garbage, but has a fairly secure niche that is only recently even getting attention from competitors.
Here on HN everyone is a PLT prodigy who would never deign to admit that they would ever be beaten by the 90% of programmers--but in the real world, well, they make shit happen. Some problems are solved better by throwing warm bodies with lukewarm IQs than by trying to get the perfect language lawyer.
Have some humility.
C++ backend jobs have a number of great properties: devs are scarce, because of the difficulty interviews will definitely reveal good vs bad, it's an absolute necessity to have these devs, and the products they produce outperform. Ergo mucho dinero. And NOBODY goes to a C++ dev with a photoshop file asking for things to be moved over a few pixels. Nobody. That, by itself ... And to top it all off : because C++ is a mature language the tooling is actually passable to good. In frontend, VSCode+typescript is the only thing I know of that has passable tooling. Passable meaning that editing code is good to well done. Debugging, performance measurement, deployment, monitoring, ... very much not solved. And there's a lot of things that are adding insult to injury.
Performance programming also has the advantage that there's problems that just can't be solved by anything that's more than 5% or so removed from optimal performance. And those are really interesting problems : from machine learning to gaming. When I say "can't" I don't literally mean can't, just that a company like Google or Facebook simply can't risk the other one using C++ and not using C++ themselves, due to the risk that it results in the other one getting a 50% faster stack, as they would fear that would be an overriding advantage (and advantage big enough that nothing else matters. Working with a double as fast stack, a mediocre data scientist can outperform a good one, resulting in more advancement, resulting in first-to-market, resulting in). And this applied not just to machine learning, but it applied to games, to databases, to OS, to ... C++ has conquered the machine learning stack, and it's never letting go. The point is that this is not C++'s first rodeo so to speak. C++ has conquered many markets that will never go back to non-C++. And those are exactly the ones you want to work in.
But yes, it's pretty serious in the learning curve. And a subtle language results in subtle problems. That generates a ton of hate. One last advantage though : if you're an expert in C++, you're not far removed from being an expert in a lot of different (imperative) languages. C++ has options to do anything, so learning a new language is figuring out how some new language maps onto C++, and getting past the frustration that lots of things just aren't possible in the new language.
You get the high productivity AND the performance together.
(Also, the tooling is so much better).
1) Spend 90% of the time writing performance-neutral code in a modern high-productivity language and 10% on performance-critical code in a modern high-performance language.
2) Write everything in a "performance beast" of a language, spending 90% of your time fighting said language and 10% delivering useful functionality.
Bottom line, if you choose option 2, you spend 10x time delivering the same functionality and performance and you force yourself to hire the kind of expensive developers who are willing to waste their time mastering modern C++.