This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3690. `std::make_from_tuple` etc. should find all tuple-like `std::get` overloads

Section: 16.4.2.2 [contents], 22.4.6 [tuple.apply] Status: New Submitter: Jiang An Opened: 2022-04-06 Last modified: 2022-05-17

Priority: 3

View all other issues in [contents].

View all issues with New status.

Discussion:

Currently it is not clear in 16.4.2.2 [contents]/3 whether all possible overloads in the standard library are considered to be found "in the context of `D`". As a result, it seems underspecified whether a certain `std::get` overload is found by `std::tuple_cat`, `std::make_from_tuple`, `std::apply`, or exposition-only concept `pair-like` or `has-tuple-element`.

There is implementation divergence: MSVC STL's `std::make_from_tuple` accepts `std::ranges::subrange`, but libstdc++'s doesn't, which is originally discussed in GCC bug #102301.

IMO `std::get` overloads need some special rules: when referred by tuple-like facilities, overloads for `std::variant` should be excluded (or at least leave whether it's found unspecified), and all other overloads should be found; and the opposite rule should be used when referred in 22.6 [variant].

[2022-04-25; Jiang An comments and provides wording]

Currently this program is accepted when using MSVC STL and libstdc++, although the acception seems unintended and problematic.

>     #include <variant>
>     #include <span>
>     #include <ranges>

>     struct Foo : std::variant<int, long> {};

>     template<>
>     struct std::tuple_element<0, Foo> { using type = int; };

>     template<>
>     struct std::tuple_element<1, Foo> { using type = long; };

>     template<>
>     struct std::tuple_size<Foo> : std::integral_constant<std::size_t, 2> {};

>     constexpr auto bad_keys = std::span<Foo>{} | std::views::values;

>     int main() {} // COMPILE-ONLY

[2022-05-17; Reflector poll]

Set priority to 3 after reflector poll.

Proposed resolution:

This wording is relative to N4910.

1. Modify 16.4.2.2 [contents] as indicated:

   > […]

   > -3- Whenever an unqualified name other than `swap` is used in the specification of a declaration `D` in Clause 17 [support] through Clause 33 [thread] or Annex D [depr], its meaning is established as-if by performing unqualified name lookup (6.5.3 [basic.lookup.unqual]) in the context of `D`.

   > [Note 1: Argument-dependent lookup is not performed. — end note]

   > Similarly, the meaning of a qualified-id is established as-if by performing qualified name lookup (6.5.5 [basic.lookup.qual]) in the context of `D`.

   > [Example 1: The reference to `is_array_v` in the specification of `std::to_array` (24.3.7.6 [array.creation]) refers to `::std::is_array_v`. — end example]

   > [Note 2: Operators in expressions (12.2.2.3 [over.match.oper]) are not so constrained; see 16.4.6.4 [global.functions]. — end note] The meaning of the unqualified name `swap` is established in an overload resolution context for swappable values (16.4.4.3 [swappable.requirements]).

   > Certain entities in the standard library are specified to select tuple-like `get` function templates. An implementation shall behave as if every tuple-like `get` function template is found in the definition of such an entity. Furthermore, an implementation shall ensure that no `get` function template that is not tuple-like is found in the definition of such an entity.

2. Add to the end of 22.3.4 [pair.astuple], 22.4.8 [tuple.elem], 24.3.7.7 [array.tuple], and 26.5.4.3 [range.subrange.access] as indicated:

   > The `get` function templates specified in this section are tuple-like (16.4.2.2 [contents]).

3. Modify 22.4.6 [tuple.apply] as indicated:

   >     template<class F, class Tuple>
   >       constexpr decltype(auto) apply(F&& f, Tuple&& t);

   > > -1- Effects: Given the exposition-only function:

   > > >     namespace std {
   > > >       template<class F, class Tuple, size_t... I>
   > > >       constexpr decltype(auto) apply-impl(F&& f, Tuple&& t, index_sequence<I...>) { // exposition only
   > > >         return INVOKE(std::forward<F>(f), get<I>(std::forward<Tuple>(t))...); // see 22.10.4 [func.require]
   > > >       }
   > > >     }

   > > Equivalent to:

   > > >     return apply-impl(std::forward<F>(f), std::forward<Tuple>(t),
   > > >                       make_index_sequence<tuple_size_v<remove_reference_t<Tuple>>>{});

   > > -?- Remarks: `apply-impl` selects tuple-like `get` function templates.

   >     template<class T, class Tuple>
   >       constexpr T make_from_tuple(Tuple&& t);

   > > -2- Mandates: If `tuple_size_v<remove_reference_t<Tuple>>` is `1`, then `reference_constructs_from_temporary_v<T, decltype(get<0>(declval<Tuple>()))>` is `false`.

   > > -3- Effects: Given the exposition-only function:

   > > >     namespace std {
   > > >       template<class T, class Tuple, size_t... I>
   > > >         requires is_constructible_v<T, decltype(get<I>(declval<Tuple>()))...>
   > > >       constexpr T make-from-tuple-impl(Tuple&& t, index_sequence<I...>) { // exposition only
   > > >         return T(get<I>(std::forward<Tuple>(t))...);
   > > >       }
   > > >     }

   > > Equivalent to:

   > > >     return make-from-tuple-impl<T>(
   > > >               std::forward<Tuple>(t),
   > > >               make_index_sequence<tuple_size_v<remove_reference_t<Tuple>>>{});

   > > […]

   > > -?- Remarks: `make-from-tuple-impl` selects tuple-like `get` function templates.

4. Add at the end of 26.5.4.1 [range.subrange.general] (after the synopsis) as indicated:

   > [Drafting note: Although IIUC `pair-like` is not needed to handle `array` and `subrange`.]

   > […]

   > -?- Remarks: `pair-like` selects tuple-like `get` function templates.

5. Add after the synopsis of 26.7.22.2 [range.elements.view] as indicated:

   > […]

   > -?- Remarks: `has-tuple-element` selects tuple-like `get` function templates.

   > […]