Section: 20.3.1.3.6 [unique.ptr.single.modifiers] Status: C++11 Submitter: Pavel Minaev Opened: 2009-02-26 Last modified: 2016-01-28
Priority: Not Prioritized
View all other issues in [unique.ptr.single.modifiers].
View all issues with C++11 status.
Discussion:
Consider the following (simplified) implementation of
std::auto_ptr<T>::reset()
:
void reset(T* newptr = 0) { if (this->ptr && this->ptr != newptr) { delete this->ptr; } this->ptr = newptr; }
Now consider the following code which uses the above implementation:
struct foo { std::auto_ptr<foo> ap; foo() : ap(this) {} void reset() { ap.reset(); } }; int main() { (new foo)->reset(); }
With the above implementation of auto_ptr, this results in U.B. at the point of auto_ptr::reset(). If this isn't obvious yet, let me explain how this goes step by step:
foo::reset()
entered
auto_ptr::reset()
entered
auto_ptr::reset()
tries to delete foo
foo::~foo()
entered, tries to destruct its members
auto_ptr::~auto_ptr()
executed - auto_ptr
is no longer a valid object!
foo::~foo()
left
auto_ptr::reset()
sets its "ptr" field to 0 <- U.B.! auto_ptr
is not a valid object here already!
[
Thanks to Peter Dimov who recognized the connection to unique_ptr
and
brought this to the attention of the LWG, and helped with the solution.
]
[ Howard adds: ]
To fix this behavior
reset
must be specified such that deleting the pointer is the last action to be taken withinreset
.
[ Alisdair adds: ]
The example providing the rationale for LWG 998 is poor, as it relies on broken semantics of having two object believing they are unique owners of a single resource. It should not be surprising that UB results from such code, and I feel no need to go out of our way to support such behaviour.
If an example is presented that does not imply multiple ownership of a unique resource, I would be much more ready to accept the proposed resolution.
[ Batavia (2009-05): ]
Howard summarizes:
This issue has to do with circular ownership, and affects
auto_ptr
, too (but we don't really care about that). It is intended to spell out the order in which operations must be performed so as to avoid the possibility of undefined behavior in the self-referential case.Howard points to message c++std-lib-23175 for another example, requested by Alisdair.
We agree with the issue and with the proposed resolution. Move to Tentatively Ready.
Proposed resolution:
Change 20.3.1.3.6 [unique.ptr.single.modifiers], p5 (Effects clause for reset
), and p6:
-5- Effects:
IfAssignsget() == nullptr
there are no effects. Otherwiseget_deleter()(get())
.p
to the storedpointer
, and then if the old value of thepointer
is not equal tonullptr
, callsget_deleter()(
the old value of thepointer)
. [Note: The order of these operations is significant because the call toget_deleter()
may destroy*this
. -- end note]-6- Postconditions:
get() == p
. [Note: The postcondition does not hold if the call toget_deleter()
destroys*this
sincethis->get()
is no longer a valid expression. -- end note]