**Section:** 22.4.4.2 [tuple.cnstr] **Status:** C++17
**Submitter:** Stephan T. Lavavej **Opened:** 2013-09-21 **Last modified:** 2017-07-30 20:15:43 UTC

**Priority: **2

**View other** active issues in [tuple.cnstr].

**View all other** issues in [tuple.cnstr].

**View all issues with** C++17 status.

**Discussion:**

Consider the following code:

void meow(tuple<long, long>) { puts("Two"); } void meow(tuple<long, long, long>) { puts("Three"); } tuple<int, int, int> t(0, 0, 0); meow(t);

This should compile and print "Three" because `tuple<long, long>`'s constructor from
`const tuple<int, int, int>&` should remove itself from overload resolution.
Implementations sensibly do this, but the Standard doesn't actually say it!

In this case, `Types` is "`long, long`" and `UTypes` is "`int, int, int`". 22.4.4.2 [tuple.cnstr]/3
says "let `i` be in the range `[0,sizeof...(Types))` in order", which is `[0, 2)`. Then /17 says
"*Remark:* This constructor shall not participate in overload resolution unless `const Ui&` is implicitly
convertible to `Ti` for all `i`." Interpreted literally, this is true! /15 says
"*Requires:* `sizeof...(Types) == sizeof...(UTypes)`." but requiring the sizes to be identical doesn't help.
Only the special phrase "shall not participate in overload resolution unless" mandates SFINAE/`enable_if` machinery.

