84 comments

[ 3.0 ms ] story [ 182 ms ] thread
There are other C++11/14 features that improve performance even further that are not mentioned here. For example, automatic type deduction often finds a more correct type for a variable or iterator than you'd guess on your own, since your guess might require an implicit conversion which might be costly in some cases. Brace initialization can also avoid costly implicit conversions. And using lambdas rather than named functions often leads to automatic inlining, an additional performance benefit.

C++ is a changing language and is doing a good job keeping up the with times, adding high-level features while increasing performance at the same time.

The calculus here is simple. You have two options:

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++.

Why would mastering modern C++ be a waste of time?
Because, for better or for worse, every second spent mastering a language is time not spent delivering value.

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.

If it helps you deliver value faster afterwards then it's a win.

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.

There is rarely an afterwards in any meaningful sense:

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.

Pretty much any idea becomes stupid when taken to an extreme.

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.

Quantity has a quality all its own, my friend.

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.

They make shit happen, until it finally collapses in on itself and the people paying the bills realize they've been paying for actual shit, then they bring in a programmer with a sharp saw to clean up the mess. Happens constantly.
And that shit paid the bills for the eventual cleanup, while waiting on a better solution wouldn't have. Moreover, it's usually pretty obvious how to fix that kind of mess whereas a more clever/advanced solution can be completely unmaintainable.
Thing is, there's a number of job types in IT. There's the frontend jobs that everyone can "easily" get into (but that means they aren't good, unfortunately the frontend devs market definitely suffers from lemon law syndrome).

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.

My (limited) experience with Rust is that it's necessary to unlearn a lot of C++ habits. E.g. forgetting that Rust likes to _move_ and C++ likes to _copy_
You forgot option 3), which I use myself: Write in a high-performance language that also has high-level features, and write 90% of your code without concern for the language's cool optimization features.

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.

Haven't used C++ in a decade - how is the modern functional programming working out without a GC? Isn't object ownership tricky to untangle?

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 using Julia, then I will presume you are primarily working in some form of numerical computing. It is a very wide field, so it is difficult to say if object ownership will be tricky or not.

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.

Interesting, thank you for the detailed answer. Expression templates do look very intriguing and powerful, I wonder what kind of application it enable.

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.

> Expression templates do look very intriguing and powerful, I wonder what kind of application it enable.

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.

Curiously enough, linear algebra is the canonical application for expression templates, and yet if you ask a linear algebra expert, they will tell you that expression templates bring no benefits to 99% of linear algebra computations and make your code complex and bug prone. For examples of how expression templates can screw you up in unexpected ways, see:

https://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html

https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenType...

https://eigen.tuxfamily.org/dox/TopicPitfalls.html

It depends on the codebase you are working with. Prior to 'auto' it was difficult to impossible to annotate expressions (thus forcing evaluation on assignment). Many people still believe "more lines of code = more slow", they will have problems. Functional non-template boundaries are also slow... any time you force evaluation you will see slow down.

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).

Can you explain what you're getting at? I've spoken to people who authored expression template libraries and they say that mixing auto with expressions is a worst practice. You're setting yourself up for bugs. See the third link above. As to chaining affine transformations, how is letting the expression-template library evaluate a chain of transformations different from doing so explicitly? Would you agree that the computation boils down to the same flops either way?
You claimed there was no benefit to expression templates and I agreed that in C++98 this is true. Auto can introduce complications, I agree... I have a few times experienced the aliasing bugs it introduces, it's tricky and not a panacea.

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.

Yeah 'auto' really messed ET up. The consequences the design would have on ET was not on the committees mind. I guess there was no one there in the committee who was that deep into high performance computing of this kind.

> 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'.

> expression templates bring no benefits to 99% of linear algebra computations

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.

One nifty trick to avoid writing new or delete even when you need manual heap allocation is to always use std::make_unique (or std::make_shared, as needed) instead. That way you're forced to spell out any transfer of ownership as it is happening, and so someone's always be sure to delete the object. You also dodge the bullet with indeterminate ordering of new's and constructors, that might result in memory leaks if one of the constructors throws.

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.

