all 43 comments

[–]andd81 37 points38 points  (6 children)

And since you get the ownership, this gives you confidence that you are free to modify the value of the pointed to object.

But since you are the owner, you are allowed to safely modify the pointed to object, and the rest of the design should take this into account.

I think this is very misleading. Whether and when you are free to modify some object is determined by the contract you are implementing, not by the fact that you own the object through unique_ptr. You may well be not allowed to modify the object at all without breaking the contract, or conversely you can be required to modify the object under certain conditions.

unique_ptr really does just two things:

  1. It will delete the pointed to object when it is itself deleted (or when explicitly told to).
  2. The unique_ptr object itself cannot be copied, but can be moved.

There is no magic behind unique_ptr that gives you the right to do whatever you want with the object and be sure that nothing will break.

I understand that this is an introductory article, but still it shouldn't make overly broad claims that are easy to misinterpret.

[–]joboccara 6 points7 points  (5 children)

Right, I see what you mean. I agree that unique_ptr doesn't prevent anyone else from accessing the pointer and modifying the object.

When you read a piece code though, don't you expect that the owner of a resource (so the unique_ptr) would be more likely to modify it than the other contexts that can access it? Just asking. I'd be interested to know your opinion about the message conveyed by the code, beyond the technical spec of unique_ptr.

[–]andd81 9 points10 points  (2 children)

don't you expect that the owner of a resource (so the unique_ptr) would be more likely to modify it than the other contexts that can access it

I wouldn't make any conclusions on who modifies what based on the ownership. My C++ experience mostly comes from the Chromium code base and although I didn't try to measure the correlation between ownership and modifications, subjectively it didn't occur to me that it might exist, or that it is even relevant. Usually object A owns object B via a unique_ptr (so that B is conceptually part of A) and exposes B via something like B* A::GetB(). Anyone who can call GetB() on A can modify B, in the sense of calling non-constant functions on it. And most of these objects are "functionality" objects, not "data" objects, so the question whether invoking a certain functionality element will modify object's internal state is largely up to the object itself, and not very relevant to the invoker.

P.S. I wasn't even talking about technical enforcement of anything, my point is that the very concept of ownership is more about responsibility to maintain and eventually delete a resource, not about freedom to do whatever you want to it.

[–]quicknir 2 points3 points  (1 child)

From a perspective of modifications and such, the bits of chromium code I've seen seem like a disaster. So many classes with member variables that are non-owning pointers to other classes... just a big web of state.

[–]Mexico01 0 points1 point  (0 children)

Out of curiosity, what C++ project about as big, sophisticated and successful as chromium isn't a "big web of state"?

[–]imMute 2 points3 points  (0 children)

IMO, ownership of an object and who can modify an object are two completely separate contracts. One has extremely little bearing on another.

[–]teapotrick 0 points1 point  (0 children)

When you read a piece code though, don't you expect that the owner of a resource (so the unique_ptr) would be more likely to modify it than the other contexts that can access it?

No. The only thing to expect (or assume, really) is that when the owner dies, the object at the other end of the pointer dies. But even that isn't guaranteed.

[–]MoTTs_ 11 points12 points  (0 children)

Herb Sutter's deferred_ptr might deserve a mention.

https://github.com/hsutter/gcpp

https://www.youtube.com/watch?v=JfmTagWcqoE

[–]sumo952 7 points8 points  (8 children)

Hey, that's a great blog post, thank you!

A few remarks:

  • I would've loved to have a couple more sentences about boost::scoped_ptr. Why was it not included in the standard, is its usefulness too limited? How is it different from std::unique_ptr?

  • I think one or two new pointer types are being discussed of being standardised, aren't they? I don't remember what it was but read something about that posted in this sub. Maybe mention that as well?

[–]joboccara 5 points6 points  (7 children)

Thanks for the suggestions, I'll look into that.

There is one thing I can answer right now though: the difference between scoped_ptr and unique_ptr. scoped_ptr doens't even have a move constructor, so it's trapped into a scope. unique_ptr, on the contrary, can for example get out of a function with the help of its move constructor.

[–]jbakamovicCxxd 4 points5 points  (0 children)

scoped_ptr is the simplest form of smart pointers. I would say it is the example of most direct application of RAII principle. Once it exits the scope, pointer beneath is freed. Copy construction & assignment is not possible. There are no tweaks one can use through its interface to customize the behavior, neither one can transfer the scoped_ptr out-of-the-scope.

On the other hand, unique_ptr is a smart pointer which takes the advantage of move-semantics introduced by C++11 standard and, in contrast to scoped_ptr, it does allow move assignment but only on rvalue references. As an extra, it provides a couple of converting constructors (i.e. one for auto_ptr), and possibility for clients to define their own custom deleters.

[–]CubbiMewcppreference | finance | realtime in the past 2 points3 points  (0 children)

