Title
tuple constructor constraints for UTypes&&... overloads
Status
c++23
Section
[tuple.cnstr]
Submitter
Matt Calabrese

Created on 2018-06-12.00:00:00 last changed 13 months ago

Messages

Date: 2021-10-14.09:56:08

Proposed resolution:

This wording is relative to N4885, and also resolves LWG 3155.

  1. Modify [tuple.cnstr] as indicated:

    template<class... UTypes> explicit(see below) constexpr tuple(UTypes&&... u);
    

    -?- Let disambiguating-constraint be:

    1. (?.1) — negation<is_same<remove_cvref_t<U0>, tuple>> if sizeof...(Types) is 1;

    2. (?.2) — otherwise, bool_constant<!is_same_v<remove_cvref_t<U0>, allocator_arg_t> || is_same_v<remove_cvref_t<T0>, allocator_arg_t>> if sizeof...(Types) is 2 or 3;

    3. (?.3) — otherwise, true_type.

    -12- Constraints:

    1. (12.1) — sizeof...(Types) equals sizeof...(UTypes), and

    2. (12.2) — sizeof...(Types) ≥ 1, and

    3. (12.3) — conjunction_v<disambiguating-constraint, is_constructible<Types, UTypes>...> is true is_constructible_v<Ti, Ui> is true for all i.

    -13- Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u).

    -14- Remarks: The expression inside explicit is equivalent to:

    !conjunction_v<is_convertible<UTypes, Types>...>

Date: 2021-10-14.00:00:00

[ 2021-10-14 Approved at October 2021 virtual plenary. Status changed: Voting → WP. ]

Date: 2021-08-15.00:00:00

[ 2021-08-20; LWG telecon ]

Set status to Tentatively Ready after telecon review.

Date: 2021-05-20.00:00:00

[ 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).

The proposed wording has been implemented and tested on top of libstdc++.

Date: 2021-05-21.00:53:53

[ 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 [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 std::forward<UTypes>(u).

    -10- Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) == sizeof...(UTypes) and sizeof...(Types) >= 1 and (sizeof...(Types) > 1 || !is_same_v<remove_cvref_t<U0>, tuple>) and is_constructible_v<Ti, Ui&&> is true for all i. The expression inside explicit is equivalent to:

    !conjunction_v<is_convertible<UTypes, Types>...>

Date: 2018-08-15.00:00:00

[ 2018-08-20, Daniel comments ]

The wording changes by this issue are very near to those suggested for LWG 3155.

Date: 2018-08-15.00:00:00

[ 2018-08-20, Jonathan provides wording ]

Date: 2018-06-23.00:00:00

[ 2018-06-23 after reflector discussion ]

Priority set to 3

Date: 2018-06-12.00:00:00

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.

As an example of why this lack of short circuiting is a problem in practice, consider the following line:

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).

I do not provide wording for the proposed fix in anticipation of requires clauses potentially changing how we do the specification, however, the basic solution should be similar to what we've done for other standard library types, which is to say that the very first constraint should be to check that if 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.

History
Date User Action Args
2023-11-22 15:47:43adminsetstatus: wp -> c++23
2021-10-14 09:56:08adminsetmessages: + msg12110
2021-10-14 09:56:08adminsetstatus: voting -> wp
2021-09-29 12:57:28adminsetstatus: ready -> voting
2021-08-20 17:21:40adminsetmessages: + msg12008
2021-08-20 17:21:40adminsetstatus: open -> ready
2021-05-21 00:53:53adminsetmessages: + msg11831
2018-11-12 05:21:03adminsetmessages: + msg10221
2018-11-12 05:21:03adminsetstatus: new -> open
2018-08-20 15:00:12adminsetmessages: + msg10083
2018-08-20 15:00:12adminsetmessages: + msg10082
2018-08-20 15:00:12adminsetmessages: + msg10081
2018-06-25 00:47:25adminsetmessages: + msg9987
2018-06-12 00:00:00admincreate