Great tip, thanks for the clear example :)
Yep. I use C++ and don't bother with optimization because it's fast enough on my slowest machines (5-6 years old).
I use both C++ and C# a lot these days, and like both. I am more comfortable with C++ so memory management is usually not a problem, interestingly I find that it is in C# that it is sometimes annoying to wonder if the objects you manipulate are references or values, but you end up getting the gist of it after a bit of practice.

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.

Not all smart pointers are reference counting - unique_ptr is not, and it has zero overhead compared to a raw pointer. Sometimes you just need to heap allocate, or, say, shove a bunch of objects of different derived types in a same container.
In fact, in my tests, unique_ptr with make_unique was faster than a raw pointer with new.
Yes sorry, I conflated smart pointers and shared_ptr. I rarely use the other kinds of smart pointers.
(comment deleted)
I agree, C++ is very versatile in terms of supporting both low-level and high-level coding styles, and the fine-grained interleaving of the two. There is indeed a ridiculous, and probably unnecessary, amount of complexity available in the language, but if you're writing new code there's no problem with sticking to a reasonable subset and ignoring the esoteric stuff. Of course you may not be able to avoid it if you have to deal with other people's code.

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...

Applications that employ computer vision, machine learning or even deep learning, especially if they are trying to run models on mobile devices have to write their codebases on an efficient high-performance language otherwise, their apps will be just too computationally expensive (too slow).

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.

Perhaps this is true for your applications (and why no option 3: developer knows C++ well and is efficient in it?), but not all of us can goof around with GC's and a complete lack of performance oriented features.

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 not all of us can goof around with GC's and a complete lack of performance oriented features.

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 fine with IDEs generating boilerplate.

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.

Use java, C#, or Go.

You get the high productivity AND the performance together.

(Also, the tooling is so much better).

> Spend 90% of the time writing performance-neutral code in a modern high-productivity language

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 key here is to distinguish from the pedantic snobs from good developers.

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.

> 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.

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?

> spending 90% of your time fighting said language

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.

If only it was a reliability/robustness/simplicity beast instead. Instead, it's evolved to be this atrocity of unspeakable proportions that continues to wreak havoc (security bugs, technical debt, complexity) pretty much everywhere it's used. When the best C++ developers on the planet can not do better than that, you know we have a problem.

It remains to be seen if the alternatives that are now slowly emerging will replace it. My personal estimation based on all the expert C++ programmers I know is that this will not happen through people abandoning C++ since it sits so high on the abstract ladder of complexity that for one to even begin to question its foundations (and all the time/years/decades one has sank into becoming an expert C++ programmer) will be a Tower-of-Babel moment. On top of that you have the (quite illusionary) tremendous feeling of empowerment one gets when using something that required such an investment to master.

JWZ once wrote (on an unrelated topic):

“No matter how hard you try, you can’t make a racehorse out of a pig. You can, however, make a faster pig.”

This seems fitting to the way C++ is evolving.

Should quote this one as well:

   (3)  With sufficient thrust, pigs fly just fine. However, this is
        not necessarily a good idea. It is hard to be sure where they
        are going to land, and it could be dangerous sitting under them
        as they fly overhead.
https://tools.ietf.org/html/rfc1925
Languages like C++ only get replaced when OS vendors force developers to use something else.

That is how both C and C++ pushed other alternatives away from the market.

So right now UNIX vendors don't care for alternatives, Apple is pushing Swift, Google and Microsoft are on the improving C++ with Go and .NET Native for the use cases one doesn't need C++ level features and a few embedded OS vendors have Ads, Basic and Pascal compilers alongside their C and C++ compilers. Finally console vendors are still only having C and C++ on their SDKs.

Then there is Rust and D, none of them being pushed in OS SDKs. However Mozilla is actually increasing the amount of Rust code being used in Firefox. So something positive there.

So it doesn't look like a clear winner exists for the near future.

It's easy to be blinded by the size of that investment, both technical and emotional. Still enjoy the new C++ though.
Does anyone have a good recommendation for a cannonical reference on Modern C++14?

I have experience with C++ but its the old kind with raw pointers.

My usual starting point is Herb Sutter's books, with Bjarne's bible handy when I need it. I'm not sure where to start with modern C++.

