Issue 2899: is_(nothrow_)move_constructible and tuple, optional and unique_ptr (original) (raw)
This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++20 status.
2899. 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 110
The 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) >= 1tuple(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) == 2- Modify 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
std::forward<UTypes>(u).-9- Remarks: This constructor shall not participate in overload resolution unless
sizeof...(Types) == sizeof...(UTypes)andsizeof...(Types) >= 1andis_constructible_v<T_i_, U_i_&&>istruefor all_i_. The constructor is explicit if and only ifis_convertible_v<U_i_&&, T_i_>isfalsefor at least one_i_. The expression insidenoexceptis equivalent to the logical AND of the following expressions:is_nothrow_constructible_v<T_i_, U_i_&&>
where
T_i_is the_i_th type inTypes, andU_i_is the_i_th type inUTypes.[…]
template <class... UTypes> EXPLICIT constexpr tuple(tuple<UTypes...>&& u) noexcept(see below);
-16- Effects: For all
_i_, initializes the_i_th element of*thiswithstd::forward<U_i_>(get<_i_>(u)).-17- Remarks: This constructor shall not participate in overload resolution unless
[…]
The constructor is explicit if and only if
is_convertible_v<U_i_&&, T_i_>isfalsefor at least one_i_. The expression insidenoexceptis equivalent to the logical AND of the following expressions:is_nothrow_constructible_v<T_i_, U_i_&&>
where
T_i_is the_i_th type inTypes, andU_i_is the_i_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
std::forward<U1>(u.first)and the second element withstd::forward<U2>(u.second).-22- Remarks: This constructor shall not participate in overload resolution unless
sizeof...(Types) == 2,is_constructible_v<T0, U1&&>istrueandis_constructible_v<T1, U2&&>istrue.-23- The constructor is explicit if and only if
is_convertible_v<U1&&, T0>isfalseoris_convertible_v<U2&&, T1>isfalse. The expression insidenoexceptis 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
_i_, assignsstd::forward<U_i_>(get<_i_>(u))toget<_i_>(*this).-13- Remarks: This operator shall not participate in overload resolution unless
is_assignable_v<T_i_&, U_i_&&> == truefor all_i_andsizeof...(Types) == sizeof...(UTypes).The expression insidenoexceptis equivalent to the logical AND of the following expressions:is_nothrow_assignable_v<T_i_&, U_i_&&>
where
T_i_is the_i_th type inTypes, andU_i_is the_i_th type inUTypes.-14- Returns:
*this.[…]
template <class U1, class U2> tuple& operator=(pair<U1, U2>&& u) noexcept(see below);
-18- Effects: Assigns
std::forward<U1>(u.first)to the first element of*thisandstd::forward<U2>(u.second)to the second element of*this.-19- Remarks: This operator shall not participate in overload resolution unless
sizeof...(Types) == 2andis_assignable_v<T0&, U1&&>istruefor the first typeT0inTypesandis_assignable_v<T1&, U2&&>istruefor the second typeT1inTypes.The expression insidenoexceptis 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(i). 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-
RequiresConstraints:is_move_constructible_v<T_i_>istruefor all_i_.-14- Effects: For all
_i_, initializes the_i_th element of*thiswithstd::forward<T_i_>(get<_i_>(u)). - Modify 20.3.1.3.2 [unique.ptr.single.ctor] as indicated:
unique_ptr(unique_ptr&& u) noexcept;
-?- Constraints:
is_move_constructible_v<D>istrue.-15- Requires: If
Dis not a reference type,Dshall satisfy the Cpp17MoveConstructible requirements (Table 26). Construction of the deleter from an rvalue of typeDshall not throw an exception.[…]
- Modify 20.3.1.3.4 [unique.ptr.single.asgn] as indicated:
unique_ptr& operator=(unique_ptr&& u) noexcept;
-?- Constraints:
is_move_assignable_v<D>istrue.-1- Requires: If
Dis not a reference type,Dshall satisfy the Cpp17MoveAssignable requirements (Table 28) and assignment of the deleter from an rvalue of typeDshall not throw an exception. Otherwise,Dis a reference type;remove_reference_t<D>shall satisfy the _Cpp17CopyAssignable_requirements and assignment of the deleter from an lvalue of typeDshall not throw an exception.-2- Effects: Calls
reset(u.release())followed byget_deleter() = std::forward<D>(u.get_deleter()).[…]