This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.
Section: 32.6.5.4.2 [thread.lock.unique.cons], 32.6.5.5.2 [thread.lock.shared.cons] Status: WP Submitter: Casey Carter Opened: 2024-11-13 Last modified: 2025-02-16
Priority: Not Prioritized
View all other issues in [thread.lock.unique.cons].
View all issues with WP status.
Discussion:
The postconditions in 32.6.5.4.2 [thread.lock.unique.cons] paragraph 19:
Postconditions:contradict themselves ifpm == u_p.pm
andowns == u_p.owns
(whereu_p
is the state ofu
just prior to this construction),u.pm == 0
andu.owns == false
.
*this
and the parameter u
refer to the same object.
(Presumably "this construction" means the assignment, and it is copy-pasta from
the move constructor postconditions.) Apparently
unique_lock
didn't get the memo that we require well-defined behavior
from self-move-assignment as of LWG 2839(i).
Also, the move assignment operator doesn't specify what it returns.
[2024-11-18; Casey expands the PR to cover shared_lock
]
shared_lock
has the same problems, and can be fixed in the same way.
[2025-02-07; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
"Should use parentheses not braces for the initializations." Jonathan volunteers to do that editorially after this gets approved.
[Hagenberg 2025-02-16; Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4993.
Drafting Note: I've chosen to use the move-into-temporary-and-swap idiom here to keep things short and sweet. Since move construction, swap, and destruction are allnoexcept
, I've promoted move assignment from "Throws: Nothing" tonoexcept
as well. This is consistent with eliminating the implicit narrow contract condition that*this
andu
refer to distinct objects.
In the class synopsis in 32.6.5.4.1 [thread.lock.unique.general],
annotate the move assignment operator as noexcept
:
namespace std { template<class Mutex> class unique_lock { [...] unique_lock& operator=(unique_lock&& u) noexcept; [...] }; }
Modify 32.6.5.4.2 [thread.lock.unique.cons] as follows:
unique_lock& operator=(unique_lock&& u) noexcept;
-18- Effects:
IfEquivalent to:owns
callspm->unlock()
.unique_lock{std::move(u)}.swap(*this)
.-?- Returns:
*this
.
-19- Postconditions:pm == u_p.pm
andowns == u_p.owns
(whereu_p
is the state ofu
just prior to this construction),u.pm == 0
andu.owns == false
.
-20- [Note 1: With a recursive mutex it is possible for both*this
and u to own the same mutex before the assignment. In this case, *this will own the mutex after the assignment and u will not. — end note]
-21- Throws: Nothing.
Modify 32.6.5.5.2 [thread.lock.shared.cons] as follows:
shared_lock& operator=(shared_lock&& sl) noexcept;
-17- Effects:
IfEquivalent to:owns
callspm->unlock_shared()
.shared_lock{std::move(sl)}.swap(*this)
.-?- Returns:
*this
.
-18- Postconditions:pm == sl_p.pm
andowns == sl_p.owns
(wheresl_p
is the state ofsl
just prior to this assignment),sl.pm == nullptr
andsl.owns == false
.