If you have experience with C++ already then go with "Effective Modern C++" by Scott Meyers.
I'd second "Effective Modern C++" by Scott Meyers and point out that while the book was written for C++11 most of the new features of C++14 extend and polish off those existing features. Now const expressions are more flexible and powerful, template meta programming is more powerful and in general they fix a bunch of smaller issues with the standard library. So you'll learn those things as you get to them.
Check all of Herb Sutter's CppCon videos from 2015-2016, especially this one: https://www.youtube.com/watch?v=JfmTagWcqoE

He does a great job of walking through the thought process of deciding which of the options makes sense in different situations.

Wow the author is giving a two day course soon in C++ for up to 40 people that is costing 2500 per person. Is that a big industry? Do many people on HN run programming workshops? I would be interested in chatting if you do. Email on my profile
C++ training is a big industry, yes. Not sure about other languages.
Part of the problem with the new move semantics for me, and C++ in general, is figuring out what the compiler is going to do and their interaction with the return value optimization. Sometimes I can't figure out if the RVO or the move constructor is going to be used.

C++ has definitely blown it's complexity budget and the problem is getting worse. I don't really see a way out of it either. I sure hope Rust starts to gel or a better C++ comes along, because I don't think I can keep pace with C++ too much longer.

> I don't really see a way out of it either.

Stop using C++ unless forced to do so.

That's the only way to leave the complexity minefield.

Heh, I think this isn't complex as much as making something complex simpler. Just super awkard because it's caked in afterwords. I'm happy to see some good examples of move constructors and the like.
If you're using clang you can just turn on -Wpessimizing-move and the compiler will tell you when you have a std::move call that is inhibiting RVO.

The rule of thumb is don't use std::move on a return value unless you are returning a move-only type (which forces you to do so).

> The rule of thumb is don't use std::move on a return value unless you are returning a move-only type (which forces you to do so).

RVO will be automatically applied if the returned variable is local to a function and of the same type as the function's return type. That's all that the standard allows.

RVO is not applied to function arguments, regardless of whether or not they're rvalue references.

So your rule of thumb breaks for cases such as these (X is a movable and copyable type):

    X f(X&& x) {
      return x;  // x is copied, not moved
    }
In order to get x moved into the return location, you would need to do move it explicitly.

Source: Effective Modern C++ by Meyers.

I reacted like that at first, and then later on I realized that in order to use containers on classes, I either have to use pointers and manual allocation or accept wasteful copies. The move semantics simply allows to mimic a copy without actually doing it, when you plan to discard your initial object. A common situation when populating a container with class instances.

I had a long back and forth relationship with C++. I have the same criticism everyone has: it is hard to use, hard to understand, there are tons of possibilities and concepts that overlap each others, many ways to do the same things that are sometimes incompatible...

