61 comments

[ 3.2 ms ] story [ 121 ms ] thread
I've been using a lot of these new features in XCode 4 and my code is already much cleaner and more readable. Modern C++ really is a pleasure to use and I love writing code that's this high level but still smoking fast.
I wouldn't exactly call C++ "high level", but I agree that the new features make it nicer to use when you have to.
Are you kidding? The syntax is still inscrutable and the template system is shit. It's all slightly less shitty than it used to be:

binder2nd< greater<int> > x = bind2nd( greater<int>(), 42 );

but IMHO C++ jumped the shark in 1996 or so.

While people are ranting about C++'s warts I'm having a great time writing performant but clean DSP code and getting paid for it. I don't give a shit if there's some imaginary pristine alternative.
I used to write high-performance embedded C++ code (and get paid for it). I'd still rather use a JVM language for the general logic and drop down to low-level C/assembler for the really performance-critical stuff.

The only thing I'd use C++ for these days is where it's minimal runtime support requirements come in handy (e.g. runtimes for other languages).

Sure. I'd use Scala if the situation allows. But these days I'm doing realtime DSP & OpenGL stuff and I'm very happy I don't have to do this in a language as low-level as C.
That looks surprisingly good, and might even improve new projects which have to use C++. How long until compilers support all this?
I'd say by the middle of next year you should be able to use the a lot of the features portably.

GCC: http://gcc.gnu.org/projects/cxx0x.html

Clang: http://clang.llvm.org/cxx_status.html (currently down)

MSVC: http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.a...

Notably lacking in current production versions:

1. explicit overrides in GCC

2. lambdas in Clang

3. Variadic templates, range loop in MSVC

Sadly, vendor supplied toolchains for other systems are going to lag far behind. It is not always an option to use GCC/Clang on these boxes. Compilers like Oracle Studio CC, IBM xlC, and HP aCC, etc. So this is not really the definition of "portable" I'm used to. I know I can take any C++98 code and reliably build on any platform/toolchain. It will be a long time before I can say the same about C++11. Even C99 isn't truly "portable" because Microsoft refuses to add all C99 features to MSVC.
To be honest, I can understand why, as C++98 and C99 are (aside from some trivial features) mostly different and incompatible extensions to C90.
Microsoft don't ship a C compiler

Adding C99 features to their C++ compiler would be nice be probably not cost effective for them

Sure they do, it's bundled with their C++ compiler. cl.exe will happily turn on C90 mode and compile files as C, not C++, if you supply the /TC option or your source files have the .c extension.
We have just started using some C++11 features, and with new features come new traps and potential complexities, as you'd expect with C++.

A few examples:

1. Implicit lambdas ("[=]") capture "this" pointer for data members, if you capture a shared pointer and let the lambda object leave the scope, the shared pointer won't be copied (http://stackoverflow.com/q/7764564/23643), which may create unpleasant results, like code blowing up in your face.

2. Overuse of "auto" (not exclusive to C++) may make the code harder to read, especially if storing a result returned from a function. You can't tell if the return value is a smart pointer or a raw pointer, value or a reference, so you can't figure ownership semantics just by looking at the local code.

