shared_ptr operator*()
should not be noexcept
Section: 20.3.2.2.6 [util.smartptr.shared.obs] Status: NAD Submitter: Stephan T. Lavavej Opened: 2013-10-05 Last modified: 2017-07-17
Priority: 2
View all other issues in [util.smartptr.shared.obs].
View all issues with NAD status.
Discussion:
20.3.1.3.5 [unique.ptr.single.observers]/3: "pointer operator->() const noexcept;
Requires: get() != nullptr
."
T& operator*() const noexcept;
Requires: get() != 0
."
20.3.2.2.6 [util.smartptr.shared.obs]/5: "T* operator->() const noexcept;
Requires: get() != 0
."
Narrow-contract functions should not be noexcept
.
[2014-02-15 Issaquah]
Issue is contentious, raise to P2.
[2015-02 Cologne]
AM: This ship has sailed. JM: What's the issue? AM: operator->
has narrow contract and should never have had
noexcept
. DK: Not quite. We explicitly called out that for shared_ptr
this is fine. You said so in your
"narrow contract" paper. GR: This would be a fairly major regression in the design of {unique
,shared
}_ptr
over raw pointers; raw pointer dereferencing is noexcept
. It's not a performance regression but a usability regression.
AM: Do we expect users to query noexpect
on dereference expressions? Room: Yes. VV: We don't just expect it, we have
seen it. JM: Yes, users may be querying something like noexcept(x->y)
and expect to be checking y
, but
silently end up checking x->
.
This wording is relative to N3691.
In 20.3.1.3 [unique.ptr.single]/1, class template
unique_ptr
synopsis for single objects, change as indicated:pointer operator->() constnoexcept;In 20.3.1.3.5 [unique.ptr.single.observers] change as indicated:
pointer operator->() constnoexcept;-3- Requires:
-4- Returns:get() != nullptr
.get()
. -?- Throws: Nothing. -5- Note: use typically requires thatT
be a complete type.In 20.3.2.2 [util.smartptr.shared]/1, class template
shared_ptr
synopsis, change as indicated:T& operator*() constnoexcept; T* operator->() constnoexcept;In 20.3.2.2.6 [util.smartptr.shared.obs] change as indicated:
T& operator*() constnoexcept;-2- Requires:
-3- Returns:get() != 0
.*get()
. -?- Throws: Nothing. -4- Remarks: WhenT
isvoid
, it is unspecified whether this member function is declared. If it is declared, it is unspecified what its return type is, except that the declaration (although not necessarily the definition) of the function shall be well formed.T* operator->() constnoexcept;-5- Requires:
-6- Returns:get() != 0
.get()
. -?- Throws: Nothing.
[2015-03-03, Geoffrey provides rationale]
Rationale:
It is by design that these members are
It is notable that N3279, which proposed this policy, did not propose strikingnoexcept
, and changing that now would be a substantial regression in functionality. These classes were designed to substitute for plain pointers as transparently as possible, so since those operations are effectivelynoexcept
on plain pointers, they should benoexcept
onunique_ptr
andshared_ptr
as well. This matters in practice because we expect these members to be used fairly often inside thenoexcept
operator, and such code could be broken by this change. These design considerations override our general policy againstnoexcept
for narrow-contract functions.noexcept
from these operations. It's not clear if the omission ofoperator*
andoperator->
was an oversight, or an intentional reflection of the above considerations. N3279 was based on N3248 by the same authors, which states that:"Most applications of
noexcept
forunique_ptr
andshared_ptr
are on functions with wide contracts. However, there are preconditions on the atomic access functions, so these should lose the specification."
Proposed resolution: