is_(nothrow_)move_constructible
and tuple
, optional
and unique_ptr
Section: 22.4 [tuple], 22.5 [optional], 20.3.1.3.2 [unique.ptr.single.ctor] Status: C++20 Submitter: United States Opened: 2017-02-03 Last modified: 2021-02-25
Priority: 2
View all other issues in [tuple].
View all issues with C++20 status.
Discussion:
Addresses US 110The move constructors for tuple
, optional
, and unique_ptr
should return false for
is_(nothrow_)move_constructible_v<TYPE>
when their corresponding Requires clauses are not
satisfied, as there are now several library clauses that are defined in terms of these traits. The same concern
applies to the move-assignment operator. Note that pair
and variant
already satisfy this constraint.
[2017-02-26, Scott Schurr provides wording]
[ 2017-06-27 P2 after 5 positive votes on c++std-lib. ]
[2016-07, Toronto Thursday night issues processing]
The description doesn't match the resolution; Alisdair to investigate. Status to Open
Previous resolution [SUPERSEDED]:
This wording is relative to N4640.
Modify 22.4.4 [tuple.tuple] as indicated:
// 20.5.3.1, tuple construction EXPLICIT constexpr tuple(); EXPLICIT constexpr tuple(const Types&...); // only if sizeof...(Types) >= 1 template <class... UTypes> EXPLICIT constexpr tuple(UTypes&&...) noexcept(see below); // only if sizeof...(Types) >= 1 tuple(const tuple&) = default; tuple(tuple&&) = default; template <class... UTypes> EXPLICIT constexpr tuple(const tuple<UTypes...>&); template <class... UTypes> EXPLICIT constexpr tuple(tuple<UTypes...>&&) noexcept(see below); template <class U1, class U2> EXPLICIT constexpr tuple(const pair<U1, U2>&); // only if sizeof...(Types) == 2 template <class U1, class U2> EXPLICIT constexpr tuple(pair<U1, U2>&&) noexcept(see below); // only if sizeof...(Types) == 2 […] // 20.5.3.2, tuple assignment tuple& operator=(const tuple&); tuple& operator=(tuple&&) noexcept(see below); template <class... UTypes> tuple& operator=(const tuple<UTypes...>&); template <class... UTypes> tuple& operator=(tuple<UTypes...>&&) noexcept(see below); template <class U1, class U2> tuple& operator=(const pair<U1, U2>&); // only if sizeof...(Types) == 2 template <class U1, class U2> tuple& operator=(pair<U1, U2>&&) noexcept(see below); // only if sizeof...(Types) == 2Modify 22.4.4.2 [tuple.cnstr] as indicated:
template <class... UTypes> EXPLICIT constexpr tuple(UTypes&&... u) noexcept(see below);-8- Effects: Initializes the elements in the tuple with the corresponding value in
-9- Remarks: This constructor shall not participate in overload resolution unlessstd::forward<UTypes>(u)
.sizeof...(Types) == sizeof...(UTypes)
andsizeof...(Types) >= 1
andis_constructible_v<Ti, Ui&&>
istrue
for alli
. The constructor is explicit if and only ifis_convertible_v<Ui&&, Ti>
isfalse
for at least onei
. The expression insidenoexcept
is equivalent to the logical AND of the following expressions:is_nothrow_constructible_v<Ti, Ui&&>where
Ti
is thei
th type inTypes
, andUi
is thei
th type inUTypes
.[…]
template <class... UTypes> EXPLICIT constexpr tuple(tuple<UTypes...>&& u) noexcept(see below);-16- Effects: For all
-17- Remarks: This constructor shall not participate in overload resolution unless […] The constructor is explicit if and only ifi
, initializes thei
th element of*this
withstd::forward<Ui>(get<i>(u))
.is_convertible_v<Ui&&, Ti>
isfalse
for at least onei
. The expression insidenoexcept
is equivalent to the logical AND of the following expressions:is_nothrow_constructible_v<Ti, Ui&&>where
Ti
is thei
th type inTypes
, andUi
is thei
th type inUTypes
.[…]
template <class U1, class U2> EXPLICIT constexpr tuple(pair<U1, U2>&& u) noexcept(see below);-21- Effects: Initializes the first element with
-22- Remarks: This constructor shall not participate in overload resolution unlessstd::forward<U1>(u.first)
and the second element withstd::forward<U2>(u.second)
.sizeof...(Types) == 2
,is_constructible_v<T0, U1&&>
istrue
andis_constructible_v<T1, U2&&>
istrue
. -23- The constructor is explicit if and only ifis_convertible_v<U1&&, T0>
isfalse
oris_convertible_v<U2&&, T1>
isfalse
. The expression insidenoexcept
is equivalent to:is_nothrow_constructible_v<T0, U1&&> && is_nothrow_constructible_v<T1, U2&&>Modify 22.4.4.3 [tuple.assign] as indicated:
template <class... UTypes> tuple& operator=(tuple<UTypes...>&& u) noexcept(see below);-12- Effects: For all
-13- Remarks: This operator shall not participate in overload resolution unlessi
, assignsstd::forward<Ui>(get<i>(u))
toget<i>(*this)
.is_assignable_v<Ti&, Ui&&> == true
for alli
andsizeof...(Types) == sizeof...(UTypes)
. The expression insidenoexcept
is equivalent to the logical AND of the following expressions:is_nothrow_assignable_v<Ti&, Ui&&>where
Ti
is thei
th type inTypes
, andUi
is thei
th type inUTypes
.-14- Returns:
*this
.[…]
template <class U1, class U2> tuple& operator=(pair<U1, U2>&& u) noexcept(see below);-18- Effects: Assigns
-19- Remarks: This operator shall not participate in overload resolution unlessstd::forward<U1>(u.first)
to the first element of*this
andstd::forward<U2>(u.second)
to the second element of*this
.sizeof...(Types) == 2
andis_assignable_v<T0&, U1&&>
istrue
for the first typeT0
inTypes
andis_assignable_v<T1&, U2&&>
istrue
for the second typeT1
inTypes
. The expression insidenoexcept
is equivalent to:is_nothrow_assignable_v<T0&, U1&&> && is_nothrow_assignable_v<T1&, U2&&>-20- Returns:
*this
.
[2019-02-14; Jonathan comments and provides revised wording]
The suggested change was already made to std::optional
by LWG 2756. The current P/R for 2899
doesn't resolve the issue for std::tuple
or std::unique_ptr
. I hope the following alternative does.
[2019-02; Kona Wednesday night issue processing]
Status to Ready
Proposed resolution:
This wording is relative to N4800.
Modify 22.4.4.2 [tuple.cnstr] as indicated:
tuple(tuple&& u) = default;-13-
-14- Effects: For allRequiresConstraints:is_move_constructible_v<Ti>
istrue
for alli
.i
, initializes thei
th element of*this
withstd::forward<Ti>(get<i>(u))
.
Modify 20.3.1.3.2 [unique.ptr.single.ctor] as indicated:
unique_ptr(unique_ptr&& u) noexcept;-?- Constraints:
-15- Requires: Ifis_move_constructible_v<D>
istrue
.D
is not a reference type,D
shall satisfy the Cpp17MoveConstructible requirements (Table 26). Construction of the deleter from an rvalue of typeD
shall not throw an exception. […]
Modify 20.3.1.3.4 [unique.ptr.single.asgn] as indicated:
unique_ptr& operator=(unique_ptr&& u) noexcept;-?- Constraints:
-1- Requires: Ifis_move_assignable_v<D>
istrue
.D
is not a reference type,D
shall satisfy the Cpp17MoveAssignable requirements (Table 28) and assignment of the deleter from an rvalue of typeD
shall not throw an exception. Otherwise,D
is a reference type;remove_reference_t<D>
shall satisfy the Cpp17CopyAssignable requirements and assignment of the deleter from an lvalue of typeD
shall not throw an exception. -2- Effects: Callsreset(u.release())
followed byget_deleter() = std::forward<D>(u.get_deleter())
. […]