3. Move constructors/assignment operators are a bit of a black magic, especially with regard to suppressing default implementation (http://mmocny.wordpress.com/2010/12/09/implicit-move-wont-go) and exception safety (http://cpp-next.com/archive/2009/10/exceptionally-moving). I would refrain from using it, until I fully understand it.

With that said, most of C++11 is a welcome change and makes C++ more pleasant to write, but, as always, you have to have your guard up and use new features judiciously.

Good points. Re. 2, we decided to only use auto for iterators, which eradicated a large amount of boilerplate without being too confusing.
Wow that's interesting. Never realized lambdas capture "this" and not the actual data members itself!

That also means you can get in big trouble if the object that got captured was deleted, ouch.

This is why Lisp never had manual allocation. It's a booby trap for a new generation, kind of like the collision between heap allocation and exceptions was in our youth.
C++ always seems to lead to traps. In point #2, you're obviously right about the micro-coding/readability point. But the premise is kind of scary: you're dealing with a code base where the ownership semantics of an object are captured only by a function return type? Yikes. That's a framework issue: if everyone working on the source isn't 100% sure what that return means, you're guaranteed to get bugs with or without auto. Non-trivial ownership/allocation/copy semantics (i.e. anything other than "on the stack" or "operator new") are deep C++ voodoo, not something you worry about in passing as a readability thing.
At least you can express ownership semantics in C++ code, using constructs like `unique_ptr`, `scoped_ptr`, and `shared_ptr`. In a garbage collected language, nothing expresses ownership, and so while you not be at risk of memory leaks, you're still vulnerable to all the other bugs that come with aliasing mutable objects.

The "framework issue" you describe is solved precisely by specifying ownership semantics in terms of smart pointer return values. The way everyone becomes "100% sure what that return means" is by whether it returns a `unique_ptr` or a `shared_ptr`. The OP is simply complaining that `auto` takes away that local information on ownership.

`operator new()` is only trivial by virtue of not specifying anything at all about ownership. `scoped_ptr` and `shared_ptr` and `unique_ptr` are each specifying different, more restricted, but still trivial ownership semantics. They aren't "deep C++ voodoo" in the least. Your post smacks of someone who doesn't actually write C++ for a living.

> In a garbage collected language, nothing expresses ownership, and so while you not be at risk of memory leaks, you're still vulnerable to all the other bugs that come with aliasing mutable objects.

You are conflating required language features with proper API design and documentation. I don't need '(unique|scoped|shared)_ptr' to refrain from aliasing mutable types, or to document my code properly on the rare occasion when I am required to.

I wish C++11 added syntactic sugar for the smart pointer classes like scoped_ptr, shared_ptr, and unique_ptr. I would rather see new operators (like Microsoft C++/CLI's ^ and %) than even more templated code. Compare something like:

  int^ foo(int% bar);
versus:

  std::unique_ptr<int> foo(std::shared_ptr<int> bar);
Smart pointers have long been available in boost and are required for writing safe code, but they make code unreadable (even after typedefs).
I don't think introducing more syntax to the language for a feature that ultimately is a library solution, is a sustainable way to go.

But if you really need a compact notation for smart pointers, you'll get pretty close by using template aliases, e.g.

    template <typename T> using up = std::unique_ptr<T>;
    template <typename T> using sp = std::shared_ptr<T>;
    // ...
    up<int> foo(sp<int> bar) {
        return bar ? up<int>(new int(*bar)) : nullptr;
    }
Oh C++11 has template typedefs? Time to migrate my project.
Coming from a dynamically-typed language perspective, (2) seems like incredibly not a big deal. If you're unclear on the return type, just look at the declaration of the function that's being called.

Compare this to, e.g., Python, where you have to look at the actual definition of the function being called (and maybe eve n the definition of functions it calls, and so on), which is way worse.

Dynamically typed languages respond to the problem in (2) by keeping typing simple. C++'s proliferation of multiple ways to do the same thing (regular pointer, smart pointer, reference, etc) and multiplicity of ownership rule (no GC) cuts across that.
It's certainly true that dynamic, high-level languages save you from having to worry about ownership-semantics, but you still care a lot about the return-type (e.g., am I getting back an int, a str, a set, a dict, a simple list, a generator, &c &c &c)!

You care about this; either it must be obvious (e.g., fairly obvious with a function like len()), or you have to look it up in the documentation or comments of the function. If it's not documented, you have to try to figure it out from the function body, which is often non-trivial. This is why you'll typically see what amounts informal type annotations in the comments of functions of mature code.

Despite all this, many people feel that the flexibility of not having to specify types is a net-win.

C++ "auto" adds some of the convenience, but at a much lower cost in readibility, since when you see something like

  auto x = foo.gimmeBar();
you can find out the type of x immediately just by looking at the definition of gimmeBar() (with IDE support, this could even be zero-effort (i.e., it could show a little grey popup bubble with the deduced type); even with something old-fashioned like emacs+ctags, it's just a hop-skip-and-a-jump away).

  *
By the way, regarding ownership-semantics, it's true they could be a massive PITA pre-C++11, and (sort of analogously to the what-are-the-types-in-a-dynamic-language situation) you'd often have to fall back to relying on comments to know what was going on. If they're weren't comments and you were getting back a raw pointer, well...that could be rough.

This is a problem that can be fixed with library support (for smart pointers) and disciplined coding -- i.e., using the smart pointers. For a good example of this working really well in practice, see Objective-C.

C++11 helps because it gives us built-in library support, and will hopefully provide the community motivation for the convention/discipline of actually using the libs.

(This pointer-ownership stuff is pretty much orthogonal to auto, though, IMO.)

> but you still care a lot about the return-type (e.g., am I getting back an int, a str, a set, a dict, a simple list, a generator,

You do, but convention gives you a lot more context. If, by the nature of the function, I know I'm expecting a list, I know I'm going to get a list. I'm not going to get a pointer to a list versus a reference to a list versus a smart pointer to a list versus an iterator pointing into a list, etc.

Compare the C++ STL to Common Lisp's standard library. A given STL function might take or return an iterator, an iterator pointing to a pair, a pointer, or a value. In the Common Lisp standard library, something is going to return either a value or a collection and its obvious from context which it is going to do.

I think you're sugar-coating a bit -- at least for Python, any function that might return a list could also reasonably return a generator, and might even return a tuple or a set.

I can't speak to common lisp, but I have a fair amount of experience with Python and with the STL (and other stuff, but those two most of all), and I've found the STL to be one of best-designed, most predictable, and easy to use libraries out there. YMMV, of course.

(BTW, an iterator is just a generalization of the concept of a pointer; i.e., a pointer is an iterator.)

"It's certainly true that dynamic, high-level languages save you from having to worry about ownership-semantics"

What languages are you thinking about? In most languages I can think of as being "dynamic, high-level", state is mutable. So, if a method on an object returns a collection, and the caller wants to keep the result around, the programmer will have to somehow know whether the collection that the method returned was freshly allocated or internal to the object, whether it is modifiable, how long the content of the collection is guaranteed to be stay constant (e.g: what happens when I append to a list from a loop that iterates over it?), etc.

Similarly, if one passes an object to say a constructor, one should know whether the constructed object holds a reference to that object, thus sort-of claiming ownership.

It is true, though, that whenever I think about such issues, I am amazed that these things so rarely lead to problems. Apparently, all programmers share a single mental model for the 'natural' ownership policies.

Enter Clojure with its first-class persistent + immutable collections!
Yup, you're right -- I was just talking about deallocation, but more than that matters in a mainstream, side-effecty language, as you point out.
2. I'm sure IDEs/editors will start to implement features to grok object output and/or do static analysis to show you what type auto implies. It isn't necessarily easy but it seems like the feature everyone would want. You could even produce a "de-auto" button that would toggle between auto and the substituted real type for readability.

3. Move operations are completely welcome as far as I'm concerned. I recently hit some performance problems which beg for move or emplace semantics. Most people don't realize the amount of copies they are making when using STL containers. These semantics will allow code to ensure only one instance of a value type is ever created when, say, inserting into a map. I feel like some people resort to going back to hand-rolled data structures or raw pointers when performance is critical because the current containers are missing this.

In templates, it is almost impossible to remove the 'auto'. Even something simple like:

    template<typename T>
    void f(T t1, T t2)
    { auto sum = t1 + t2; }
There is nothing to replace auto with. Similar things apply to most templated functions. Of course outside of templates things get much easier, but I wouldn't expect to see quite so many autos.
True, but at least screw-ups will be noticed at compile time. And how would you even write this snippet, pre auto?!

(I think you'd either just assume t1 + t2 was T, or you'd have to go thru less-general, even-less-clear, gymnastics (like having some structure defined like get_sum_type<T,T>::thetype sum = t1 + t2).)

>And how would you even write this snippet, pre auto?!

  typeof(t1 + t2) sum = t1 + t2;
That's cheating! I meant how would you write it pre-C++11 extensions (and also not using the earlier GCC typeof extension). decltype is just a slightly different form of auto. :)
Exactly right, in C++03 this kind of stuff was really horrible.

In general people (the STL, and all Maths code I've ever read), will just assume that the type is T. For things like iterators, you have to do things like:

    typename std::vector<T>::const_iterator it = v.begin();
(That typename has to be there, for stupid C++ reasons). Being able to reduce that to:

    auto it = v.begin();
Is probably the most visible advantages to C++11 is one of the most obvious improvements for many programers.
This c++ is very different from the c++ I remember. I'd like to learn more about modern c++ style and idioms. Can anyone recommend a good text?
Herb Sutter, whose blog post is the subject of this thread, has written a few books. I enjoyed C++ Coding Standards in particular.
Does anyone have a recommendation for an intro book for experienced programmers (not C++ programmers) that teaches C++ using a "good" C++11 style?
Newer books likely have not been written yet. C++ is pretty standardized and has been for a while. I suggest "Accelerated C++" if you want to learn idiomatic C++, and then you'll understand (for the most part) how the new 2011 additions work.
Herb Sutter and Andrei Alexandrescu's C++ Coding Standards: 101 Rules, Guidelines, and Best Practices is a great, little book. It does not teach C++ (or C++11) and seems a little light at first, but it provides good advice for "modern" (21st century) C++ and gotchas from "good practices" from many 20th century C++ books.

Herb Sutter's website has more details about the book and some articles with excerpts: http://www.gotw.ca/publications/c++cs.htm

Auto everywhere is pretty dangerous despite what Mr. Sutter says. Many C++ libraries rely on type conversion (in high performance math it's very common however you can find it as close as the STL's vector<bool>). Whenever you use your own memory allocator and/or care about performance (and this is whenever you are writing high performance/high reliability code) library smart pointers are pretty useless too. Other things are not as controversial as these two, but don't really define the modern C++. Herb should have read his friend's, Anderi Alexandrescu's, book from ten years ago to get a hint of what modern C++ looks like :) (I know he has read it. I guess he is just trying to pimp C++ to Java/PHP/Python/etc developers).
He's not advocating "auto everywhere", he just said it will make the STL algorithms easier to use (which it will). The other benefit of auto is for generic code (code in templates) where you might not necessarily know what the return type is, or at least where the return type is going to be very complicated to specify. (This last is the same reason C# added var at the same time as they added LINQ - the return types of LINQ expressions would be difficult to specify manually.)
He says "Use C++11 auto wherever possible." Nobody argues auto and decltype is necessary for generic code but this not what Sutter's writing about. In fact he does not have a single word like "generic" or "template" in his entire article. Which alone should be a good indication this has nothing to do with the modern C++ style unlike Alexandrescu's book with the similar name.
The only issue I have ran into is that some of these newer features are not yet available in compilers that are shipping with OS's we are deploying on, so I HAVE to continue writing to C++98 since installing a new compiler/libraries is a no go (if it is not vendor supported, can't do it, thank you US gov't rules!).

It will take some time for the compilers and their libc++ libraries to move forward and have older versions of OS's replaced by ones that do support it.

That being said, wherever I can (in personal projects) I will slowly start adapting and using the new features...

When I read articles like this I think of a quote from Carmack.

"The Escalation programmers come from a completely different background, and the codebase is all STL this, boost that, fill-up-the-property list, dispatch the event, and delegate that. I had been harboring some suspicions that our big codebases might benefit from the application of some more of the various “modern” C++ design patterns, despite seeing other large game codebases suffer under them. I have since recanted that suspicion." (http://www.bethblog.com/index.php/2010/10/29/john-carmack-di...)

The rage in the game dev community these days is all about highly cache aware Data Oriented Design (DOD) over OOP. Curiously there aren't a lot of DOD discussions or articles on HN.

I read the link, but there's no data there... just the high-level conclusion. It would be interesting to know which features or approaches he found problematic. Otherwise it's just "doesn't work for me".
> Curiously there aren't a lot of DOD discussions or articles on HN.

How much is there to say, really, once you've got the basic idea?

OOP has always emphasized single objects. Part of that emphasis is that data is grouped by object rather than by field.

For some applications, this style can work well. GUIs are the canonical example.

However, if speed really matters, a focus on individual objects is rarely helpful, because it doesn't support typical data access patterns where algorithms probably want to work with one field for many objects rather than many fields for the same object.

This doesn't just matter for games. If you're working on any kind of mathematical code using large matrices, something as simple as choosing row-major vs. column-major ordering appropriately can make a very noticeable difference to performance on modern hardware with several layers of cache.

One of the nicer things about C++ is that while it supports OOP (at least in the variation that has become most popular) it doesn't try to shoe-horn everything into being an object and is quite happy for you to worry about your own memory layouts if you want to.

Once you've understood that basic point, there isn't really much else to know about Data Oriented Design. It seems like one of those ideas that has been capitalised to make it seem more important than it really is. If no-one had popularised OOP, despite its fundamental flaws in this respect, perhaps we would have just called the DOD approach "common sense"...

I think the types of DOD being discussed in the great twitter wars are far more complex than common sense.

http://macton.smugmug.com/gallery/8936708_T6zQX/1/593426709_...

I remember when that first did the rounds a few years ago...

The thing is, while it's interesting seeing the potential trouble spots Acton highlighted, he didn't actually show a better way of doing any of it. As someone who has also done his fair share of low-level/high-performance programming in C++, I would take some convincing that the result isn't going to be somewhat faster but effectively write-only code. That might be OK if you know your target platforms well enough to count on those micro-optimisations not being premature and you know no-one is going to care about maintaining your code for very long, which is not entirely unrealistic in the games industry but doesn't apply to much else.

However, I think a lot of the ideas on those "slides" go beyond Data Oriented Design as the term has come to be used, and into general low-level, architecture-specific optimisations. There's nothing wrong with exploring the latter, of course, but I'm not sure it's helpful to conflate very fine-grained, platform-specific, relatively localised adjustments with a high-level view of how to group multiple properties on a set of objects in memory, or with the more general principle of transforming your data in advance into some format that lends itself to the kind of processing you want to do.

(None of which is to say that articles on how to do that sort of low-level optimisation on popular platforms wouldn't be interesting. I'm just suggesting that they would probably have to go some way beyond what is commonly called DOD today before they were likely to get interesting.)

I don't know a lot of C++, but the examples that demonstrate how C++11 compares to C++98 look like a vast improvement. Until you come down to this monster:

    auto i = find_if( begin(v), end(v), [=](int i) { return i > x && i < y; } );
This is the improved version by the way, but I'm not sure if a loop wouldn't be more readable for such a straight-forward problem. Plus I can't help but think that C++ will probably never win a beauty contest.
I don't see what the problem is here, this is very readable to me. You want to find between the beginning and end of a list called v, for a value between y < i < x. Admittedly the condition in the lambda could be better written, and the results should probably be called something other than i, but it's perfectly clear what's going on here.

Let's look at the equivalent C++98 loop:

   int i = -1;
   for(std::vector<int>::iterator iter = v.begin(); iter != v.end(); ++iter)
   {
      if(*iter > x && *iter < y)
      {
         i = *iter;
         break;
      }
   }
It's not only much more verbose, but the types need to be expressly written, and what's going on isn't immediately obvious from the function name.

No, C++ will never win a beauty contest (I'd vote for D or Haskell personally), but it's great for expressibility, and the new C++11 changes make things more concise.

Agree completely.

When you compare the new code to the C++98 one, it looks like a great improvement. But isn't that because C++98 simply sucked even more? As you say, when you compare it to languages like Haskell, even the new C++11 leaves something to be desired.

This almost looks like ruby. For a language as close to the metal as C++ I think that's pretty impressive.
I think the lambdas are the more useful the more complicated the function is. Simple asynchronous tasks might be one such example. This one, however, still is a pretty common use case, so introducing a function object type for it wouldn't hurt readability. Something like:

    template <typename A, typename B>
    struct between_ {
        A a; B b;
        between_(A const & a, B const & b) : a(a), b(b) {}
        template <typename T>
        bool operator()(T const & t) const { return a < t && t < b; }
    };
    template <typename A, typename B>
    between_<A, B> between(A const & a, B const & b) {
        return between_<A, B>(a, b);
    }
    // ...
    auto i = find_if(begin(v), end(v), between(x, y));
Verbose? Admittedly. Except maybe for the point of use, i.e. the last line. Which, I think, helps a lot anyway.
Best joke is at the end: "These features [...] make modern C++ the clean, and safe, and fast language that our industry will continue relying on heavily for years to come."
Will C++ get properties some day (similar to C# properties)?