Interview: C++–A Language for Modern Times

imageLast week I spent 30 minutes with interviewer Robert Hess to talk about the differences between managed and native languages, and why modern C++ is clean, safe, and fast – as “clean and safe” as any other modern language, and still the king of “fast.”

The interview just went live today on Channel 9. Here’s the blurb from the site:

C++: A Language for Modern Times

C++ has been around for what seems like forever. It might seem like it’s taken a back seat to languages that provide better application isolation and better development throughput, but in truth it remains one of the most widely used languages in the world. In order to gain some better insights on how C++ measures up in a "managed" world, I’ve invited Herb Sutter, Program Manager for Visual Studio, to explain how C++ has evolved and how it remains a great choice for many modern day development tasks.

I’ve said some of these things before, but it’s important to get the word out – modern C++11 is not your daddy’s C++, and as people return more and more to C++, we have some work still to do to help people unlearn some negative things that are no longer true (and in some cases never were true) about C++ as we know and use it today.

21 thoughts on “Interview: C++–A Language for Modern Times

  1. But without XP SP3 targeting support in VS11, many of us will have to write not-so-modern C++ for at least an year or two. Sad.

  2. You say in C++98 you’d have to write “flip(const string& s)”, but IMO that’s false, as you’re modifying s in the function.

  3. @Olaf: The typical C++98 advice is to take the expensive-to-copy object by const& and then make a local copy inside the function:

        // Option 1: Typical C++98 style
        string flip( const string& s ) {
            string tmp = s;
            reverse( tmp.begin(), tmp.end() );
            return tmp;
        }
    
        // calling code
        string s = "xyzzy";
        string reverse = flip( s );    // nice and natural calling code for a value-like type

    and if you wanted to avoid the copy (without relying on RVO “maybe” kicking in) you might resort to an out parameter instead of a natural return value:

        // Option 2: Or use an out parameter
        void flip( const string& src, string& dst ) {
            dst = src;
            reverse( dst.begin(), dst.end() );
        }
    
        // calling code
        string s = "xyzzy";
        string reverse;
        flip( s, reverse );    // kind of odd, but works

    or something else, such as in-place semantics which moves the burden to every caller to make a copy when he doesn’t want to modify the original:

        // Option 3: Or use in-place semantics
        void flip( string& s ) {
            reverse( s.begin(), s.end() );
        }
    
        // calling code
        string s = "xyzzy";
        string reverse = s;
        flip( reverse );    // also a little odd, but works

    and so on.

    Of course, you could also have already written it the C++11 way, like this, taking a parameter by value on the grounds that you’re going to modify it anyway:

        // Option 4 (C++11 recommended style): Clean, safe, *and* fast
        string flip( string s ) {
            reverse( s.begin(), s.end() );
            return s;
        }
    
        // calling code
        string s = "xyzzy";
        string reverse = flip( s );    // natural, and guaranteed zero copying of temporary objects

    but in C++98 this style was considered “odd” and “probably too cute,” and the only person I can recall who advocated for this style was Andrei Alexandrescu. Now, of course, move semantics changes everything, because it gives a real performance incentive to write pass-by-value and return-by-value along with a guarantee that move will kick in, no RVO required (although RVO can still happen, but is way less important because it eliminates a move instead of a deep copy).

  4. @Herbert:
    > and then make a local copy inside the function:
    That’s just plain silly.

    > Now, of course, move semantics changes everything, because it gives a real performance incentive to write pass-by-value and return-by-value
    I agree on return-by-value, but how does it affect/change pass-by-value? Both with and without move semantics I’d pass by value if the value is going to be modified.

    IMO const std::string& is hardly ever really the right argument type as it might force the caller to construct an unnecessary std::string. Not all strings are std::string. The right type would be a ptr range / string ref (const char* pair). Unfortunately, C++ still doesn’t these.

  5. @Herb: Regarding option 4, didn’t you mean:

        string flip( string s ) {
            reverse( s.begin(), s.end() );
            return move(s); // turning s into an rvalue so to it would bind with string::string(string&&) and omit copying
        }
    

    @Olaf:
    > I agree on return-by-value, but how does it affect/change pass-by-value?

    See my example code above. So you could reuse and return resource owned by s to callers of flip().

  6. > See my example code above. So you could reuse and return resource owned by s to callers of flip().
    But you don’t need move semantics for flip(string s) to be a good idea.

  7. @Kenneth Ho, I don’t think Herb meant to return move(s); that’s forcing the compiler into move semantics. It’s making you /think/ about needing the move there. In the latest standard, as I understand it, the compiler will opt for std::string’s move constructor when returning by value. At best, the compiler will inline the call and the copy/move will be elided. At worst case, the new standard will ensure move semantics are used. Explicitly stating move() removes the compiler’s choice for further optimization, it makes you think more, and clutters the code.

  8. @Kenneth: You don’t want to write move(s) there for the reasons bkuhns mentioned — it doesn’t help (the language already knows to treat a return expression as an rvalue so it can automatically be moved from) and might hurt (slightly if it disables RVO, which is no longer as important an optimization because it only elides a move instead of a copy, but still is a useful incremental optimization).

    @Olaf: I understand, and others such as Andrei have pointed this out in the past, but as I mentioned the usual C++98 guidance was nevertheless to pass by const&. The rule “pass an expensive object by value if you’re going to copy it anyway” has more upside and less downside in C++11 (thanks to move semantics), so it’s definitely time to adopt it now.

  9. @Herb: You mentioned two things I don’t fully understand in your talk.

    1) Why would C++ be a better choice for very large scale applications than NET/Java? I mean the zero abstraction penalty (which is more a JIT compiler issue and not intrinsically hardwired into C#) , okay, but besides that?

    2) C++ really only has a few language features which actually let you write faster code in theory. In practice, JIT compilers are just not good enough, yet, to fully optimize on C++ pace and that’s one of the main reasons why C++ excels at efficiency. This yields two questions. Why does Microsoft not put effort into a static C++ like compiler for C#/NET, say in manner of NGen, so that C# actually has even the slightest chance of being competitive with C++? Otherwise, saying C++ is more efficient than C# is not a theoretical issue, but caused by bad JIT compilers. If we had such a C++ paced JIT compiler (the recently deceased “Phoenix” for one), couldn’t we easily bring C# on C++ pace by just adding a few important language features like constness, immutability, restricted pointers, scoped classes and stuff like that?

    All this wondering is just because I find it rather weird that Microsoft is kinda giving up on NET performance wise, while in theory they would only need to create a good static compiler and a few new language features to make it kick-ass again. Also the C++.AMP could be named C#.AMP without too much effort…

  10. Hi, first of all really great interview, and short. This will be the reference I give to people that tell me they never need C++ and that managed code will rulled them all.

    I have a kind of technical question, first, i don’t know ConcRt (written from what i heard herb said) and I don’t know how a work stealling thred pool work ( I should google that) but Herb said that if those lambda would do a bit more, they would be schedule on a different thread. How does it this happen. We can’t know at compile time the time an instruction will take, so how does it know when to spin the lambda on a different thread or not? If this kind of info is like the secret sauce of ConcRt, maybe some literature ref would be good?

    thanks alot

  11. Hi,

    Is the overhead of futures+asyncs something that I should worry about? Should I sometimes, for performance reasons, consider using std::future<std::vector> rather than std::vector<std::future>?

    Some old-style API’s require a T* plus size, which I suspect could be mapped to a future<vector> but not to a vector<future> (I expect that future adds an extra data to each item).

    Best regards,
    Henrik

  12. Nice interview. I appreciate the links for learning more.
    Would there be any chance that the articles in the “welcome back to C++” series (http://msdn.microsoft.com/en-us/library/hh279654(v=vs.110).aspx) could be formatted as a single web page, with all sections expanded ?
    It makes it easier to put on an Amazon Kindel :)
    Thank-you for your efforts in making the benefits C++ accessible to a wider audience.

  13. It would be nice to have a list of C++98 idioms (like this one pass expensive objects by const reference) that can be improved in C++11. Or maybe a book? C++11 for C++ developers :-)

Comments are closed.