tuple
constructor constraints for UTypes&&...
overloadsSection: 22.4.4.2 [tuple.cnstr] Status: C++23 Submitter: Matt Calabrese Opened: 2018-06-12 Last modified: 2023-11-22
Priority: 2
View other active issues in [tuple.cnstr].
View all other issues in [tuple.cnstr].
View all issues with C++23 status.
Discussion:
Currently the tuple
constructors of the form:
template<class... UTypes> EXPLICIT constexpr tuple(UTypes&&...);
are not properly constrained in that in the 1-element tuple
case, the constraints do no short-circuit when
the constructor would be (incorrectly) considered as a possible copy/move constructor candidate. libc++ has a
workaround for this, but the additional short-circuiting does not actually appear in the working draft.
bool a = std::is_copy_constructible_v<std::tuple<any>>;
The above code will cause a compile error because of a recursive trait definition. The copy constructibility
check implies doing substitution into the UTypes&&...
constructor overloads, which in turn
will check if tuple<any>
is convertible to any, which in turn will check if tuple<any>
is copy constructible (and so the trait is dependent on itself).
sizeof...(UTypes) == 1
and the type, after applying remove_cvref_t
, is the tuple
type itself, then we should force
substitution failure rather than checking any further constraints.
[2018-06-23 after reflector discussion]
Priority set to 3
[2018-08-20, Jonathan provides wording]
[2018-08-20, Daniel comments]
The wording changes by this issue are very near to those suggested for LWG 3155.
[2018-11 San Diego Thursday night issue processing]
Jonathan to update wording - using conjunction. Priority set to 2
Previous resolution [SUPERSEDED]:
This wording is relative to N4762.
Modify 22.4.4.2 [tuple.cnstr] as indicated:
template<class... UTypes> explicit(see below) constexpr tuple(UTypes&&... u);-9- Effects: Initializes the elements in the tuple with the corresponding value in
-10- Remarks: This constructor shall not participate in overload resolution unlessstd::forward<UTypes>(u)
.sizeof...(Types) == sizeof...(UTypes)
andsizeof...(Types) >= 1
and(sizeof...(Types) > 1 || !is_same_v<remove_cvref_t<U0>, tuple>)
andis_constructible_v<Ti, Ui&&>
istrue
for alli
. The expression insideexplicit
is equivalent to:!conjunction_v<is_convertible<UTypes, Types>...>
[2021-05-20 Tim updates wording]
The new wording below also resolves LWG 3155, relating to an
allocator_arg_t
tag argument being treated by this constructor template
as converting to the first tuple element instead of as a tag. To minimize
collateral damage, this wording takes this constructor out of overload resolution
only if the tuple is of size 2 or 3, the first argument is an allocator_arg_t
,
but the first tuple element isn't of type allocator_arg_t
(in both cases
after removing cv/ref qualifiers). This avoids damaging tuples that actually
contain an allocator_arg_t
as the first element (which can be formed
during uses-allocator construction, thanks to uses_allocator_construction_args
).
[2021-08-20; LWG telecon]
Set status to Tentatively Ready after telecon review.
[2021-10-14 Approved at October 2021 virtual plenary. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4885, and also resolves LWG 3155.
Modify 22.4.4.2 [tuple.cnstr] as indicated:
template<class... UTypes> explicit(see below) constexpr tuple(UTypes&&... u);-?- Let
disambiguating-constraint
be:
(?.1) —
negation<is_same<remove_cvref_t<U0>, tuple>>
ifsizeof...(Types)
is 1;(?.2) — otherwise,
bool_constant<!is_same_v<remove_cvref_t<U0>, allocator_arg_t> || is_same_v<remove_cvref_t<T0>, allocator_arg_t>>
ifsizeof...(Types)
is 2 or 3;(?.3) — otherwise,
true_type
.-12- Constraints:
(12.1) —
sizeof...(Types)
equalssizeof...(UTypes)
,and(12.2) —
sizeof...(Types)
≥ 1, and(12.3) —
conjunction_v<disambiguating-constraint, is_constructible<Types, UTypes>...>
istrue
.is_constructible_v<Ti, Ui>
istrue
for all i-13- Effects: Initializes the elements in the tuple with the corresponding value in
-14- Remarks: The expression insidestd::forward<UTypes>(u)
.explicit
is equivalent to:!conjunction_v<is_convertible<UTypes, Types>...>