Great slides. This is the first time I am seeing a direct C++03 and C++11 comparison. Makes it much easier to understand.
That said, c++11 is a language that wants to be everything. Functional, object oriented, procedural, imperative, declarative and what not. The end result is slush.
Slush? Depends how you use it. It's extremely easy to write horrible C++ code, but when used by a talented developer I think the result can be extremely elegant.
I like how it's a lot more expressive than C, but without the needless verbosity of a language like Java.
C++ still has quite a bit of needless verbosity. You need to explicitly specify how variables should be captured in lexical closures. You need to explicitly choose weak pointers versus non-weak pointers. There are few useful defaults; you are forced to explicitly write a lot of boilerplate as a result. It may be better than Java, but that is not saying much.
> You need to explicitly specify how variables should be captured in lexical closures.
I prefer that to some "it's automagically captured" behavior (in the context of C++). At least so I know and can choose if the closure captures a reference or makes a copy - which can be a very important detail.
For the stuff I usually do in C++ I love this kind of freedom. For other stuff where speed and/or memory layout/usage are not that important there are higher level languages like Python or Common Lisp.
It seems to me that you could just as easily always create a copy of the captured variable, and in cases where you want to capture a reference, just create a reference to the variable and capture that instead. That is exactly what you see people doing when they capture "smart" pointers. You would not lose any freedom if captured variables were always copied, you would still be able to avoid expensive copy operations as needed (or just fix the problem at its source by making copying less expensive).
A high level feature like lexical closures should not get bogged down with low-level details (like the mechanics of how variables are captured). It's a waste of time and mental energy, distracting programmers from important issues and making debugging more complicated and time-consuming. Spending time deciding which method of capturing variables is faster screams "premature optimization."
Slush is possible in C++03 as well. I'd say C++11 is trying to be humane as possible in that regard and not destroy the program (or the output) on account of programmer idiosyncrasies, no?
What I would much rather prefer is a "from scratch" approach to learning C++11 (assuming no prior knowledge about programming, but having a basic knowledge about memory etc...) That would really help curb the tide of novice programmers seduced by sexier languages that have no concept of safety, efficiency and correctness.
What I like about this book is that it teaches you the new C++ way right from the beginning. Rather than walking you through the history of C, it gets you to the modern practices immediately.
Disclaimer: I have only read the first 10% of the book myself, but so far I like it.
I have read quite a few overviews, and I really like the direct C++03 vs C++11 code comparisons in this one. It's get right to the point of showing the things that C++11 is trying to fix.
You can prefix the left parenthesis with a string. You then repeat that string after the right parenthesis. By doing this you can include sequences in the raw string literal that would have otherwise terminated it.
Personally, I'd like to see some of these features added to C where they make sense. Raw strings, bind, lambdas, constexpr, and ranged for loops would all make sense in C.
The main thing C needs from C++ is object destructors and RAII. It would be easy to denote a function as the cleanup operator for a struct, and the compiler already knows when variables go out of scope, valuable information that is currently not used for much. auto/shared pointers would save a lot of memory management pain in C.
Also function templates that would allow such things as a fast generic sort routine without lots of function pointer dereferences would be nice.
How could lambdas make sense in C? Keeping closures around means having to keep free variables around. C doesn't have garbage collection, so it would be quite awkward to establish conventions about who gets to free the variables that each closure references.
Take a look at the "blocks" extension to C, in LLVM. It includes mechanisms to handle the lifetime issues, though it does still require explicit memory reclamation of blocks.
Even with those enhancements, C++ remains incredibly complex language, with such a great variety of features the learning curve would be rising very slowly over time.
with such a great variety of features the learning curve would be rising very slowly over time.
That is not necessarily true. The point behind C++11 is to provide new features that are easier to learn than the features they replace. So C++11 on its own has a lower learning curve than C++03.
Of course, if you're maintaining older code, then your point stands, and the learning curve would be rising because of the extra new features.
This is really helpful. I'm kind of thrown by the page about "final". The C++ example shows a method that is both virtual and final, which seem to be in contradiction of each other. Or am I missing something?
In C++, the keyword 'virtual' can be used to introduce a new virtual member function and to re-assert that a member function is virtual (http://stackoverflow.com/questions/4895294/c-virtual-keyword...). Using 'final virtual' only makes sense for the second use, but is legal in the first context because of the re-use of the 'virtual' keyword.
Nonvirtual functions are effectively always final; if you have a pointer to a Foo which has a nonvirtual member function Bar, then you will always call Foo::Bar even if the thing being pointed to is actually an instance of a subclass that defines its own Bar member function.
Using final without a base class only makes sense if you don't actually want virtual semantics but specifically need the function to be in the vtable for some reason (such as for COM).
Yes, you are missing something, which is a bit of a quirk of C++. Normally, having two functions with the same signature in a parent and child class results in overloading.
struct A
{
void f() {std::cout << "A";}
};
struct B : A
{
void f() {std::cout << "B";}
};
//.,...
A a = B();
B b = B();
a.f();
b.f();
b.A::f();
This would print ABA, as the child's f() function simply hides the parent one when working in the context of B(), but it does not override it.
Now, what virtual does is it tells the compiler, "from here on out, resolve clashing signatures for this function in child classes by overriding. I'm sure you know how our example changes when you mark f() as virtual.
That's all old news, though. The more interesting bit, final, acts like so: from here on out, prevent overloading in child classes. Now, that only makes sense in the context of virtual, so that's the only place where you're allowed to use final, and here the actual difference to the first example becomes apparent. You see, final does not negate virtual, what it actually does is completely lock up the function signature from being used in any child classes. Adding virtual final to our example would not cause B's function to hide A's - it will simply not compile.
If you think that's all pointless semantics, you are absolutely right. Locking up names is not the point of "final". In fact, declaring something as virtual final in the base class is completely pointless from any practical standpoint. The actual problem that final is meant to solve is this:
As I mentioned earlier, virtual changes the way resolving names works in child classes forever. These two pieces of code are absolutely equivalent.
struct A
{
virtual void f() {std::cout << "A";}
};
struct B : A
{
void f() {std::cout << "B";}
};
And:
struct A
{
virtual void f() {std::cout << "A";}
};
struct B : A
{
virtual void f() {std::cout << "B";}
};
Therein lies an interesting dilemma. What if I wanted to prevent any child class of B, and only B, from changing the implementation of f. Under C++03, that is not possible. In C++11, final solves that.
But why go through the trouble of introducing a new keyword - and a keyword that is only a keyword in class and function declarations to boot (Holy context dependent grammar, Batman!) - and not just drop the virtual qualifier? Backwards compatibility - ever a dreaded thing when you wish you could undo your old mistakes.
It isn't all that big of a deal (said the C++ developer about every strange rule in the language, ever), though, since when you use "final" for the intended purpose, you won't actually be needing the virtual qualifier.
struct A
{
virtual void f() {std::cout << "A";}
};
struct B : A
{
void f() final {std::cout << "B";}
};
struct C : B
{
void f(); //I'm afraid I can't let you do that.
};
This is a great overview. However, in order to use the new C++11 features correctly and avoid pitfalls, one must dig a bit deeper. A good example is auto and decltype:
I just have to thank my lucky stars i don't work with it. It's like they are trying to iterate a power saw into an aeroplane by gradually adding features while remaining backwards compatibility as a power saw.
Please hurry up and finish Rust, is all I can say to that.
I think it's a great update. Everyone already using C++ on a platform that has the required tooling (also considering libraries, IDEs) will benefit from updating.
Compare to that Python 2.x to Python 3.x. I have no interest in updating my XX kloc of Python code so that... unicode becomes cleaner?
That said, c++11 is a language that wants to be everything. Functional, object oriented, procedural, imperative, declarative and what not. The end result is slush.