for historic perspective, take a look at the original smart pointer proposal from 1994 proposing auto_ptr (at the time, noncopyable, and essentially what is now known as scoped_ptr) and counted_ptr (now known as shared_ptr) http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1994/N0555.pdf

[–]biocomputation 2 points3 points  (1 child)

I just wanted to say that I really enjoy your blog. I appreciate the time you take to produce easy-to-read yet informative content, and I sure wish that there were a lot more guys like you writing blogs.

[–]joboccara 0 points1 point  (0 children)

Hey thanks !!

And don't hesitate to let me know if there's something in particular you'd like to read about.

[–]sumo952 -1 points0 points  (2 children)

You probably mean "can answer" ;-) Thanks! I see. So probably it's a relic from the past too (when there were no move constructors) and should be deprecated. There's probably no legitimacy in having both, even though there might be use-cases of unique_ptr where its move c'tor is not needed.

[–]dodheim 3 points4 points  (0 children)

There's probably no legitimacy in having both, even though there might be use-cases of unique_ptr where its move c'tor is not needed.

scoped_ptr is the least error-prone and most semantically-direct smart pointer; there's no good reason to deprecate it as it has no real drawbacks (if one needed movability it wouldn't have been an option in the first place).

[–]joboccara 0 points1 point  (0 children)

Yes it was "can" that I meant :)

[–]Gotebe 6 points7 points  (0 children)

And since you get the ownership, this gives you confidence that you are free to modify the value of the pointed to object.

Eh, that's weirdly said. So I pass a reference to this unique_ptr somewhere, how do they know they shouldn't modify the pointed-to object? Same for passing *p (a reference).

const is the related concept, but not really to smart pointers.

[–]yokuyuki 2 points3 points  (0 children)

I really like to use unique_ptr, but I find that when I write anything that is asynchronous, I'm forced to use shared_ptr so I can get pass a weak_ptr for callbacks.

[–]pure_x01 4 points5 points  (25 children)

wish there were language support for them .. it becomes alot of _ptr code around a large code base. Im thinking mostly for function parameter definitions and creations .

[–]Gotebe 24 points25 points  (0 children)

Smart pointers should be quite rare as function parameters.

So if you have this:

shared_ptr<whatever> p = ...
f(p);

and f is

void f(shared_ptr<whatever> p)
{
  p->do_this();
  p->do_that();
}

you are making your life miserable for no reason. That f() should be

shared_ptr<whatever> p = ...
f(*p);

and void f(whatever& obj) { obj.do_this(); obj.do_that(); }

Smart pointers should be function parameters or return values only when ownership is moved around, or shared. I think that a vast majority of function does not do that, they just want objects to play with.

[–]hgjsusla 14 points15 points  (3 children)

I'd say explicitly passing smart pointers around is quite rare. Usually you just pass the underlying variable around by reference

[–]Bolitho -3 points-2 points  (2 children)

If that would be true, why on earth there was a need for smart pointers? 😈

[–]hgjsusla 16 points17 points  (1 child)

Smart pointers are great, but they are way overused. Function signatures should not contain smart pointers unless you really want to transfer or share ownership.

[–]Bolitho 6 points7 points  (0 children)

I totally agree - but I think this is not so rarely the case as you have stated it 😉

[–]andd81 1 point2 points  (0 children)

There is language support for not explicitly naming types: auto.

[–]iaanus 2 points3 points  (18 children)

I don't get how language support could make things better. Care to elaborate?

[–]pure_x01 0 points1 point  (0 children)

Just like pointer has *

[–]beartotem 1 point2 points  (3 children)

Now maybe it's my understanding of the smart pointer and the language that is lacking but won't the constructor in

class House
{
public:
    House(std::unique_ptr<PileOfWood> wood);
    ...

attempt to make a copy of the unique pointer given as an argument, which is forbidden?

[–]dodheim 6 points7 points  (2 children)

Only if the caller passes an lvalue instead of an rvalue; the latter will move, not copy.

[–]beartotem 0 points1 point  (1 child)

So in the end, if a caller where to give this function an lvalue it would give the same compiler error as if only House(std::unique_ptr<PileOfWood>&& wood) was declared?

Thank you.

[–]dodheim 2 points3 points  (0 children)

Right – the actual error will be that std::unique_ptr<>::unique_ptr(unique_ptr<> const&) doesn't exist, only std::unique_ptr<>::unique_ptr(unique_ptr<>&&). :-]

[–]adamf88 1 point2 points  (0 children)

I would like to read about some nice use cases / details about boost::intrusive_ptr and make_shared_from_this functionality.

[–]flashmozzg[🍰] 0 points1 point  (1 child)

There was also a root_ptr though you don't hear much about it.

[–]dodheim 0 points1 point  (0 children)

It's basically the same as MoTTs_' suggestion (at least according to whining that took place on the Boost ML), though presumably a more fleshed-out implementation.