84 comments

[ 3.3 ms ] story [ 163 ms ] thread
Nice, thanks for posting
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 :)

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.

C++ const-correctness is weaker than immutability in, say Haskell or Scala.
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.
And even system programmers have other choices, as demonstrated by the linux kernel.
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! :(
  }
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...
The point of the example is that this is how NOT to do it in C++.

There are two better options:

  void f(int n, int x) {
      Gadget p(n); // Stack allocated
      // ...
      if (x < 100) throw new Exception("Weird!); // no leak
      if (x < 200) return;
      // ...
  }
Or, if it really has to be a pointer:

  void f(int n, int x) {
      std::unique_ptr<Gadget> p = new Gadget(n); // Smart pointer
      // ...
      if (x < 100) throw new Exception("Weird!); // no leak
      if (x < 200) return;
      // ...
  }
Both will be automatically freed as soon as the scope is exited.
What I was hoping you'd say :)

This is fine until I want to do this, at which point C++ becomes a memory management bastard:

    void f(int n, int x) {
        Gadget g = new Gadget(n);
        // ...
        if (x < 100) throw new GadgetException("Gadget broke", x);
        if (x < 200) return;
        // ...
    }
you can always fake a defer:

    void f(int n, int x) {
      Gadget g = new Gadget(n);
      std::shared_ptr<void> defer(nullptr, [g](void *) {
        delete g;
      });
    ...
But honestly ... I'd just use a stack allocated object - or a smart pointer. In the last year I didn't write one delete. (I'm a full time C++ dev).

    Gadget g = new Gadget(n);
That code won't compile unless Gadget has an assignment operator that accepts a pointer, which is, well, not a very common scenario.

Note: I'm not trying to be nitpicky. I really don't understand what you mean when you write that you "want to do this".

Now, I don't know anything about you, but based off your comments in this thread I'm not sure you have a working knowledge of C++.

Well, what's easy in Java here is that you're effectively always working with shared objects. That's easy to use, but in my experience quite error prone, I can't count how many memory leaks I fixed in Java/JS code. YMMV.

In C++, you have various ways of referencing and passing objects, so you need to be aware of the lifetime and ownership of objects. It's arguably harder, and unfortunately, the C heritage makes it a lot harder than it needs to be :(

This code makes no sense. I think you do not understand C++ very well.
The point is not about C++ vs Java, it is rather about writing C++ like you would write Java, which leads to leaks.
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.
try/finally in Java / using in C# are designed for that scenario.
But you'll need to document it very visibly that "Thou shalt call resource.close() whatever happened."

C++ makes it possible for the library writer to take care of freeing the resources automatically.

A class implementing IDisposable is that documentation. You shouldn't need more documentation than the fact that IDisposable is there.
An annoying fly in this ointment is cases where objects assume the responsibility for calling Dispose on caller-provided disposable objects. While it should always be okay to "double-dispose", this is only a guideline, and can't be enforced by the language, so, to maximize future compatibility with classes outside your control, you should probably defensively avoid calling Dispose on an object that will have already been disposed by other means.
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.

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).
They are poor solutions compared to RAII: leaky abstractions which are non-composable and error prone. Unfortunately, something like C# using is probably the best you can do in a language without clear ownership semantics like C++.
Go's defer keyword is good for this. You could also attach a finalizer to the struct, to panic/log in case it's never closed.
Nitpick:

    if (x < 100) throw new Exception("Weird!); // LEAK!
You shouldn't use `new` either when throwing exceptions. Just:

    if (x < 100) throw Exception("Weird!); // no leak.
The grandparent's code is Java, not C++.
Yes, Java is a far superior language than C++ for writing Java code.
I'm a C# dev and I totally agree with this.

I've done a limited amount of C++ many many years ago before I even knew what garbage collection was and I keep thinking of revisiting it, but honestly in my line of work ("Enterprise") I don't need the mental overhead of dealing with things such as pointers and memory allocation.

Perhaps, my view is outdated, but I get the impression that everything in C/C++ is just a little thorny when compared to other slightly more high-level languages, such as namespaces, package management, list comprehensions, library compatibilities, type strictness etc.

I would like to be wrong about that though... I wish I had a little more motivation to spend some real time with C++ (or perhaps even C).

In OOP its recommended to use pointers as class members, managed by class methods. What you show is a "wild" allocation that doesn't really belong to no one and thus not really managed. You're right, pointers can be misused, but what can't be? Think of infinite loops or some similar very bad algorithm choice if nothing else. No amount of language design will fix bad programming.
I don't think OOP means what you think it means. There's nothing in Object Oriented Programming paradigms that specifies what you should declare class members as. There are more important components to that decision such as what language you're using.
OOP means you design your thing as objects, with each object taking care of its own stuff. You don't have to use pointers, you may just use other objects as members, as you do in many other languages. Pointers just give you additional power and flexibility and if they are used in a object oriented design, its safe to assume that they should be managed by the owner object, like any other object's internal stuff.

Of course, you may prove me wrong.

Bjarne's advice also applies to pointers wrapped as class members. There are just too many ways you can mess things up inside the class when dealing with raw pointers, examples including:

- partial construction: having to deal with already allocated pointers in case of errors during the construction - copy construction: who owns the resources? (Alternatively, you'll need to remember to disable the copy constructor explicitly.) - copy assignment: likewise

Doing the Right Thing is just so much easier when you wrap those member objects into e.g. `std::unique_ptr<T>`. That'll disable copy construction and assignment, which means that you'll need to think of that separately if it's needed.

These are problems that pop out from a bad design choices. One of the first things I remember from OOP learning is that constructors should be as safe as they can be, which means no allocations, partial or not. Of course it is tempting to misuse the language features out of personal commodity and blame someone else. I think we can agree that a few design changes would solve the ambiguous cases. Also, for the "remembering" problem, we can inherit from a base class having all the needed restrictions in place. The std's "something_ptr" gimmicks may sound like a good compromise at first, but just like the garbage collector, these will make you lazy and neglectful over time. It's good for those to exist though.
> constructors should be as safe as they can be, which means no allocations, partial or not.

Can you expand upon what you mean by "safe" here?

If you mean "unable to fail", I vehemently disagree. Constructors should validate what is passed into them and fail if that is invalid. The alternative is to construct a zombie object that can't actually be used. Objects like this subvert the type system and lead to lots of unnecessary "if object.is_valid()" checks all over code that uses them.

"safe" like nullifying pointers and dummy-initialization for objects. This kind of initialization has a small footprint on performance, but it was made optional nevertheless, due to language's performance aims (you can skip them if you don't need that). You can make a little bit more complex operations in constructors that involve data validation, but it should only default to blank initialization if something doesn't pass the checks. The initialization that can fail (and the failure must be taken in account) should be done outside constructors. If an object is or isn't considered valid after a dummy initialization is a design decision, and likewise an adjustment in design can avoid excessive "if (object.is_valid())" kind of checks.
>That'll disable copy construction and assignment, which means that you'll need to think of that separately if it's needed.

If you have a pointer in a class that is exactly what you want to happen in most cases. Then if you want copies you "have to" define your own copy constructor and assignment operator that copy the object pointed to rather than just copying the pointer.

(comment deleted)
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.

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.

C++11 move semantics eliminates the need of deep copying in most cases and return by value usually is very cheap.
But move semantics only work with temporaries. They don't magically make every object a handle.
> 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.

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

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

I'd also say that using any class without reference to either its documentation or its implementation is a terrible idea in the first place, because who cares where it stores data if you're not using it properly?

I can't count the number of bugs I've fixed due to programmers (myself, sadly, included) using IDE code completion features as a substitute for reading documentation.

Somehow I don't find this to be a problem in my code. Yes, when using someone else's library you need to know how they handle ownership. But this is basically a problem inherited from C.

For example, in the code you posted all the pointer-like handles are named 'p'. In my experience, if we consistently pursue minimal scope and minimal lifetime for our objects, then longer-lived objects are oddballs and we use the type of the handle to help describe its ownership strategy.

I agree that this is an issue with third party libraries, not with my own code. But C++ libraries can be a real horror. Every single one of them comes with its own peculiar memory management idea. Recently I started to use rapidjson. It's insanely fast but that's not the only insanity. It basically screams for memory management bugs.
Ever done any enterprise C development?

This is a very big problem in enterprise size code basis, specially with the rotation of external developers.

Oh noes. Yeah, C seems very not the thing to rotate external developers around in an enterprise size code base.
> C++'s C heritage

Well, at this point it's more like C++'s C++ heritage.

> const correctness

It certainly exists in plenty of other languages, but in a non-broken way. C++'s const correctness model is mostly broken, by the way [1]. D's model is a bit more sane [2]. Languages like Haskell or Clojure (AFAIK) make immutability a default. This kind of functional style is a good thing for C++ developers overall, as emphasized by John Carmack [3].

[1] Once again, the C++ FQA has plenty of wisdom to share: http://yosefk.com/c++fqa/const.html [2] http://en.wikipedia.org/wiki/Const-correctness#const_and_imm... [3] http://www.altdevblogaday.com/2012/04/26/functional-programm...

> RAII didn't catch in other languages

That's because most other languages are garbage collected. (Not that RAII is a bad thing, it's just not needed.)

> It certainly exists in plenty of other languages, but in a non-broken way. C++'s const correctness model is mostly broken, by the way [1]. D's model is a bit more sane [2]. Languages like Haskell or Clojure (AFAIK) make immutability a default.

What in particular is broken about const correctness in C++? That things are not const by default? I agree, they should be, but I wouldn't really call that broken. I put const wherever I can, just like Carmack. So I sort of pretend it's the default :)

I don't think it makes sense to compare const correctness in C++ to immutability in Clojure. In Clojure, you don't have objects that encapsulate internal state. const in C++ allows you to say which methods do change internal state and which don't. If you pass a const reference, the receiver can only call const methods. I just learned from you that D has this, wasn't aware of any other OOP language that has it. Not even Scala :(

> That's because most other languages are garbage collected. (Not that RAII is a bad thing, it's just not needed.)

RAII is about more than just memory, it's about any kind of resource. That includes stuff like file handles, Java's try {} finally {} is IMO a weird workaround. In C++, stream's can just close themselves when they go out of scope.

> What in particular is broken about const correctness in C++?

The lack of transitivity for one. Const pointers shouldn't be able to modify the pointed objects (that's the default in the D language). The fact that working with non-const-correct libraries is a nightmare (prepare to const_cast a lot!). There are plenty of other things that make this a real PITA to work with on sizable projects with legacy or outside code.

> RAII is about more than just memory, it's about any kind of resource.

That's true, yeah, though most of the time it's about memory (with smart pointers). But you're absolutely right on that point.

As far as I know, Common Lisp users have some kind of RAII with macros, but I'm not familiar enough to compare it to C++. Of course, D also has support for this idiom.

> The lack of transitivity for one. Const pointers shouldn't be able to modify the pointed objects

Are they? I either misunderstand or disagree. The syntax gets a bit silly with pointers, because you can have const pointers and pointers to const (and const pointers to const of course):

    const Foo* foo; // Cannot change *foo, but can change the pointer (e.g. foo = 0)
    Foo* const foo; // Can change *foo, but cannot change the pointer
    const Foo* const foo; // Cannot change *foo or the pointer
> The fact that working with non-const-correct libraries is a nightmare (prepare to const_cast a lot!).

Agreed!

> As far as I know, Common Lisp users have some kind of RAII with macros, but I'm not familiar enough to compare it to C++.

In my experience, the kind of problem RAII is useful for is far less common in Lisps (can only really speak for Clojure). One tends to not encapsulate resource management in any way, so handling it explicitly in the caller is fine. And you can use macros to do something when the scope is excited.

So in Clojure e.g., you can use with-open to do something with a file:

    (with-open [writer (io/writer "foo.txt")]
        (write writer "Hello")))
with-open ensures that the reader is properly closed when the scope is exited or when an exception occurs.

In C++ (or any OOP language I guess), you'd want something like this:

    File foo("foo.txt");
    foo.write("Hello");
So I suppose it depends on the philosophy. For OOP languages, RAII makes a lot of sense IMO.
Const correctness is the essence of all functional languages; RAII is in functional languages too and even java has an interface for it
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.
Is the std::move call really necessary? It's a little sad if the compiler can't see that s is already an rvalue.
s isn't an rvalue. It has a name, and you can refer to it within the body of the constructor.
I understand that, but it's clearly not used in the body. I was hoping it would be a legal and implemented optimization.

move calls strike me as ugly and kinda dangerous. What if you do wind up wanting to refer to s in the body later on? It'd be nice if the compiler just handled it.

> What if you do wind up wanting to refer to s in the body later on?

Then copy s, don't move it.

The point is move makes things that look like values as annoying as pointers. You have to keep track.
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.
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).
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.

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