The wording that we need is almost available in the *Requires* paragraphs, except that the *Requires* paragraphs say
"`is_constructible`" while the Remark paragraphs say "is implicitly convertible", which is the correct thing for the SFINAE
constraints to check. My proposed resolution is to unify the *Requires* and *Remark* paragraphs, after which there
will be no need for *Requires* (when a constructor participates in overload resolution if and only if `X` is true,
then there's no need for it to *Require* that `X` is true).

Note: 21.3.5.4 [meta.unary.prop]/6 specifies `is_constructible<To, From>` and 21.3.7 [meta.rel]/4 specifies
`is_convertible<From, To>`. Both are specified in terms of
"`template <class T> typename add_rvalue_reference<T>::type create();`".
Therefore, passing `From` and `From&&` is equivalent, regardless of whether `From` is an object type,
an lvalue reference, or an rvalue reference.

Also note that 22.4.4.2 [tuple.cnstr]/3 defines `T0` and `T1` so we don't need to repeat their definitions.

*[2014-10-05, Daniel comments]*

This issue is closely related to LWG 2419.

*[2015-02, Cologne]*

AM: Howard wants to do something in this space and I want to wait for him to get a paper in.

Postponed.

*[2015-05, Lenexa]*

MC: handled by Daniel's tuple paper N4387

STL: look at status after N4387 applied.

*[2015-05-05, Daniel comments]*

N4387 doesn't touch these area intentionally. I agree with Howard that a different option exists that would introduce a TupleLike concept. Some implementations currently take advantage of this choice and this P/R would forbid them, which seems unfortunate to me.

*[2015-09, Telecon]*

Proposed resolution is obsolete.

Howard has considered writing a paper.

Status quo gives more implementation freedom.

**Previous resolution [SUPERSEDED]:**

This wording is relative to N3691.

Edit 22.4.4.2 [tuple.cnstr] as indicated:

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

~~-8-~~Requires:sizeof...(Types) == sizeof...(UTypes).is_constructible<Ti, Ui&&>::valueis true for alli.[…]

-10-

Remark:This constructor shall not participate in overload resolution unless~~each type in~~UTypesis implicitly convertible to its corresponding type inTypessizeof...(Types) == sizeof...(UTypes)and bothis_constructible<Ti, Ui>::valueandis_convertible<Ui, Ti>::valueare true for alli.[…]

template <class... UTypes> constexpr tuple(const tuple<UTypes...>& u);

~~-15-~~Requires:sizeof...(Types) == sizeof...(UTypes).is_constructible<Ti, const Ui&>::valueis true for alli.[…]

-17-

Remark:This constructor shall not participate in overload resolution unlessconst Ui&is implicitly convertible toTisizeof...(Types) == sizeof...(UTypes)and bothis_constructible<Ti, const Ui&>::valueandis_convertible<const Ui&, Ti>::valueare true for alli.template <class... UTypes> constexpr tuple(tuple<UTypes...>&& u);

~~-18-~~Requires:sizeof...(Types) == sizeof...(UTypes).is_constructible<Ti, Ui&&>::valueis true for alli.[…]

-20-

Remark:This constructor shall not participate in overload resolution unless~~each type in~~UTypesis implicitly convertible to its corresponding type inTypessizeof...(Types) == sizeof...(UTypes)and bothis_constructible<Ti, Ui>::valueandis_convertible<Ui, Ti>::valueare true for alli.template <class U1, class U2> constexpr tuple(const pair<U1, U2>& u);

~~-21-~~Requires:sizeof...(Types) == 2.is_constructible<T0, const U1&>::valueis true for the first typeT0in Types andis_constructible<T1, const U2&>::valueis true for the second typeT1inTypes.[…]

-23-

Remark:This constructor shall not participate in overload resolution unlessconst U1&is implicitly convertible toT0andconst U2&is implicitly convertible toT1sizeof...(Types) == 2 && is_constructible<T0, const U1&>::value && is_constructible<T1, const U2&>::value && is_convertible<const U1&, T0>::value && is_convertible<const U2&, T1>::valueis true.template <class U1, class U2> constexpr tuple(pair<U1, U2>&& u);

~~-24-~~Requires:sizeof...(Types) == 2.is_constructible<T0, U1&&>::valueis true for the first typeT0in Types andis_constructible<T1, U2&&>::valueis true for the second typeT1inTypes.[…]

-26-

Remark:This constructor shall not participate in overload resolution unlessU1is implicitly convertible toT0andU2is implicitly convertible toT1sizeof...(Types) == 2 && is_constructible<T0, U1>::value && is_constructible<T1, U2>::value && is_convertible<U1, T0>::value && is_convertible<U2, T1>::valueis true.

*[2016-03, Jacksonville]*

STL provides improved wording.

*[2016-06 Oulu]*

Tuesday: Adopt option 1, drop option B of 2549, and move to Ready.

Friday: status to Immediate

**Proposed resolution:**

This wording is relative to N4567.

Edit 22.4.4.2 [tuple.cnstr] as indicated:

template <class... UTypes>

*EXPLICIT*constexpr tuple(UTypes&&... u);~~-8-~~*Requires*:`sizeof...(Types) == sizeof...(UTypes)`.[…]

-10-

*Remarks*: This constructor shall not participate in overload resolution unless`sizeof...(Types) >= 1`and`sizeof...(Types) == sizeof...(UTypes)`and`is_constructible<`is*T*,_{i}*U*&&>::value_{i}`true`for all. The constructor is explicit if and only if*i*`is_convertible<`is*U*&&,_{i}*T*>::value_{i}`false`for at least one.*i*[…]

template <class... UTypes>

*EXPLICIT*constexpr tuple(const tuple<UTypes...>& u);~~-15-~~*Requires*:`sizeof...(Types) == sizeof...(UTypes)`.[…]

-17-

*Remarks*: This constructor shall not participate in overload resolution unless`sizeof...(Types) == sizeof...(UTypes)`and`is_constructible<`is*T*, const_{i}*U*&>::value_{i}`true`for all. The constructor is explicit if and only if*i*`is_convertible<const`is*U*&,_{i}*T*>::value_{i}`false`for at least one.*i*template <class... UTypes>

*EXPLICIT*constexpr tuple(tuple<UTypes...>&& u);~~-18-~~*Requires*:`sizeof...(Types) == sizeof...(UTypes)`.[…]

-20-

*Remarks*: This constructor shall not participate in overload resolution unless`sizeof...(Types) == sizeof...(UTypes)`and`is_constructible<`is*T*,_{i}*U*&&>::value_{i}`true`for all. The constructor is explicit if and only if*i*`is_convertible<`is*U*&&,_{i}*T*>::value_{i}`false`for at least one.*i*template <class U1, class U2>

*EXPLICIT*constexpr tuple(const pair<U1, U2>& u);~~-21-~~*Requires*:`sizeof...(Types) == 2`.[…]

-23-

*Remarks*: This constructor shall not participate in overload resolution unless`sizeof...(Types) == 2`and`is_constructible<`is*T*, const U1&>::value_{0}`true`and`is_constructible<`is*T*, const U2&>::value_{1}`true`. The constructor is explicit if and only if`is_convertible<const U1&,`is*T*>::value_{0}`false`or`is_convertible<const U2&,`is*T*>::value_{1}`false`.template <class U1, class U2>

*EXPLICIT*constexpr tuple(pair<U1, U2>&& u);~~-24-~~*Requires*:`sizeof...(Types) == 2`.[…]

-26-

*Remarks*: This constructor shall not participate in overload resolution unless`sizeof...(Types) == 2`and`is_constructible<`is*T*, U1&&>::value_{0}`true`and`is_constructible<`is*T*, U2&&>::value_{1}`true`. The constructor is explicit if and only if`is_convertible<U1&&,`is*T*>::value_{0}`false`or`is_convertible<U2&&,`is*T*>::value_{1}`false`.