
Bjarne Stroustrup – The Essence of C++ [video] - qznc
http://channel9.msdn.com/Events/GoingNative/2013/Opening-Keynote-Bjarne-Stroustrup
======
fhd2
If you've ever wondered what's the deal with C++ and why it's being used in
2013, you might wanna watch this. Stroustrup isn't a very flashy speaker, but
he says some incredibly insightful things.

C++'s C heritage makes it hard to master and also causes countless
misconceptions. Even if you never want to use C++, it's worth looking into
some of the unique concepts that, sadly, didn't catch on in other languages so
far. (My personal favourites are const correctness and RAII.)

Do watch the talk, the first few minutes might already be mind blowing for
anyone who thought C and C++ are basically the same language.

~~~
fauigerzigerk
There are also some things that are powerful but I'm not quite sure if I
really like them. For instance, he promotes the use of handles instead of
pointers. He goes through these variants:

    
    
      Gadget* p = new Gadget(n); // not exception safe
      shared_ptr<Gadget> p{new Gadget(n)}; //exception safe
      unique_ptr<Gadget> p{new Gadget(n)}; //exception safe and less wasteful than shared_ptr if local
      Gadget g{n}; //his preferred solution
    

My problem with his preferred solution is that I cannot know that Gadget is
really a handle to a shared Gadget and not a big fat Gadget value on the
stack. The only way of knowing that is to look at the implementation or the
documentation.

So if g is returned from a function or passed to a function by value, I don't
know whether or not a deep copy is made. If I manipulate that Gadget, am I
manipulating a shared object affecting others or is this my private copy?

Qt uses that pattern throughout, and because it is used for everything in Qt,
you know that you're dealing with handles. But the C++ standard library
doesn't do it that way. Almost none of the classes in the standard library are
handles.

If I see a pointer, shared or otherwise, I know I'm not dealing with a deep
copy. I know someone somewhere else might point to the same object.

~~~
fhd2
> My problem with his preferred solution is that I cannot know that Gadget is
> really a handle to a shared Gadget and not a big fat Gadget value on the
> stack. The only way of knowing that is to look at the implementation or the
> documentation.

I'd say you do not _need_ to know. If Gadget contains tons of data, it's wiser
for it to just put that on the heap, internally. Like std::vector does. As
long as Gadget handles (or forbids) copying, assigment etc. (rule of three),
that's fine.

If you do have a huge performance issue because you're using a class that,
unwisely, puts tons of data on the stack, you can use a unique_ptr instead.
Kind of an edge case in my experience though.

~~~
fauigerzigerk
I disagree. You have to be absolutely certain at all times whether you're
holding on to a reference to a shared data structure or you have your own
copy, unless the object is immutable. This is not just a performance thing.