I guess considering a user defined move constructor could do something arbitrarily stupid you have to be consistent.
Also, any function can be overloaded on rvalue references.
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) {}
  			}
  		}
  	}
  }
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.
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.
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.

Reality is there are many Java code bases which handle resources like this. For java 6 and below this not bad code; it is how you are forced to deal with resources.

Even with the new block syntax, you have painful nesting if the operation requires several disparate resources.

Blocks are an improvement, but not the same as RAII. A block is literally converted to a try-finally by the compiler. It is the same thing with a cleaner syntax. There is no safety added to the old Java way. A programmer can forget to put his stuff in a block and leak the resource the same as he forgets to use a try-finally. The onus is on the consumer, not the library writer.

With RAII the onus is removed from the consumer. The consumer writes safe code automatically, no onus to remember any special clean up idioms.

Java 7 introduced a new language construct, called the try-with-resources statement, for automatic resource management:

    void foo() {
        try (open file or resource here) {
            // do stuff
        }
        // after try block, resource will close automatically
    }
This works with non-memory resources such as files, streams, sockets, and database connections.
Pasting a response:

Blocks are an improvement, but not the same as RAII. A block is literally converted to a try-finally by the compiler. It is the same thing with a cleaner syntax. There is no safety added to the old Java way. A programmer can forget to put his stuff in a block and leak the resource the same as he forgets to use a try-finally. The onus is on the consumer, not the library writer.

With C++ RAII the onus is removed from the consumer. The consumer writes safe code automatically, no onus to remember any special clean up idioms.

As long as the object is stack allocated or a member variable.
Well you do have to ban raw pointers. Heap allocation is safe with smart pointers.
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.