Is World's Dumbest Pointer still on the table?!?

426 views
Skip to first unread message

mihailn...@gmail.com

unread,
Oct 18, 2016, 6:03:53 AM10/18/16
to ISO C++ Standard - Future Proposals

I though, it was settled, raw pointers will be serving as non-owning pointers? (and use references, as well not_null<> and owner<> to cover corner cases and legacy)

Also, observer_ptr is not without problems too.

For instance, it has lifetime management API (reset, release) although it does not manage lifetime!

Right now if you see reset or release in code, it means the object lifetime has been altered - either deleted, a ref dropped or no longer managed.
observer_ptr will change that for the worse - these will start to mean something different.
 
Also, it can be well misleading, it monitors object lifetime - after all other smart pointers control lifetime, this one is probably just observing it.

Ville Voutilainen

unread,
Oct 18, 2016, 7:53:59 AM10/18/16
to ISO C++ Standard - Future Proposals
On 18 October 2016 at 13:03, <mihailn...@gmail.com> wrote:
> I was surprised to see
> http://en.cppreference.com/w/cpp/experimental/observer_ptr
>
> I though, it was settled, raw pointers will be serving as non-owning
> pointers? (and use references, as well not_null<> and owner<> to cover
> corner cases and legacy)

I don't know what authority settled that point for every codebase on the planet.

> Also, observer_ptr is not without problems too.
>
> For instance, it has lifetime management API (reset, release) although it
> does not manage lifetime!
> Right now if you see reset or release in code, it means the object lifetime
> has been altered - either deleted, a ref dropped or no longer managed.
> observer_ptr will change that for the worse - these will start to mean
> something different.

I don't consider reset and release to necessarily manage lifetime.

> Also, it can be well misleading, it monitors object lifetime - after all
> other smart pointers control lifetime, this one is probably just observing
> it.

It doesn't observe lifetime.

Bo Persson

unread,
Oct 18, 2016, 8:17:07 AM10/18/16
to std-pr...@isocpp.org
> other smart pointers /control /lifetime, this one is probably just
> /observing/ it.
>


Having similar functions use the same names as functions of the slighlty
smarter pointers, might help replacing those with an observer_pointer.
Changing "release" into "stop_observing" (or something) doesn't appeal
to me.


If we look at a similar problem in merging "optional", "variant", and
"any" into the draft standard, we ended up with two of them having a
"bool has_value()" function, while the third got a "bool
valueless_by_exception()".

Not only do they have different names, but also different true/false
return value for the same(?) condition - does it store anything.

I think the "release" function for the pointer types is similar enough -
give me the pointer, I'll take care of it!


Bo Persson


mihailn...@gmail.com

unread,
Oct 18, 2016, 9:08:04 AM10/18/16
to ISO C++ Standard - Future Proposals, b...@gmb.dk
In any case, for a smart pointer, reset and release are lifetime management interfaces.

observer_ptr changes this.

Also note, owner<> and not_null<> do not have these also!

I don't propose changing the name, because I don't believe observer_ptr existence is justified.
Not after 3+ years of evangelism how "row pointers are just fine, you should use them for non-owners", something I completely agree!

Ville Voutilainen

unread,
Oct 18, 2016, 9:17:29 AM10/18/16
to ISO C++ Standard - Future Proposals
On 18 October 2016 at 16:08, <mihailn...@gmail.com> wrote:
> In any case, for a smart pointer, reset and release are lifetime management
> interfaces.
>
> observer_ptr changes this.

But yet its users are unlikely to find this a problem, because some of
them don't consider
reset and release to be lifetime management interfaces.

> Also note, owner<> and not_null<> do not have these also!

Well, of course not, because they are raw pointers.

> I don't propose changing the name, because I don't believe observer_ptr
> existence is justified.

I have no trouble finding users who don't agree with that.

> Not after 3+ years of evangelism how "row pointers are just fine, you should
> use them for non-owners", something I completely agree!

..and it's your choice whether you take such evangelism to be the whole truth.

mihailn...@gmail.com

unread,
Oct 18, 2016, 9:44:40 AM10/18/16
to ISO C++ Standard - Future Proposals


On Tuesday, October 18, 2016 at 4:17:29 PM UTC+3, Ville Voutilainen wrote:
On 18 October 2016 at 16:08,  <mihailn...@gmail.com> wrote:
> In any case, for a smart pointer, reset and release are lifetime management
> interfaces.
>
> observer_ptr changes this.

But yet its users are unlikely to find this a problem, because some of
them don't consider
reset and release to be lifetime management interfaces.

reset and release is lifetime interface in the context of a smart pointer -  if you touch these, the lifetime of the object will change.
That's a fact, not a point a view.

observer_ptr pretends to be smart with risk of being harmful and/or confusing.

is this safe?

p.reset(new int{});

Right now - yes!, with any smart pointer.

With observer_ptr into the picture we have to say - "it depends, go look up the type of p".
 

> Also note, owner<> and not_null<> do not have these also!

Well, of course not, because they are raw pointers.


If you meant "raw" as in a C pointer, only owner<> is raw, not_null is a class.
But yes, they are not lifetime managers... but observer is also not one.

Ville Voutilainen

unread,
Oct 18, 2016, 9:57:58 AM10/18/16
to ISO C++ Standard - Future Proposals
On 18 October 2016 at 16:44, <mihailn...@gmail.com> wrote:
> is this safe?
>
> p.reset(new int{});
>
> Right now - yes!, with any smart pointer.

No, because you have now given your shared_ptr an object with a
default deleter, which might
not be what your program expected. You have also decreased the
reference count of the original
object, which might again not be what your program expected.

It might not be safe for a unique_ptr either, depending on what your
deleter type is.

Tony V E

unread,
Oct 18, 2016, 10:40:47 AM10/18/16
to Standard Proposals
On Tue, Oct 18, 2016 at 9:44 AM, <mihailn...@gmail.com> wrote:


On Tuesday, October 18, 2016 at 4:17:29 PM UTC+3, Ville Voutilainen wrote:
On 18 October 2016 at 16:08,  <mihailn...@gmail.com> wrote:
> In any case, for a smart pointer, reset and release are lifetime management
> interfaces.
>
> observer_ptr changes this.

But yet its users are unlikely to find this a problem, because some of
them don't consider
reset and release to be lifetime management interfaces.

reset and release is lifetime interface in the context of a smart pointer -  if you touch these, the lifetime of the object will change.
That's a fact, not a point a view.

release() doesn't change the object's lifetime for unique_ptr - it hands the pointer back to you, still alive.  It does change the ownership. It maybe should have been called detach(), if we wanted to have a distinction here.  shared_ptr doesn't have a release().

unique_ptr's release appears to be an evolution of auto_ptr's.  Which also "detached"


The first 4 "detach" - ie break the association, without changing the underlying object lifetime/state/refcount

The last 2 actually release the memory.

This is unfortunate deviation of the word "release", maybe the last 2 should be changed?

Outside the standard, release() usually means free() or decr-refcount etc, so auto_ptr didn't set a precedent outside the standard unfortunately :-(