~~~
fhd2
You mean that it's not clear if copying the handle will copy the data or just
create another reference? At least in the STL that's kinda clear: vector will
copy its elements (if they are pointers, it'll copy the pointers. if they are
values it'll copy all the data), shared_ptr will copy the handle pointing to
the same shared object, unique_ptr is not copyable and so on.

But agreed, it's opaque when working with third party code. Gotta figure out
the conventions.

~~~
fauigerzigerk
_> You mean that it's not clear if copying the handle will copy the data or
just create another reference?_

Exactly. In the STL, if it looks like a value copy it is a value copy, but the
handle pattern that Stroustrup prefers gives you no clue whatsoever as to
what's going to happen. You have to figure it out one class at a time.

------
mkl
I think I watched all the talks from Going Native 2012, and I highly recommend
them [1]. I expect 2013's will be just as good, though I won't have time to
watch any for a while. Here are the others:
[http://channel9.msdn.com/Events/GoingNative/2013?sort=sequen...](http://channel9.msdn.com/Events/GoingNative/2013?sort=sequential&direction=desc&term=&d=1&d=2&d=3&Media=true#theSessions)

[1]
[http://channel9.msdn.com/Events/GoingNative/GoingNative-2012...](http://channel9.msdn.com/Events/GoingNative/GoingNative-2012?sort=sequential&direction=desc&term=&d=1&d=2&Media=true#theSessions)

------
zowch
As somebody who attended this conference, and hadn't had much exposure to
C++11 features outside of 'auto', my personal biggest takeaway from almost
every talk was this: _Stop passing your sink variables as const refs._

That is to say if you have a constructor:

    
    
      MyClass::MyClass(const std::string& s) : m_s(s) {}
    

That you call like:

    
    
      std::string s = "Some string";
      MyClass c(s);
    

You're hamstringing the compiler into _always_ copying that string instead of
being able to use the new move semantics, because it can't mess with the guts
of a const reference. Instead, do the previously unspeakable evil of passing
by value and then moving, e.g.

    
    
      MyClass::MyClass(std::string s) : m_s(std::move(s)) {}
    

This lets the compiler know that if string has a move constructor, and is an
rvalue, it can just move the guts into place instead of performing the copy,
since the variable is 'sunk' into the new location. Huge wins all around.

~~~
a8da6b0c91d
Is the std::move call really necessary? It's a little sad if the compiler
can't see that s is already an rvalue.

~~~
detrino
Once things have a name and you can take their address they are no longer an
rvalue. It would require a much more sophisticated type system to do this
automatically (when you pass a reference to another function, does it capture
it?). It would also be non-obvious when you were moving. Think of Java style
escape analysis vs C++ stack allocation (implicit vs explicit). There is one
case where the compiler will do this though, when you return a stack allocated
value from a function, it is implicitly moved.

~~~
saurik
The argument here is that in the body of the constructor the developer
probably doesn't use s, so a trivial analysis of the function can say "oh, it
doesn't matter if I destroy s, so in the one place where it is used I'll just
move it". It doesn't then matter if it is obvious it happens or not, because
the semantics of the program wouldn't be maintained (and honestly, I'd argue
it is never quite obvious when what happens in different contexts given the
large number of rules involved: you kind of just have to have some trust).

~~~
detrino
There are a number of considerations because the compiler can't prove it in
general without augmenting the type system or doing whole-program analysis.

Do you allow, but not require, the compiler to do this substitution whenever
it can prove a value is never used again? This is unreliable across build
options and compilers so it would be unwise to depend on it. There is an
opportunity here for a tool that could identify places where you could insert
move, perhaps even a -W option for the trivial cases such as above, but I am
not convinced the language should allow the compiler to do this.

Do you make it mandatory and add more special cases for the compiler to have
to implement? This would require the compiler to track references to make sure
they don't get passed to some other function. It would need someone to codify
the special cases in the standard and this might be very difficult. It would
also be fragile, there would be cases where implicit move used to kick in but
some added function call inhibits it even though a move is still the right
thing to do.

~~~
saurik
You seem to be assuming that optimizations are only of value if they can be
depended on happening consistently as a defined property of the language on
every single platform for which there exists a compiler: in practice,
compilers perform numerous optimizations (such as static branch prediction)
that only work probabilistically even for the case of a specific version of a
specific compiler on a specific operating system; I know of very few people
(maybe you are one of them, however) who consider this to be a detriment. For
a more obvious comparison: yes, I could sit around constructing object pools
in my Java program to avoid heap allocations and their associated garbage
collection penalty, but if rudimentary escape analysis manages to pick up a
lot of the value, even if "fragile" or nondeterministic, I might not need to
spend the time anymore to worry about that kind of detail in my software as
the cost benefit analysis shows that the toolchain is now a more efficient
usage of resources towards that issue (I could be spending my energy working
on algorithm design, for example, instead of object pools). As long as the
semantics of the language support the optimization, and as it doesn't seem to
be difficult to implement, it would be useful to have the compiler perform
this optimization for the developer, and one would expect such an optimization
to either already be there or be "in the works".

~~~
detrino
This is a very different kind of optimization than the other things you are
talking about. This is something that is operating at a high level and
potentially has implications that might not be obvious. You are changing the
type of something and effecting the function overloads which are resolved. I
think its important for what functions you are calling to be deterministic. My
previous post was just touching on what possible value can be gained from
allowing this and my conclusion is that it would be very little.

Also, your example of escape analysis is precisely because Java doesn't allow
you to express what you are wanting to do, whereas C++ does allow you to
express moves.

edit: I would compare this to C++'s copy elision, but that has a very high
value and is much less intrusive.

~~~
a8da6b0c91d
I guess considering a user defined move constructor could do something
arbitrarily stupid you have to be consistent.

~~~
detrino
Also, any function can be overloaded on rvalue references.

------
Aldo_MX
I liked this snippet about pointer misuse:

    
    
      void f(int n, int x)
      {
        Gadget* p = new Gadget(n);  // look I'm a java programmer! :)
        // ...
        if(x<100) throw std::runtime_error("Weird!");  // leak
        if(x<200) return;                              // leak
        // ...
        delete p;  // and I want my garbage collector! :(
      }

~~~
harrytuttle
I rather prefer Java:

    
    
        void f(int n, int x) {
            Gadget p = new Gadget(n);
            // ...
            if (x < 100) throw new Exception("Weird!); // no leak
            if (x < 200) return;
            // ...
        }
    

Yes, good night's sleep tonight after writing that...

~~~
Sharlin
Java:

    
    
        void f(int n, int x) {
            Reader fr = new FileReader("foo.txt");
            // ...
            if (x < 100) throw new Exception("Weird!); // resource leak
            if (x < 200) return;                       // resource leak
            // ...
        }
    

A garbage collector that gives a false sense of security is much worse than no
garbage collector at all.

~~~
harrytuttle
try/finally in Java / using in C# are designed for that scenario.

~~~
Sharlin
And try/finally assigns the cleanup responsibility to the caller, not the
callee, which just adds boilerplate and mental burden. C++ does not need a
finally block due to RAII. The using block (and "try-with-resource" in Java 7)
is a poor man's RAII emulation.

Anyway, what if you need to share non-memory resources? Suddenly you cannot
depend on the garbage collector, you cannot use try/finally, you cannot use
using or try-with-resource - you need to handle the situation just like in
C++, except you're given fewer tools to do it - and a poorer understanding of
the situation if you've learned that you don't need to do manual resource
management due to the garbage collector.

~~~
pkolaczk
True, but 90% of resources is memory. Java makes 90% of resources much easier
to handle and 10% only little harder (like always use the try-catch-finally or
try-with-resources).

------
FurrBall
I look at Java code and it horrifies me. Java is great if your resource is
memory. It turns into a horrible unsafe mess the moment your resources are not
memory.

    
    
      //C++ RAII goodness.
      void foo()
      {
      	conn.open();
      	file.open();
      	printer.activate();
      	//do stuff
      }
      
      //Java's lack of RAII is disturbing
      void foo()
      {
      	try{
      		conn.open();
      		file.open();
      		printer.activate();
      		//do stuff
      	}
      	catch(Exception e) { }
      	finally{
      		try {
      			conn.close();
      		}
      		catch(Exception ee) {}
      		finally{
      			try {
      				file.close();
      			}
      			catch(Exception eee) {}
      			finally {
      				try{
      					printer.close();
      				}
      				catch(Exceptoin eeee) {}
      			}
      		}
      	}
      }

~~~
pkolaczk
You can write bad code in any language. No experienced Java coder would write
a code like this. Java has ARM blocks for dealing with deterministic resource
cleanup.

~~~
plorkyeran
using/ARM/etc. make classes which own non-memory resources a lot less awkward
to deal with, but one of the big wins of RAII is that the caller doesn't have
to know or care that you have things which require special cleanup.

~~~
pkolaczk
Agreed, however it is very easy to catch a forgotten close() automatically
(IDEs do that), and, at least in my experience, most non-memory resources are
used locally and not shared, so they are pretty easy.

On the other side, some programming techniques are hard or downright
impossible without GC - e.g. heavy functional programming with immutable
persistent structures.

------
polskibus
I posted some of the others while they were live :
[https://news.ycombinator.com/item?id=6336789](https://news.ycombinator.com/item?id=6336789)
[https://news.ycombinator.com/item?id=6336778](https://news.ycombinator.com/item?id=6336778)

Hopefully we can have an interesting discussions on those topics now that at
least 1 news about them has reached the main page.

------
bsaul
really interesting to see classes as ressource manager above all, instead of
concept incarnation. I was almost convinced to go back to C++ until the slide
with auto range and all, combining new features of C++, at which point i
remembered why i didn't want ro have a look at C++ code again.

but hey, i'm not a system programmer so i can afford it :)

~~~
fhd2
RAII ("classes as resource manager") is one of the nice concepts in C++. My
favourite is certainly const correctness.

And then there's tons of things that creep me out a bit. Being a multi
paradigm language sounds good on paper, but it seems to cause a lot of
accidental complexity. The best way to stay sane is probably to pick a certain
subset of C++ for your project and stick to that, that's what I tend to do.

Then again, that's not uncommon in simpler languages either. "JavaScript, The
Good Parts", lint and all that. Code accessibility matters IMO.

~~~
pkolaczk
C++ const-correctness is weaker than immutability in, say Haskell or Scala.

~~~
fhd2
Can't say anything about Haskell, but what Scala has is less flexible then
const correctness in C++ IIRC. You can't have an immutable reference to a
otherwise mutable object and then call non-mutating functions on it. It's
either fully immutable or fully mutable. CMIIW by all means.

------
frozenport
Am I the only one who is too lazy to write concepts? The process seems too
involved and represent a case of diminishing returns. Large OOP projects are
infamous for having classes that are impossible to mock, this will only make
the problem worse.

------
yules
Nice, thanks for posting