But reading more about it, getting updated on the more recent iterations, I understood that C++ is more than a programming language: it is actually a quest. It is a search for a language that can both manipulate high-level concepts and also descend to the very low level required for software living close to the hardware (you'll do mostly C there but a few C++ constructs can be surprisingly useful)

Most other languages will specialize in a niche. C++ developers want to be able to express the most abstract concepts in the best possible implementation. That's a quest for the grail. Few other languages dare to dream that big. And of course C++ stumbled sometime along the way, still limping from backward compatibility, but it is so far the most advanced in this quest.

> Part of the problem with the new move semantics for me, and C++ in general, is figuring out what the compiler is going to do and their interaction with the return value optimization.

It's very simple:

If you return something that can be moved, it will be moved, always.

[N]RVO is something that may or may not happen. However, if you don't write code that does crazy things (like auto_ptr style copy constructor that actually moves), at most, it'll result in an extra copy/move that is redundant and won't change semantics. So you should never care about whether it happens or not.

> If you return something that can be moved, it will be moved, always.

How about the example I gave in my reply to your sibling? https://news.ycombinator.com/item?id=12578621

Good point. Let me correct that: if you return something that doesn't have a name, or is a local variable in the function you're returning from, it will always be moved. Usually it amounts to the same thing, except when non-const lvalue or rvalue references are involved (but it's fairly rare to return via a non-const reference).
So when I said RVO, I mean't NRVO too (but not from an argument passed in).
Wouldn't looking at the generated assembly work? If performance matters to the level of caring about RVO in a specific instance, looking at the assembly can't be avoided.

I agree it's inconvenient, ideally a compiler should do the right thing, but there are no such smart compilers yet :)

(comment deleted)
> Part of the problem ... is figuring out what the compiler is going to do

Not meant to offend you, but that should not be any of your business :-)

I disagree. C++ has never had a clearly defined abstract machine, or a common virtual machine. Knowledge of what is actually going to happen is necessary if one needs high performance in anything other than trivial circumstances, and knowledge of what the compiler is going to do is part of that. In C++, there are times when it very much is our business to know how our tools work and what they're going to do.

There are languages in which not knowing what the tools are going to do is never a concern. C++ isn't one of those languages.

I've been several times in the situation of having to push the language to its limits for performance, and gcc documentation was of great help. But, maybe it's a nice property of gcc, maybe it's a nice property of gcc's documentation, I never felt I had to learn some dirty trick for gcc in particular. I always understood the principle behind the implementation, and the code I wrote as a consequence was better in principle, too.

Summing up, knowing your tool is a must, but if you find yourself doing its job, than it's time to at least question your choice.

When micro- and nanoseconds matter, it is your job to dig into the ugliness of the generated code, if it is C/C++/Rust or even if it is Java.
I hope everyone read down to the end to see this factoid about Leor Zolman, the author: In 1979, Leor wrote the BD Software C Compiler ("BDS C"), the first 8080/Z80 native-code C compiler both developed on and targeted for personal computers.

Leor's C compiler didn't support floating point, but it compiled programs like a bat out of hell on a 4 MHz 8-bit Z80 microprocessor with 64 KB of memory.

Yes, it was C, running well, on a 4 MHz micro with 65,536 bytes of memory. Truly remarkable for its time.

https://en.wikipedia.org/wiki/BDS_C

As a rust programmer. I've been very happy to see c++ talking about the things that are core to how we get stuff done in rust. As a c++ programmer, do you ever wish that auto wasn't const by default? Couldn't you have auto (let) and volatile (mut) :) .

I'm pretty sure this wouldn't be too hard to implement.

Move semantics are a good idea, but C++ doesn't have the compiler machinery to catch when you move something away and then try to reference it. Move semantics really need a borrow checker, like Rust. In C++, you have to be very, very careful not to do things like accidentally move something passed to you as a non-const ref.
(comment deleted)
In C++, you can't "accidentally" move something that's a non-const ref, since a move from one would require explicit use of std::move.

The only things which can be moved accidentally are rvalues and rvalue references. But for rvalues, that doesn't hurt anything; and for rvalue references, the only reason why you'd use one is if you intended to play tricks with move semantics, which implies that you know what you're doing.

Basically if you write C++ as if move semantics didn't exist (i.e. never use rvalue references or std::move), you won't shoot yourself in the foot by those means. Of course, this also means that compiler assumptions are fairly pessimistic, so many places that could be a move aren't, because the move has to be explicit.

(comment deleted)
I think most of these educated comments about memory safety wouldn't be here without the hindsight provided by Rust. So thanks, Rust!
I'm over-generalising, but: if you want performance, don't create a huge soup of tiny objects on the heap pointing to each other as can often be seen in 'smart-pointer-heavy' C++ projects. Creating and destroying objects on the heap is expensive, and you'll end up pointer-chasing with cache-misses all over place.

Unfortunately smart-pointers are only useful with this 'every object lives isolated on the heap' approach, since alloc-init and cleanup-free are merged into the same operation. Of course there's ways around that by writing custom allocators, but this just adds more complexity when often granular objects with their own lifetime and ownership aren't needed in the first place.

Smart-pointers are a convenience feature to not having to think about object-lifetime, and in the case of shared-pointers, also to not think about ownership, they're not about performance. If you want performance, don't allocate single objects on the heap, and spend some time thinking about data layout in memory.

The vector is initialized with 500000 widget objects. The size and capacity of the vector are equal. The following while loop is never executed.

>> while (vw.size() < vw.capacity()) >> vw.push_back(Widget());

In the situation that the vector is not initialized with elements and populated in a loop the time for a single push_back is equal. I think this is because there is no need for a resize.

>> vector<Widget> vw; >> for(int i = 0; i < 500000; i++) { >> vw.push_back(Widget()); >> }

Does this illustrate how fast a vector of objects can be copied/resized using move semantics?