unique_ptr::pointer
requirements underspecifiedSection: 20.3.1.3 [unique.ptr.single] Status: Resolved Submitter: Daniel Krügler Opened: 2008-05-14 Last modified: 2016-01-28
Priority: Not Prioritized
View other active issues in [unique.ptr.single].
View all other issues in [unique.ptr.single].
View all issues with Resolved status.
Discussion:
Issue 673 (including recent updates by 821) proposes a useful
extension point for unique_ptr
by granting support for an optional
deleter_type::pointer
to act as pointer-like replacement for element_type*
(In the following: pointer
).
Unfortunately no requirements are specified for the type pointer
which has
impact on at least two key features of unique_ptr
:
The unique_ptr
specification makes great efforts to require that essentially all
operations cannot throw and therefore adds proper wording to the affected
operations of the deleter as well. If user-provided pointer
-emulating types
("smart pointers") will be allowed, either all throw-nothing clauses have to
be replaced by weaker "An exception is thrown only if pointer
's {op} throws
an exception"-clauses or it has to be said explicitly that all used
operations of pointer
are required not to throw. I understand the main
focus of unique_ptr
to be as near as possible to the advantages of native pointers which cannot
fail and thus strongly favor the second choice. Also, the alternative position
would make it much harder to write safe and simple template code for
unique_ptr
. Additionally, I assume that a general statement need to be given
that all of the expressions of pointer
used to define semantics are required to
be well-formed and well-defined (also as back-end for 762).
[ Sophia Antipolis: ]
Howard: We maybe need a core concept
PointerLike
, but we don't need the arithmetic (seeshared_ptr
vs.vector<T>::iterator
.Howard will go through and enumerate the individual requirements wrt.
pointer
for each member function.
[ 2009-07 Frankfurt: ]
Move to Ready.
[ 2009-10-15 Alisdair pulls from Ready: ]
I hate to pull an issue out of Ready status, but I don't think 834 is fully baked yet.
For reference the proposed resolution is to add the following words:
unique_ptr<T, D>::pointer
's operations shall be well-formed, shall have well defined behavior, and shall not throw exceptions.This leaves me with a big question : which operations?
Are all pointer operations required to be nothrow, including operations that have nothing to do with interactions with
unique_ptr
? This was much simpler with concepts where we could point to operations within a certain concept, and so nail down the interactions.
[ 2009-10-15 Daniel adds: ]
I volunteer to prepare a more fine-grained solution, but I would like to ask for feedback that helps me doing so. If this question is asked early in the meeting I might be able to fix it within the week, but I cannot promise that now.
[ 2009-10 Santa Cruz: ]
Leave in open. Daniel to provide wording as already suggested.
[ 2009-12-22 Daniel provided wording and rationale. ]
[ 2010 Pittsburgh: Moved to NAD Editorial. Rationale added below. ]
Rationale:
The here proposed resolution has considerable overlap with the requirements that are used in the allocator requirements.
This might be a convincing argument to isolate the common subset into one requirement. The reason I did not do that is basically because we might find out that they are either over-constraining or under-constraining at this late point of specification. Note also that as a result of the idea of a general requirement set I added the requirement:
A default-initialized object may have a singular value
even though this does not play a relevant role for unique_ptr
.
One further characteristics of the resolution is that availability of relational
operators of unique_ptr<T, D>::pointer
is not part of the basic
requirements, which is in sync with the allocator requirements on pointer-like
(this means that unique_ptr
can hold a void_pointer
or
const_void_pointer
).
Solved by N3073.
Proposed resolution:
Change 20.3.1.3 [unique.ptr.single] p. 1 as indicated: [The intent is to
replace the coupling between T*
and the deleter's operator()
by a coupling between unique_ptr<T, D>::pointer
and this
operator()
]
1 - The default type for the template parameter
D
isdefault_delete
. A client-supplied template argumentD
shall be a function pointer or functor for which, given a valued
of typeD
and apointervalueptr
of type, the expression
T*unique_ptr<T, D>::pointerd(ptr)
is valid and has the effect of deallocating the pointer as appropriate for that deleter.D
may also be an lvalue-reference to a deleter.
Change 20.3.1.3 [unique.ptr.single] p. 3 as indicated:
3 - If the type
remove_reference<D>::type::pointer
exists, thenunique_ptr<T, D>::pointer
shall be a synonym forremove_reference<D>::type::pointer
. Otherwiseunique_ptr<T, D>::pointer
shall be a synonym forT*
. The typeunique_ptr<T, D>::pointer
shallbesatisfy the requirements ofEqualityComparable
,DefaultConstructible
,CopyConstructible
(Table 34) and,CopyAssignable
(Table 36),Swappable
, andDestructible
(16.4.4.2 [utility.arg.requirements]). A default-initialized object may have a singular value. A value-initialized object produces the null value of the type. The null value shall be equivalent only to itself. An object of this type can be copy-initialized with a value of typenullptr_t
, compared for equality with a value of typenullptr_t
, and assigned a value of typenullptr_t
. The effect shall be as if a value-initialized object had been used in place of the null pointer constant. An objectp
of this type can be contextually converted tobool
. The effect shall be as ifp != nullptr
had been evaluated in place ofp
. No operation on this type which is part of the above mentioned requirements shall exit via an exception.[Note: Given an allocator type
X
(16.4.4.6 [allocator.requirements]), the typesX::pointer
,X::const_pointer
,X::void_pointer
, andX::const_void_pointer
may be used asunique_ptr<T, D>::pointer
— end note]In addition to being available via inclusion of the
<utility>
header, theswap
function template in 22.2.2 [utility.swap] is also available within the definition ofunique_ptr
'sswap
function.
Change 20.3.1.3.2 [unique.ptr.single.ctor] p. 2+3 as indicated: [The first
change ensures that we explicitly say, how the stored pointer is initialized.
This is important for a constexpr
function, because this may make a
difference for user-defined pointer-like types]
constexpr unique_ptr();...
2 - Effects: Constructs a
unique_ptr
which owns nothing, value-initializing the stored pointer.3 - Postconditions:
get() ==
.0nullptr
Change 20.3.1.3.2 [unique.ptr.single.ctor] p. 6+7 as indicated: [This is a step-by-fix to ensure consistency to the changes of N2976]
unique_ptr(pointer p);...
6 - Effects: Constructs a
unique_ptr
which ownsp
, initializing the stored pointer withp
.7 - Postconditions:
get() == p
.get_deleter()
returns a reference to adefault constructedvalue-initialized deleterD
.
Insert a new effects clause in 20.3.1.3.2 [unique.ptr.single.ctor] just before p. 14: [The intent is to fix the current lack of specification in which way the stored pointer is initialized]
unique_ptr(pointer p,implementation-definedsee below d1); unique_ptr(pointer p,implementation-definedsee below d2);...
Effects: Constructs a
unique_ptr
which ownsp
, initializing the stored pointer withp
and the initializing the deleter as described above.14 - Postconditions:
get() == p
.get_deleter()
returns a reference to the internally stored deleter. IfD
is a reference type thenget_deleter()
returns a reference to the lvalued
.
Change 20.3.1.3.2 [unique.ptr.single.ctor] p. 18+22 as indicated: [The intent is to clarify that the moved-from source must contain a null pointer, there is no other choice left]
unique_ptr(unique_ptr&& u);[..]
18 - Postconditions:
get() == value u.get()
had before the construction andu.get() == nullptr
.get_deleter()
returns a reference to the internally stored deleter which was constructed fromu.get_deleter()
. IfD
is a reference type thenget_deleter()
andu.get_deleter()
both reference the same lvalue deleter.template <class U, class E> unique_ptr(unique_ptr<U, E>&& u);[..]
22 - Postconditions:
get() == value u.get()
had before the construction, modulo any required offset adjustments resulting from the cast fromunique_ptr<U, E>::pointer
topointer
andu.get() == nullptr
.get_deleter()
returns a reference to the internally stored deleter which was constructed fromu.get_deleter()
.
Change 20.3.1.3.2 [unique.ptr.single.ctor] p. 20 as indicated: [With the possibility of user-defined pointer-like types the implication does only exist, if those are built-in pointers. Note that this change should also be applied with the acceptance of 950]
template <class U, class E> unique_ptr(unique_ptr<U, E>&& u);20 - Requires: If
D
is not a reference type, construction of the deleterD
from an rvalue of typeE
shall be well formed and shall not throw an exception. IfD
is a reference type, thenE
shall be the same type asD
(diagnostic required).unique_ptr<U, E>::pointer
shall be implicitly convertible topointer
.[Note: These requirements imply thatT
andU
are complete types. — end note]
Change 20.3.1.3.3 [unique.ptr.single.dtor] p. 2 as indicated:
~unique_ptr();...
2 - Effects: If
get() ==
there are no effects. Otherwise0nullptrget_deleter()(get())
.
Change 20.3.1.3.4 [unique.ptr.single.asgn] p. 3+8 as indicated: [The intent is to clarify that the moved-from source must contain a null pointer, there is no other choice left]
unique_ptr& operator=(unique_ptr&& u);[..]
3 - Postconditions: This
unique_ptr
now owns the pointer whichu
owned, andu
no longer owns it,u.get() == nullptr
. [Note: IfD
is a reference type, then the referenced lvalue deleters are move assigned. — end note]template <class U, class E> unique_ptr& operator=(unique_ptr<U, E>&& u);[..]
8 - Postconditions: This
unique_ptr
now owns the pointer whichu
owned, andu
no longer owns it,u.get() == nullptr
.
Change 20.3.1.3.4 [unique.ptr.single.asgn] p. 6 as indicated: [With the possibility of user-defined pointer-like types the implication does only exist, if those are built-in pointers. Note that this change should also be applied with the acceptance of 950]
template <class U, class E> unique_ptr& operator=(unique_ptr<U, E>&& u);[..]
6 - Requires: Assignment of the deleter
D
from an rvalueD
shall not throw an exception.unique_ptr<U, E>::pointer
shall be implicitly convertible topointer
.[Note: These requirements imply thatT
andU
are complete types. — end note]
Change 20.3.1.3.4 [unique.ptr.single.asgn] before p. 11 and p. 12 as indicated: [The first change is a simple typo fix]
unique_ptr& operator=(nullptr_t});11 - Effects:
reset()
.12 - Postcondition:
get() ==
0nullptr
Change 20.3.1.3.5 [unique.ptr.single.observers] p. 1+4+12 as indicated:
typename add_lvalue_reference<T>::type operator*() const;1 - Requires:
get() !=
. The variable definition0nullptradd_lvalue_reference<T>::type t = *get()
shall be well formed, shall have well-defined behavior, and shall not exit via an exception.[..]
pointer operator->() const;4 - Requires:
get() !=
.0nullptr[..]
explicit operator bool() const;12 - Returns:
get() !=
.0nullptr
Change 20.3.1.3.6 [unique.ptr.single.modifiers] p. 1 as indicated:
pointer release();1 - Postcondition:
get() ==
.0nullptr
Change 20.3.1.3.6 [unique.ptr.single.modifiers] p. 9 as indicated: [The
intent is to ensure that potentially user-defined swaps are used. A side-step
fix and harmonization with the specification of the the deleter is realized.
Please note the additional requirement in bullet 2 of this proposed resolution
regarding the availability of the generic swap
templates within the
member swap
function.]
void swap(unique_ptr& u);8 - Requires: The deleter
D
shall beSwappable
and shall not throw an exception underswap
.9 - Effects: The stored pointers of
*this
andu
are exchanged by an unqualified call to non-memberswap
. The stored deleters areexchanged by an unqualified call to non-memberswap
'd (unqualified)swap
.
Change 20.3.1.4.4 [unique.ptr.runtime.observers] p. 1 as indicated:
T& operator[](size_t i) const;Requires:
i <
the size of the array to which the stored pointer points. The variable definitionT& t = get()[i]
shall be well formed, shall have well-defined behavior, and shall not exit via an exception.
Change 20.3.1.4.5 [unique.ptr.runtime.modifiers] p. 1 as indicated:
void reset(pointer p = pointer()); void reset(nullptr_t p);1 - Effects: If
get() ==
there are no effects. Otherwise0nullptrget_deleter()(get())
.
Change 20.3.1.6 [unique.ptr.special] as indicated: [We don't add the relational operators to the basic requirement set, therefore we need special handling here]
template <class T1, class D1, class T2, class D2> bool operator==(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition
bool b = x.get() == y.get();
shall be well formed, shall have well-defined behavior, and shall not exit via an exception.2 - Returns:
x.get() == y.get()
.Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition
bool b = x.get() != y.get();
shall be well formed, shall have well-defined behavior, and shall not exit via an exception.3 - Returns:
x.get() != y.get()
.Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator<(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition
bool b = x.get() < y.get()
; shall be well formed, shall have well-defined behavior, and shall not exit via an exception.4 - Returns:
x.get() < y.get()
.Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator<=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition
bool b = x.get() <= y.get();
shall be well formed, shall have well-defined behavior, and shall not exit via an exception.5 - Returns:
x.get() <= y.get()
.Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator>(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition
bool b = x.get() > y.get();
shall be well formed, shall have well-defined behavior, and shall not exit via an exception.6 - Returns:
x.get() > y.get()
.Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator>=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition
bool b = x.get() >= y.get();
shall be well formed, shall have well-defined behavior, and shall not exit via an exception.7 - Returns:
x.get() >= y.get()
.Throws: nothing.