(There's also memory_order_release but I think the context there is different enough to not cause confusion)


--
Be seeing you,
Tony

mihailn...@gmail.com

unread,
Oct 18, 2016, 11:36:22 AM10/18/16
to ISO C++ Standard - Future Proposals


On Tuesday, October 18, 2016 at 5:40:47 PM UTC+3, Tony V E wrote:


On Tue, Oct 18, 2016 at 9:44 AM, <mihailn...@gmail.com> wrote:


On Tuesday, October 18, 2016 at 4:17:29 PM UTC+3, Ville Voutilainen wrote:
On 18 October 2016 at 16:08,  <mihailn...@gmail.com> wrote:
> In any case, for a smart pointer, reset and release are lifetime management
> interfaces.
>
> observer_ptr changes this.

But yet its users are unlikely to find this a problem, because some of
them don't consider
reset and release to be lifetime management interfaces.

reset and release is lifetime interface in the context of a smart pointer -  if you touch these, the lifetime of the object will change.
That's a fact, not a point a view.

release() doesn't change the object's lifetime for unique_ptr - it hands the pointer back to you, still alive.

Well, release() changes the lifetime to "live until program termination." and I don't have a problem with the name at all (or any problem with shared or unique ptr).

About the many things called release - they are different set of classes, they can have different meaning what release mean.
I have no problem, optional has reset for instance.

The problem is, until now, only the memory managing pointers have this interface and this very interface already means something.
If you see today reset (on a pointer), it means only one thing, be it std, Qt or any other smart pointer.

observer changes this, overloads what reset means, all that for no good enough reason (by a long shot).

Ville Voutilainen

unread,
Oct 18, 2016, 11:45:50 AM10/18/16
to ISO C++ Standard - Future Proposals
On 18 October 2016 at 18:36, <mihailn...@gmail.com> wrote:
>> release() doesn't change the object's lifetime for unique_ptr - it hands
>> the pointer back to you, still alive.
> Well, release() changes the lifetime to "live until program termination."

Depends on how the resource was acquired.

> The problem is, until now, only the memory managing pointers have this
> interface and this very interface already means something.
> If you see today reset (on a pointer), it means only one thing, be it std,
> Qt or any other smart pointer.

I can quite easily write such a deleter that reset() on a shared_ptr
and on a unique_ptr
will do exactly what observer_ptr's reset() does.

> observer changes this, overloads what reset means, all that for no good
> enough reason (by a long shot).

Well, reset() means what it always meant, it resets the handle to have
some other pointer or none.
The same logic applies to release(). As far as reasons go,
observer_ptr remains superior
to things like owner<T>, since it says nothing about ownership
semantics and is not a pointer.
The latter bit is the most useful characteristic of observer_ptr, and
while it has downsides for some
cases, that's a tolerable trade-off to make for quite many audiences.
The former bit is surprisingly
handy as well.

Tony V E

unread,
Oct 18, 2016, 12:29:26 PM10/18/16
to Standard Proposals
On Tue, Oct 18, 2016 at 8:16 AM, Bo Persson <b...@gmb.dk> wrote:


Having similar functions use the same names as functions of the slighlty smarter pointers, might help replacing those with an observer_pointer. Changing "release" into "stop_observing" (or something) doesn't appeal to me.


If we look at a similar problem in merging "optional", "variant", and "any" into the draft standard, we ended up with two of them having a "bool has_value()" function, while the third got a "bool valueless_by_exception()".

Not only do they have different names, but also different true/false return value for the same(?) condition - does it store anything.


This was very intentional.  optional and any are expected to be empty, variant is not.  My personal suggestion is to never call valueless_by_exception().  Depends on your codebase and type of product, but in most cases, if your variant got into the valueless state, you have bigger problems to worry about.



   Bo Persson

mihailn...@gmail.com

unread,
Oct 18, 2016, 12:33:28 PM10/18/16
to ISO C++ Standard - Future Proposals


On Tuesday, October 18, 2016 at 6:45:50 PM UTC+3, Ville Voutilainen wrote:

> observer changes this, overloads what reset means, all that for no good
> enough reason (by a long shot).

Well, reset() means what it always meant, it resets the handle to have
some other pointer or none.

It was never just about the handle, it was always about the object lifetime.

Let me point of the irony here.

We can have unique_ptr to consume a row pointer just with assignment - we have overloading for that.
But we don't do it. We invent a function.
This function is called reset
It is there to state intend.
It is there to say, "take this object and manage it for me, you are the owner now".
And from this point onwards, every well behaved smart pointer library uses this idiom.
You can read 10+ years old code and reset will be there, saying the same thing.

Now comes a pointer which stops obeying this idiom.
It starts using reset() as operator=
From this point onwards when encountering reset (on a pointer) is code is ambiguous -
"Does it transfer or does it not? Better dig out the definition of the pointer?"

So we invited ourselves the reverse problem reset() solved decades ago.

Where before we prevented the ambiguity of operator=(T*).
Now we introduce ambiguity in reset().

All this for a dumb pointer trying to be smart.

Nicol Bolas

unread,
Oct 18, 2016, 12:35:54 PM10/18/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
On Tuesday, October 18, 2016 at 6:03:53 AM UTC-4, mihailn...@gmail.com wrote:

I though, it was settled, raw pointers will be serving as non-owning pointers? (and use references, as well not_null<> and owner<> to cover corner cases and legacy)

Who settled this? The C++ Core Guidelines? Does everyone follow those, uniformly, across all applications?

Some people believe that a codebase should do away with raw pointers entirely. For them, `observer_ptr` is the correct tool.

Now granted, I would much rather we had `owner` and `not_null` in the standard library than `observer_ptr`. But right now, even the latter is just part of Library Fundamentals v2, rather than the standard library. So while it is "on the table", that's hardly a free ride into the standard library.

Ville Voutilainen

unread,
Oct 18, 2016, 12:43:11 PM10/18/16
to ISO C++ Standard - Future Proposals
On 18 October 2016 at 19:33, <mihailn...@gmail.com> wrote:
>> > observer changes this, overloads what reset means, all that for no good
>> > enough reason (by a long shot).
>>
>> Well, reset() means what it always meant, it resets the handle to have
>> some other pointer or none.
>
>
> It was never just about the handle, it was always about the object lifetime.

I don't know how many times I need to point out that reset() on a
unique_ptr might have no
effect on the object lifetime, depending on the deleter.

mihailn...@gmail.com

unread,
Oct 18, 2016, 1:02:47 PM10/18/16
to ISO C++ Standard - Future Proposals

On Tuesday, October 18, 2016 at 7:43:11 PM UTC+3, Ville Voutilainen wrote:
..


I don't know how many times I need to point out that reset() on a
unique_ptr might have no
effect on the object lifetime, depending on the deleter.


std::unique_ptr is a smart pointer that retains sole ownership of an object through a pointer and destroys that object when the unique_ptr goes out of scope. No two unique_ptr instances can manage the same object.

The object is destroyed and its memory deallocated when either of the following happens:
 the managing unique_ptr object is destroyed
 the managing unique_ptr object is assigned another pointer via operator= or reset().

The object is destroyed using a potentially user-supplied deleter by calling Deleter(ptr). The deleter calls the destructor of the object and dispenses the memory.

All else is a naughty corner case. 

Ville Voutilainen

unread,
Oct 18, 2016, 1:06:42 PM10/18/16
to ISO C++ Standard - Future Proposals
All else is subject to the semantics of any possible custom deleter
provided, and none of that nonsense
on cppreference applies to such custom deleters. The standard doesn't
make such incorrect claims about
how unique_ptr works.

T. C.

unread,
Oct 18, 2016, 1:48:01 PM10/18/16
to ISO C++ Standard - Future Proposals
Fixed, I think. 

mihailn...@gmail.com

unread,
Oct 18, 2016, 3:16:40 PM10/18/16
to ISO C++ Standard - Future Proposals


On Tuesday, October 18, 2016 at 8:06:42 PM UTC+3, Ville Voutilainen wrote:
...


All else is subject to the semantics of any possible custom deleter
provided, and none of that nonsense
on cppreference applies to such custom deleters. The standard doesn't
make such incorrect claims about
how unique_ptr works.

No matter what the deleter does, reset is added to make it explicit, the deleter will be called both on the old object (if any) and at some later time on the new one (if any).

The sole reason reset, as a function, exist is to donate this functionality. No other reason.
With shared ptr is practically the same similar, just the deleter call might be delayed, again no matter what the deleter does.

This is, until observer_ptr comes and reintroduce the very same ambiguity, reset solved - does it call my (magic) deleter or does it not.

Nicol Bolas

unread,
Oct 18, 2016, 4:09:48 PM10/18/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
On Tuesday, October 18, 2016 at 9:44:40 AM UTC-4, mihailn...@gmail.com wrote:
On Tuesday, October 18, 2016 at 4:17:29 PM UTC+3, Ville Voutilainen wrote:
On 18 October 2016 at 16:08,  <mihailn...@gmail.com> wrote:
> In any case, for a smart pointer, reset and release are lifetime management
> interfaces.
>
> observer_ptr changes this.

But yet its users are unlikely to find this a problem, because some of
them don't consider
reset and release to be lifetime management interfaces.

reset and release is lifetime interface in the context of a smart pointer -  if you touch these, the lifetime of the object will change.
That's a fact, not a point a view.

No, it is a point of view.

To hold onto a smart pointer object means that you expect the object pointed to by the smart pointer to continue to exist, pursuant to the smart pointer's contract. A `weak_ptr` does not ensure the lifetime of the pointed-to object. What it does ensure is that, if that lifetime has ended, you'll know about it. You may get a NULL pointer, but you'll never get an invalid one.

When you `reset` a smart pointer, you are saying that you no longer expect that object to exist. Whether or not it is actually destoryed is smart-pointer specific. `shared_ptr::reset` doesn't guarantee the object will be destroyed. `weak_ptr::reset` doesn't destroy anything at all.

By calling `reset`, you are making a contract with the smart pointer. From that point forward, you will not do anything to access the object referenced by that smart pointer, nor any object who's lifetime depends on it, unless you have a guarantee from some other object. That is, as far as this particular smart pointer is concerned, it is not going to ensure anything.

`release` isn't even a function that most smart pointers have.

observer_ptr pretends to be smart with risk of being harmful and/or confusing.

To whom is it harmful or confusing? Are you actually trying to claim that someone is going to assume that a smart pointer with the word "observer" in it is going to manage memory?

Anyone who does that deserves what they get.

The point of `observer_ptr` is to represent when a function is being given a pointer it does not have ownership of.

mihailn...@gmail.com

unread,
Oct 18, 2016, 5:37:57 PM10/18/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


On Tuesday, October 18, 2016 at 11:09:48 PM UTC+3, Nicol Bolas wrote:
On Tuesday, October 18, 2016 at 9:44:40 AM UTC-4, mihailn...@gmail.com wrote:
...

No, it is a point of view.

To hold onto a smart pointer object means that you expect the object pointed to by the smart pointer to continue to exist, pursuant to the smart pointer's contract. A `weak_ptr` does not ensure the lifetime of the pointed-to object. What it does ensure is that, if that lifetime has ended, you'll know about it. You may get a NULL pointer, but you'll never get an invalid one.

When you `reset` a smart pointer, you are saying that you no longer expect that object to exist. Whether or not it is actually destoryed is smart-pointer specific. `shared_ptr::reset` doesn't guarantee the object will be destroyed. `weak_ptr::reset` doesn't destroy anything at all.

reset on a waek_ptr does not take a pointer! It does not overload the meaning of reset!
 

By calling `reset`, you are making a contract with the smart pointer. From that point forward, you will not do anything to access the object referenced by that smart pointer, nor any object who's lifetime depends on it, unless you have a guarantee from some other object. That is, as far as this particular smart pointer is concerned, it is not going to ensure anything.

reset purpose is to re-set the smart pointer to a new value.

reset always, to this point, controls the deleter call - one way or another (right away call, pointer dtor call, use_count etc, etc does not matter).
This is what a smart pointer ensures, this is what is designed for.  

Same with release - touching it, means you are touch the deleter call, when and if it runs.  

So, right now and for many, many years, these are lifetime management APIs.
 

`release` isn't even a function that most smart pointers have.

observer_ptr pretends to be smart with risk of being harmful and/or confusing.

To whom is it harmful or confusing? Are you actually trying to claim that someone is going to assume that a smart pointer with the word "observer" in it is going to manage memory?

O, yes I do. The moment a lib typedefs these, you are left to wonder what's up with all those reset-s and release-s.

And what about auto? The explicit  type is less and less visible in modern code.

Even if not typedefed, even if you learn the types, you will have to constantly remind yourself, "is this is observer or is it not".

You look a function and an observer is used.
Then you switch to another function, you see exactly the same code, but this time it is not an observer and you forgot to look up the type.
You spend 10 min consuming the code in the wrong way, thinking it does one thing, but it does something different.

This has the exact same problem as if one overloads a smart pointer operator= on the managed type.

Two completely different actions (pointer copy and deleter management) are expressed by visually the same code.
Sure, the type is different, but in this case this is not enough - you better use different language for different things.

reset is that different language.


Anyone who does that deserves what they get.

The point of `observer_ptr` is to represent when a function is being given a pointer it does not have ownership of.

As commented, raw pointers are left for that use, and that use only. No confusion if owner<> is used instead. No overloading of operations. No new concepts.

And BTW owner<> is better, because it signals the unordinary (in modern code) case, it is labeled, because it is special.
Raw pointer is not labeled, he is not special.

Nicol Bolas

unread,
Oct 18, 2016, 6:31:14 PM10/18/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
On Tuesday, October 18, 2016 at 5:37:57 PM UTC-4, mihailn...@gmail.com wrote:
On Tuesday, October 18, 2016 at 11:09:48 PM UTC+3, Nicol Bolas wrote:
On Tuesday, October 18, 2016 at 9:44:40 AM UTC-4, mihailn...@gmail.com wrote:
...
No, it is a point of view.

To hold onto a smart pointer object means that you expect the object pointed to by the smart pointer to continue to exist, pursuant to the smart pointer's contract. A `weak_ptr` does not ensure the lifetime of the pointed-to object. What it does ensure is that, if that lifetime has ended, you'll know about it. You may get a NULL pointer, but you'll never get an invalid one.

When you `reset` a smart pointer, you are saying that you no longer expect that object to exist. Whether or not it is actually destoryed is smart-pointer specific. `shared_ptr::reset` doesn't guarantee the object will be destroyed. `weak_ptr::reset` doesn't destroy anything at all.

reset on a waek_ptr does not take a pointer! It does not overload the meaning of reset!

`reset` on all pointer types doesn't have to be given a pointer. The parameter is optional; if you pass nothing, then the value of the pointer becomes NULL.

By calling `reset`, you are making a contract with the smart pointer. From that point forward, you will not do anything to access the object referenced by that smart pointer, nor any object who's lifetime depends on it, unless you have a guarantee from some other object. That is, as far as this particular smart pointer is concerned, it is not going to ensure anything.

reset purpose is to re-set the smart pointer to a new value.

And in so doing, you can no longer use the old value.

Consider the following.

auto *ptr = smart_ptr_from_somewhere.get();
smart_ptr_from_somewhere
.reset();
ptr
->stuff();

Is this reasonable code? Regardless of the type of `smart_ptr_from_somewhere`, I submit that it is not reasonable. It may work, depending on the exact type of the smart pointer. But it is never reasonable code.

If the smart pointer is `shared_ptr`, it might work. It might not. If the smart pointer is an `observer_ptr`, then it will work. If the smart pointer is a `unique_ptr`, it will almost certainly not work.

But regardless of whether it "works", doing this is a violation of the idea of resetting a smart pointer. By resetting one, you are releasing your hold over that object. Whether that actually destroys the object or not is essentially irrelevant. You are saying that you can no longer access a resource through a particular pointer anymore. And therefore, the smart pointer is free to do any cleanup. Or not do any, whatever the case may be.

Your point of view is founded on what the code is actually doing. My point of view is founded in what it means conceptually. And that's really what a reader of this code cares about. `reset` means that the user isn't going to use it anymore, not "delete this right now".

If the above code were submitted to code review, I would say that the reader should reject it regardless of what kind of smart pointer it is. The reader should not look up the pointer type, and check various other things to see if the object is live or some such.

The problem is not that the code might be broken. The problem is that the code always doesn't make sense.
 
Same with release - touching it, means you are touch the deleter call, when and if it runs.  

No. `unique_ptr::release` means that you are now in control of, and therefore assume the responsibility of, releasing the resource managed by the object.

Now, I agree that `observer_ptr` should not have such a function. But only because it conceptually makes no more sense for an `observer_ptr` to be able to release the pointer than it does for a `shared_ptr` to do so. It cannot release ownership that it does not possess.

`release` isn't even a function that most smart pointers have.

observer_ptr pretends to be smart with risk of being harmful and/or confusing.

To whom is it harmful or confusing? Are you actually trying to claim that someone is going to assume that a smart pointer with the word "observer" in it is going to manage memory?

O, yes I do. The moment a lib typedefs these, you are left to wonder what's up with all those reset-s and release-s.

Even if I agree that `reset` on an observer_ptr creates confusion, I submit that this confusion will be highly localized. Why?

Because at the end of the day, `reset` is not a commonly used function.

Really, how many times have you actually `reset` a smart pointer object explicitly? Usually, you copy/move into them, typically from `make_unique/shared` or with some other pointer you've been given. The most common use is to create them on the stack/as member variables, initialize them, and let the scope/destructor clean them up.

Oh sure, `reset` happens. But it's hardly the most common usage of smart pointers. So I won't be wondering about "all those" calls to a rarely used function.
 
You look a function and an observer is used.
Then you switch to another function, you see exactly the same code, but this time it is not an observer and you forgot to look up the type.
You spend 10 min consuming the code in the wrong way, thinking it does one thing, but it does something different.

It's no more different than wondering if `shared_ptr::reset` is destroying the object or not.

This has the exact same problem as if one overloads a smart pointer operator= on the managed type.

No, it does not. That is about preventing the implicit adopting and managing a pointer which was not meant to be managed. It has nothing to do with the implicit ending of the management of the existing object.

Anyone who does that deserves what they get.

The point of `observer_ptr` is to represent when a function is being given a pointer it does not have ownership of.

As commented, raw pointers are left for that use, and that use only.

You declared this, but with no proof. Yes, the C++ core guidelines lays this out, but they are not the Gods of All C++ Programming. They are not a Holy Text brought down from the mountain top. They are not divinely inspired wisdom. They're just a set of rules.

While I certainly prefer their use of raw pointers, if someone else has a different use, that's up to them and their codebase. I lack the arrogance to declare that they are wrong to want to go a different way.

FrankHB1989

unread,
Oct 18, 2016, 10:19:36 PM10/18/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


在 2016年10月19日星期三 UTC+8上午5:37:57,mihailn...@gmail.com写道:


By calling `reset`, you are making a contract with the smart pointer. From that point forward, you will not do anything to access the object referenced by that smart pointer, nor any object who's lifetime depends on it, unless you have a guarantee from some other object. That is, as far as this particular smart pointer is concerned, it is not going to ensure anything.

reset purpose is to re-set the smart pointer to a new value.

reset always, to this point, controls the deleter call - one way or another (right away call, pointer dtor call, use_count etc, etc does not matter).
This is what a smart pointer ensures, this is what is designed for.  

The name `reset` is not only used on smart pointers. Literally, it has nothing to do with lifetime of other objects. The additional meaning is granted by the class type.
 
Same with release - touching it, means you are touch the deleter call, when and if it runs.  

So, right now and for many, many years, these are lifetime management APIs.

I agree `release` is a poor name here, it should have been `detach` or something else to reduce confusion with genuine lifetime management.

But for smart pointers, it is not about lifetime management. Smart pointers are even not necessarily involved with controlling the lifetime.

 

`release` isn't even a function that most smart pointers have.

observer_ptr pretends to be smart with risk of being harmful and/or confusing.

To whom is it harmful or confusing? Are you actually trying to claim that someone is going to assume that a smart pointer with the word "observer" in it is going to manage memory?

O, yes I do. The moment a lib typedefs these, you are left to wonder what's up with all those reset-s and release-s.

And what about auto? The explicit  type is less and less visible in modern code.

Even if not typedefed, even if you learn the types, you will have to constantly remind yourself, "is this is observer or is it not".

You look a function and an observer is used.
Then you switch to another function, you see exactly the same code, but this time it is not an observer and you forgot to look up the type.
You spend 10 min consuming the code in the wrong way, thinking it does one thing, but it does something different.

This has the exact same problem as if one overloads a smart pointer operator= on the managed type.

Two completely different actions (pointer copy and deleter management) are expressed by visually the same code.
Sure, the type is different, but in this case this is not enough - you better use different language for different things.

reset is that different language.

You misread what is "smart".

It is "smart" because as a designer of the interface, you can attach intended additional behavior to something already like pointers. That's all. No magic about lifetime management is guaranteed existed; it is up to the designer of the API.
 

Anyone who does that deserves what they get.

The point of `observer_ptr` is to represent when a function is being given a pointer it does not have ownership of.

As commented, raw pointers are left for that use, and that use only. No confusion if owner<> is used instead. No overloading of operations. No new concepts.

And BTW owner<> is better, because it signals the unordinary (in modern code) case, it is labeled, because it is special.
Raw pointer is not labeled, he is not special.
Raw pointer is simply ill-designed in aspect of the need of a general-purposed language. It is essentially a kind of the sum type of random access iterators and non-owning smart pointer types. The rest thing it provides is the ease of binary interops, which is a kind of leaked abstraction from the underlying implementation, and not guaranteed to work even in the current language design. Explicit address types may be better for the last task.

The sin of raw pointer is, it is too special. It has coupled many unrelated functionalities (as portable abstraction) as a bundle in the core language (as well polluting the binary interface continuously). To label it to get the right functionality is in the sorts of workaround, not the fix, even this approach can easily play well with modern language implementations. So there should be a place for the nominal (named) non-owning pointer in a right interface design.

Another thing to be noted: you should normally fix the ownership requirements statically during the design of API, and labeling some non-owning pointers to change them as owning ones is essentially risky and difficult to do correctly. Even you have to do so, `owner<>` is not necessarily better. Anyway, to patch it with `owner<>` or convert it to some owning pointer types to express ownership is the further step, based on the fact that you have already precisely expressed non-onwership - and raw pointer is not the correct type at the first place.


 

mihailn...@gmail.com

unread,
Oct 19, 2016, 4:30:37 AM10/19/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
Ok,
I hope we all agree reset(T*) and release() have new meaning in the context of a observer_ptr. Ok?

For you, it is not a problem - the class is free to add whatever meaning it wants to.

I simply argue, it can well be a problem and potentially adding a confusion.

auto p = foo();

if(cond(p))
 p
.reset(hello.getBar());



All I am saying, this code above, for current smart pointers, meant - p now has the bar object.

With observer_ptr into the picture, it might also mean - p now points to a bar.

There is some overloading, which I personally find unfortunate and not well justified.

That's all.

-

Now lets look at release, which has also a different problem.

std::unique_ptr::release
std::shared_lock::release
std::unique_lock::release
std::pmr::monotonic_buffer_resource::release
std::pmr::unsynchronized_pool_resource::release


What is common b/w these all?

They all give up a resource, which affects other objects in the system.

In general, right now, when you see release() in code, something quite important, worth noting is happening.
Something possibly requiring some other actions to take care of.

None of these are trivial operation.

observer_ptr makes it trivial.
It makes it only about itself, it does not affect other objects and state.

Ok, for you it might not be a problem.

I only argue, it might well be - release() statement in code loses from its importance.

The important uses of release() will be accompanied of trivial uses of it.
Not a good thing.


FrankHB1989

unread,
Oct 19, 2016, 5:38:30 AM10/19/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


在 2016年10月19日星期三 UTC+8下午4:30:37,mihailn...@gmail.com写道:
Ok,
I hope we all agree reset(T*) and release() have new meaning in the context of a observer_ptr. Ok?

For you, it is not a problem - the class is free to add whatever meaning it wants to.

I simply argue, it can well be a problem and potentially adding a confusion.

auto p = foo();

if(cond(p))
 p
.reset(hello.getBar());



All I am saying, this code above, for current smart pointers, meant - p now has the bar object.

With observer_ptr into the picture, it might also mean - p now points to a bar.

For current smart pointers in the standard, it is also true. The assumption is just further weaken (correctly).
 
There is some overloading, which I personally find unfortunate and not well justified.

If you do need the meaning you want, you can introduce a non-member function template only for smart pointers having such property, with a clearer, unambiguous name, e.g. `change_owning`, rather than `reset`.


 
That's all.

-

Now lets look at release, which has also a different problem.

std::unique_ptr::release
std::shared_lock::release
std::unique_lock::release
std::pmr::monotonic_buffer_resource::release
std::pmr::unsynchronized_pool_resource::release


What is common b/w these all?

They all give up a resource, which affects other objects in the system.

In general, right now, when you see release() in code, something quite important, worth noting is happening.
Something possibly requiring some other actions to take care of.

None of these are trivial operation.

observer_ptr makes it trivial.
It makes it only about itself, it does not affect other objects and state.

Ok, for you it might not be a problem.

I only argue, it might well be - release() statement in code loses from its importance.

The important uses of release() will be accompanied of trivial uses of it.
Not a good thing.

The problem here is the name clashes within different use cases, whether taking observer_ptr into account or not. You have to do further (on the returned value from the smart pointers' `release()`) to make them similar, that's already somewhat awful (error-prone) if both styles are used in the same codebase.

I do want to "detach" smart pointers or locks. But for backward compatibility, perhaps it is easier to rename the latter ones.

 

mihailn...@gmail.com

unread,
Oct 19, 2016, 5:58:55 AM10/19/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


On Wednesday, October 19, 2016 at 12:38:30 PM UTC+3, FrankHB1989 wrote:


在 2016年10月19日星期三 UTC+8下午4:30:37,mihailn...@gmail.com写道:
Ok,
I hope we all agree reset(T*) and release() have new meaning in the context of a observer_ptr. Ok?

For you, it is not a problem - the class is free to add whatever meaning it wants to.

I simply argue, it can well be a problem and potentially adding a confusion.

auto p = foo();

if(cond(p))
 p
.reset(hello.getBar());



All I am saying, this code above, for current smart pointers, meant - p now has the bar object.

With observer_ptr into the picture, it might also mean - p now points to a bar.

For current smart pointers in the standard, it is also true. The assumption is just further weaken (correctly).
 ...

Is it, really? For both (all?) smart pointers today, the meaning is the same - p is now master of bar. The only difference is, is this ownership shared with other p clones or not.
Where in the observer case, it is something quite different.

Again, it might well not be a problem, but I don't believe it is a difference to be taken lightly.

FrankHB1989

unread,
Oct 19, 2016, 6:29:04 AM10/19/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


在 2016年10月19日星期三 UTC+8下午5:58:55,mihailn...@gmail.com写道:
No. A smart pointer is not necessarily owning, even if all instances in the current standard are owning. See also https://en.wikipedia.org/wiki/Smart_pointer, esp. external links.
 

mihailn...@gmail.com

unread,
Oct 19, 2016, 6:42:04 AM10/19/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
Of course, but the case in point is all the code written against the current notions, from both std and other implementations like Qt, and the effect observer will have.
Mind you, I don't argue, observer does something wrong by itself, if taken in isolation.
I only argue, in the context of older pointer types, this overload use of the old interface is something rather questionable.

joseph....@gmail.com

unread,
Oct 19, 2016, 1:05:07 PM10/19/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
Something like observer_ptr should exist. Use of raw pointers is simply too overloaded. Even if you buy the argument that, in modern C++, raw pointers have a single accepted use as non-owning references (and I don't**), something like observer_ptr narrows the range of operations to what is sensible for such a reference type (e.g. no pointer arithmetic). However, I believe the design of observer_ptr is not well tailored to its purported purpose:
  • It is cumbersome to use: any pointer can be "observed" safely, so why do I have to use make_observer or reset to change what I'm observing? This seems to be due to observer_ptr being based on unique_ptr (hence the inappropriate release function) which understandably wants to prevent accidental assignment of arbitrary raw pointers, since it takes ownership of whatever it holds. The API is appropriate for an owning pointer, not an observing pointer (p.s. the presence of a reset function would be a bike shed issue if direct assignment were possible).
  • It has a null state: this is sometimes desirable, but references are frequently used instead of pointers to "observe" something while precluding the possibility of not observing anything. Of course, pointers can be used, but care must be taken not to dereference a null pointer; it is better (IMO) to have compile-time assurance that your reference type cannot be null. Where the the null-less counterpart to observer_ptr?
  • It is not const-correct: pointers circumvent the "const-correct" nature of the C++ type system; a const pointer is not implicitly a pointer to const. This should be the default behaviour of any "observer" type. In fact, this should be the behaviour of any "owner" type as well; I feel as though unique_ptr and shared_ptr should have really been called unique_owner and shared_owner, and should have exhibited const-correct behaviour. That said, I can see value in the flexibility of pointer-like owning types, so perhaps we are in need of both sets of types (that is, if propagate_const cannot solve the issue satisfactorily; I'm not yet sure it can).

I am aware of the existence of not_null in the GSL, as well as the proposed propagate_const wrapper. However, I'm not sure how I feel about littering my code with these beauties:


  propagate_const<not_null<observer_ptr<T*>>> obs = make_observer(&obj);


Imagine the compiler errors. In addition, there are a number of other issues:
  • not_null may not support smart pointers.
  • not_null isn't necessarily guaranteed to not be null, and may have run-time overhead if it is.
  • not_null isn't proposed for inclusion in the standard AFAIK.
  • propagate_const disables all copying, even when it is technically possible (e.g. T* to T* or T* const to T const*), which is hugely limiting when dealing with "observer" types.
  • propagate_const is designed with a very limited "pointer-like" interface; it is likely to be of limited use for "smart wrappers" which do not adhere to the pointer model (a problem if you want a null-less counterpart to observer_ptr).

What's more, you have to ask, why are we starting with a type which is ill-fitting to model a const-correct, not-null observer type (T*) and then layering it with various wrappers designed to mould it into something which fits the bill? Wouldn't it be better to design separate wrapper types which just do the job?


  observer<T> obs = obj;

  optional_observer<T> opt_obs = obj;


This is infinitely more desirable IMO. It's worth noting that, while is dislike the idea of using not_null for anything other than sanitizing legacy code (and the designers seem to agree, seeing as they want it to support only raw pointers), I am aware (I think) of why propagate_const is designed the way it is. If propagate_const could be redesigned to be more general and flexible (and I believe it may, if the Smart References through Delegation proposal were accepted), then perhaps it would make sense to leave it the responsibility of applying const-correctness to a "smart wrapper" API.


  propagate_const<observer<T>> obs = obj;


Just my two cents.

** Raw pointers are used in low-level code for all sorts of uses; and anyway, I assume it is better to be explicit that assume people know what you mean.

mihailn...@gmail.com

unread,
Oct 19, 2016, 2:23:06 PM10/19/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, joseph....@gmail.com


On Wednesday, October 19, 2016 at 8:05:07 PM UTC+3, joseph....@gmail.com wrote:
...

  • It is cumbersome to use: any pointer can be "observed" safely, so why do I have to use make_observer or reset to change what I'm observing? This seems to be due to observer_ptr being based on unique_ptr (hence the inappropriate release function) which understandably wants to prevent accidental assignment of arbitrary raw pointers, since it takes ownership of whatever it holds. The API is appropriate for an owning pointer, not an observing pointer (p.s. the presence of a reset function would be a bike shed issue if direct assignment were possible).
  • It has a null state: this is sometimes desirable, but references are frequently used instead of pointers to "observe" something while precluding the possibility of not observing anything. Of course, pointers can be used, but care must be taken not to dereference a null pointer; it is better (IMO) to have compile-time assurance that your reference type cannot be null. Where the the null-less counterpart to observer_ptr?
  • It is not const-correct: pointers circumvent the "const-correct" nature of the C++ type system; a const pointer is not implicitly a pointer to const. This should be the default behaviour of any "observer" type. In fact, this should be the behaviour of any "owner" type as well; I feel as though unique_ptr and shared_ptr should have really been called unique_owner and shared_owner, and should have exhibited const-correct behaviour. That said, I can see value in the flexibility of pointer-like owning types, so perhaps we are in need of both sets of types (that is, if propagate_const cannot solve the issue satisfactorily; I'm not yet sure it can).
The irony is, raw pointer + not_null OR (preferably) reference covers all 3.

 - It is trivial to use, nothing new to learn, age-old and simple proven concept, fast as hell, terse syntax
 - null state acts as an optional pointer. Else - references or not_null. And BTW not_null<> is not for sanitizing legacy code. owner<> is, but not_null is legit decoration even for modern code because there is no new alternative.
 - it is const correct - const S* will not let you call non-const functions.

Yes, it is a problem, it does much more.
But the idea is to cover all other cases with some other pointer type and leave the good old pointer only for certain scenarios.
I do understand, people will disagree - they don't want raws in upper level, in day to day object management, I get that.  

 

joseph....@gmail.com

unread,
Oct 19, 2016, 10:37:28 PM10/19/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, joseph....@gmail.com
On Thursday, 20 October 2016 02:23:06 UTC+8, mihailn...@gmail.com wrote:


On Wednesday, October 19, 2016 at 8:05:07 PM UTC+3, joseph....@gmail.com wrote:
...
  • It is cumbersome to use: any pointer can be "observed" safely, so why do I have to use make_observer or reset to change what I'm observing? This seems to be due to observer_ptr being based on unique_ptr (hence the inappropriate release function) which understandably wants to prevent accidental assignment of arbitrary raw pointers, since it takes ownership of whatever it holds. The API is appropriate for an owning pointer, not an observing pointer (p.s. the presence of a reset function would be a bike shed issue if direct assignment were possible).
  • It has a null state: this is sometimes desirable, but references are frequently used instead of pointers to "observe" something while precluding the possibility of not observing anything. Of course, pointers can be used, but care must be taken not to dereference a null pointer; it is better (IMO) to have compile-time assurance that your reference type cannot be null. Where the the null-less counterpart to observer_ptr?
  • It is not const-correct: pointers circumvent the "const-correct" nature of the C++ type system; a const pointer is not implicitly a pointer to const. This should be the default behaviour of any "observer" type. In fact, this should be the behaviour of any "owner" type as well; I feel as though unique_ptr and shared_ptr should have really been called unique_owner and shared_owner, and should have exhibited const-correct behaviour. That said, I can see value in the flexibility of pointer-like owning types, so perhaps we are in need of both sets of types (that is, if propagate_const cannot solve the issue satisfactorily; I'm not yet sure it can).
The irony is, raw pointer + not_null OR (preferably) reference covers all 3.
 
I was just listing the problems with observer_ptr; raw pointers, references and not_null each have shortcomings of their own:
  •  T* and not_null<T*> are not const-correct (i.e. const-propagating) despite your claim to the contrary
  • Depending on which pre-processor flags are set, not_null either can enter a null state or incurs a run-time cost on construction; I am watching this issue which potentially fixes this problem by allowing construction from T& (though there are still issues with this design).
  • Raw pointers (without not_null) support operations which don't make sense for an observer type; this opens the door to programming errors, bugs and UB. Assuming a raw pointer, obs, acting as an observer, all the following compile, but are conceptually unsound:
    • delete obs;
    • obs++;
    • obs + some_int;
    • obs[1];
    • obs = some_array;
  • References cannot be rebound to "observe" something else after construction; this prevents them being stored in STL containers like std::vector. And std::reference_wrapper is only a solution if you want to have reference-like behaviour rather than observer-like behaviour (e.g. operator== compares the referenced values, not the addresses of the referenced objects).
 - It is trivial to use, nothing new to learn, age-old and simple proven concept, fast as hell, terse syntax
  • "trivial to use": a well-designed set of observer wrapper types can be at least as, and probably more, trivial to use (terser, more natural syntax + added safety).
  • "nothing new to learn": I believe that the potential benefits of observer wrappers outweigh the cost of learning something new.
  • "age-old": and they are showing their age, which is why they are being increasingly replaced in modern C++; as I outlined, raw pointers have proven to be less than ideal for use in safe, modern C++ code, including where used as observers.
  • "fast as hell": observer wrapper types can easily be designed with zero run-time overhead.
  • "terse syntax": observer wrapper types can be designed with syntax that is terser in some cases than raw pointers and references; IMO, this is a natural result of them being designed for their specific purpose.
 - null state acts as an optional pointer. Else - references or not_null. And BTW not_null<> is not for sanitizing legacy code. owner<> is, but not_null is legit decoration even for modern code because there is no new alternative.

Raw pointers, references and not_null have issues; I won't repeat the details.

If not_null is not for sanitizing legacy code and is designed to work only with raw pointers, then its current design is not ideal:
 
  not_null<T*> obs = &obj;

The design can be simplified to prevent mis-parameterization (e.g. passing unique_ptr<T>) and to guarantee the lack of a null state at run-time with zero overhead (by taking a reference on construction):

  not_null<T> obs = obj;

Suddenly this is looking remarkably like a null-less observer_ptr. Now, take a step back and consider how best to design these types to complement each other, without being afraid to move away from the pointer/reference paradigm, and you will probably end up with something like this:


  observer<T> obs = obj;
  optional_observer
<T> opt_obs = obj;

Naming is an issue that can be hashed out behind the bike sheds; the important thing is that we have ended up exactly where I said we should be. This is the alternative to not_null you mentioned.

 - it is const correct - const S* will not let you call non-const functions.
 
When I say "const-correct" in the context of types which indirectly refer to other objects, is that the constness of the wrapper should propagate to the referenced object. This is the behaviour implemented by propagate_const.

Yes, it is a problem, it does much more.
But the idea is to cover all other cases with some other pointer type and leave the good old pointer only for certain scenarios.
I do understand, people will disagree - they don't want raws in upper level, in day to day object management, I get that.

Yup. And it isn't just a stylistic personal preference. I have practical reasons for wanting something other than raw pointers and references, as I hope I have made clear.

mihailn...@gmail.com

unread,
Oct 20, 2016, 3:09:16 AM10/20/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, joseph....@gmail.com
My intentions are not to change your mind - we, as well as everyone else, are perfectly aware of the pros and cons of both approaches - raw vs new ptr.

In any case, I believe we both agree the solution lies outside of observer_ptr. I have my reasons, you have your own.

We also agree, any "operator." (in quotes, not necessarily overloading it) will dramatically change any possible implementation of such a wrapper pointer type.
I also agree with you propagate_const should be integral part of such a wrapper pointer type, not a cumbersome opt-in. 
Lastly, as things like string_view / span become widely available and used, they will become a good, real world testing ground for potential "view for object" 

As a result, I (still) strongly believe observer_ptr should not go into the standard in its current form.
It will be half-assessed, and will rise as many issues/questions as it solves and will be become the auto_ptr version of a much better view type.


joseph....@gmail.com

unread,
Oct 20, 2016, 4:45:10 AM10/20/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, joseph....@gmail.com
On Thursday, 20 October 2016 15:09:16 UTC+8, mihailn...@gmail.com wrote:
My intentions are not to change your mind - we, as well as everyone else, are perfectly aware of the pros and cons of both approaches - raw vs new ptr.
 
I think it helps to have these things written down. Even if everyone else knows everything already, it helps me get my thoughts in order and realize where my reasoning is flawed.

We also agree, any "operator." (in quotes, not necessarily overloading it) will dramatically change any possible implementation of such a wrapper pointer type.

I don't agree with this. I think "operator dot overloading" will have limited to no application in this case. I believe it will have more of an application with types that act to modify select parts of an API while retaining the overall appearance of the underlying type like, for example, propagate_const.
 
I also agree with you propagate_const should be integral part of such a wrapper pointer type, not a cumbersome opt-in. 

I'm in two minds about this. It may be better to keep const propagation separate.

As a result, I (still) strongly believe observer_ptr should not go into the standard in its current form.
It will be half-assessed, and will rise as many issues/questions as it solves and will be become the auto_ptr version of a much better view type.

Agreed.

FrankHB1989

unread,
Oct 20, 2016, 6:02:02 AM10/20/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


在 2016年10月19日星期三 UTC+8下午6:42:04,mihailn...@gmail.com写道:


On Wednesday, October 19, 2016 at 1:29:04 PM UTC+3, FrankHB1989 wrote:


在 2016年10月19日星期三 UTC+8下午5:58:55,mihailn...@gmail.com写道:


On Wednesday, October 19, 2016 at 12:38:30 PM UTC+3, FrankHB1989 wrote:


在 2016年10月19日星期三 UTC+8下午4:30:37,mihailn...@gmail.com写道:
Ok,
I hope we all agree reset(T*) and release() have new meaning in the context of a observer_ptr. Ok?

For you, it is not a problem - the class is free to add whatever meaning it wants to.

I simply argue, it can well be a problem and potentially adding a confusion.

auto p = foo();

if(cond(p))
 p
.reset(hello.getBar());



All I am saying, this code above, for current smart pointers, meant - p now has the bar object.

With observer_ptr into the picture, it might also mean - p now points to a bar.

For current smart pointers in the standard, it is also true. The assumption is just further weaken (correctly).
 ...

Is it, really? For both (all?) smart pointers today, the meaning is the same - p is now master of bar. The only difference is, is this ownership shared with other p clones or not.
Where in the observer case, it is something quite different.

Again, it might well not be a problem, but I don't believe it is a difference to be taken lightly.
No. A smart pointer is not necessarily owning, even if all instances in the current standard are owning. See also https://en.wikipedia.org/wiki/Smart_pointer, esp. external links.
 
Of course, but the case in point is all the code written against the current notions, from both std and other implementations like Qt, and the effect observer will have.
I don't see they have defined or suggested such notions of smart pointers.

FrankHB1989

unread,
Oct 20, 2016, 6:11:20 AM10/20/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, joseph....@gmail.com


在 2016年10月20日星期四 UTC+8上午1:05:07,joseph....@gmail.com写道:
Something like observer_ptr should exist. Use of raw pointers is simply too overloaded. Even if you buy the argument that, in modern C++, raw pointers have a single accepted use as non-owning references (and I don't**), something like observer_ptr narrows the range of operations to what is sensible for such a reference type (e.g. no pointer arithmetic). However, I believe the design of observer_ptr is not well tailored to its purported purpose:
  • It is cumbersome to use: any pointer can be "observed" safely, so why do I have to use make_observer or reset to change what I'm observing? This seems to be due to observer_ptr being based on unique_ptr (hence the inappropriate release function) which understandably wants to prevent accidental assignment of arbitrary raw pointers, since it takes ownership of whatever it holds. The API is appropriate for an owning pointer, not an observing pointer (p.s. the presence of a reset function would be a bike shed issue if direct assignment were possible).
This is probably because of code migration.

I guess mixing of observer_ptrs and raw pointers is not intended by the committee.
 
  • It has a null state: this is sometimes desirable, but references are frequently used instead of pointers to "observe" something while precluding the possibility of not observing anything. Of course, pointers can be used, but care must be taken not to dereference a null pointer; it is better (IMO) to have compile-time assurance that your reference type cannot be null. Where the the null-less counterpart to observer_ptr?
 It is a pointer, so it is nullable.

Anyway, it may be better to have a nonnullable counterpart, but not just for observer_ptr.
  • It is not const-correct: pointers circumvent the "const-correct" nature of the C++ type system; a const pointer is not implicitly a pointer to const. This should be the default behaviour of any "observer" type. In fact, this should be the behaviour of any "owner" type as well; I feel as though unique_ptr and shared_ptr should have really been called unique_owner and shared_owner, and should have exhibited const-correct behaviour. That said, I can see value in the flexibility of pointer-like owning types, so perhaps we are in need of both sets of types (that is, if propagate_const cannot solve the issue satisfactorily; I'm not yet sure it can).
Const-correctness here is not the problem of any particular pointers.

And there are more serious ones. (So I am reluctant to introduce wrappers to solve these problems.)

First of all, the correctness should not only work for particular `const`. "Const-correctness", or more "correctly", immutable-correctness, should be based on any equivalent relationship in general. Requiring "const" here is only due to the limitation of the current type system in C++.

Hard-coded "const" leads to bad problems. For instance, enforcing `const` on the key type of associative containers (since C++11) is essentially wrong because the equivalence implied by comparator has nothing to do with "const". Even if the users can guarantee the equivalence by themselves, they can only override the `const` by `mutable` within the key type (as a class) definition which kills all benefits of the typecheck; otherwise the associative containers are almost not usable. For this particular problems, UB in C++03 is actually fair enough to ensure the zero cost abstraction (as well as to prevent reinventing the poor wheels by yourself).

To overcome this, perhaps some forms of parameterized `const` (e.g. `const<xxx::operator==>`) are needed. Then plain old `const` can be considered (relatively) harmful because it enforces less type-correctness, as non-`const` APIs are criticized today.

(Also note that the "as-if" based transformation implementations like constant folding can actually work more broadly.)

So always "default to be const" is wrong or at least suspicious, especially for a language which has even no sane way to syntheses the trivial equivelence (equality operators which behave similar enough to built-in constant folding conditions checked by a typical optimizing implementation) automatically by default.

There is no centralized authority to specify the semantics requirements of immutable-correctness (it's all up to the users' need -- whether they have to make multiple different equivalent relationships coexisted on the same type), so it is risky to assume `const` guarantee the real "correctness" needed by users of the API, merely via a single hard-coded core language feature (which is even not predictable in aspect of binary representations). But there is only one "non-const". It's the compromise when you can't guarantee the propagation of `const` is safely away from those problems in long term.

 

mihailn...@gmail.com

unread,
Oct 20, 2016, 7:06:38 AM10/20/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, joseph....@gmail.com


On Thursday, October 20, 2016 at 11:45:10 AM UTC+3, joseph....@gmail.com wrote:
On Thursday, 20 October 2016 15:09:16 UTC+8, mihailn...@gmail.com wrote:
My intentions are not to change your mind - we, as well as everyone else, are perfectly aware of the pros and cons of both approaches - raw vs new ptr.
 
I think it helps to have these things written down. Even if everyone else knows everything already, it helps me get my thoughts in order and realize where my reasoning is flawed.

Agreed. These pros and cons lists are important, so one can see all sides. I for one see better now, why people want a view type, most importantly - what features they want from it!
 

We also agree, any "operator." (in quotes, not necessarily overloading it) will dramatically change any possible implementation of such a wrapper pointer type.

I don't agree with this. I think "operator dot overloading" will have limited to no application in this case. I believe it will have more of an application with types that act to modify select parts of an API while retaining the overall appearance of the underlying type like, for example, propagate_const.

Considering all proposals include an impl of a smart reference - it certainly will have significant impact. We probably don't want to end up with too much observing wrappers.


And lets be honest, references now are the non-null pointers we are trying to invent anew. They are invented for this same purpose ages ago.

IF the downsides of these are solved (a.k.a. smart references), maybe they are the right way to handle the observer case.

May be the future is not a pointer type, but a reference type.
After all, they were the first observers out there!
 
 
I also agree with you propagate_const should be integral part of such a wrapper pointer type, not a cumbersome opt-in. 

I'm in two minds about this. It may be better to keep const propagation separate.

But we more often need the pointed-to be const, not the pointer.

We write const auto* many times more often then auto*const.

Yes, propagate, will mean const auto*const, so there should be control.

In any case, propagation is not unreasonable default:

It will enable const auto obj = getObj(); to mean what the user probably intended to mean!
Vary, very reasonable default.

It is interesting to note, smart references will suffer from the same problem.

Nicol Bolas

unread,
Oct 20, 2016, 11:07:27 AM10/20/16
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com, joseph....@gmail.com
On Thursday, October 20, 2016 at 7:06:38 AM UTC-4, mihailn...@gmail.com wrote:
On Thursday, October 20, 2016 at 11:45:10 AM UTC+3, joseph....@gmail.com wrote:
On Thursday, 20 October 2016 15:09:16 UTC+8, mihailn...@gmail.com wrote:
We also agree, any "operator." (in quotes, not necessarily overloading it) will dramatically change any possible implementation of such a wrapper pointer type.

I don't agree with this. I think "operator dot overloading" will have limited to no application in this case. I believe it will have more of an application with types that act to modify select parts of an API while retaining the overall appearance of the underlying type like, for example, propagate_const.

Considering all proposals include an impl of a smart reference - it certainly will have significant impact.

Point of order: none of the operator-dot proposals come with smart references. They talk about them, but they do not include any actual standard library addendums to include smart reference types. It's important to recognize the distinction there.

Reply all
Reply to author
Forward
0 new messages