Title
std::make_from_tuple etc. should find all tuple-like std::get overloads
Status
new
Section
[contents][tuple.apply]
Submitter
Jiang An

Created on 2022-04-06.00:00:00 last changed 23 months ago

Messages

Date: 2022-05-17.11:58:16

Proposed resolution:

This wording is relative to N4910.

  1. Modify [contents] as indicated:

    […]

    -3- Whenever an unqualified name other than swap is used in the specification of a declaration D in Clause [support] through Clause [thread] or Annex [depr], its meaning is established as-if by performing unqualified name lookup ([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 ([basic.lookup.qual]) in the context of D.

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

    [Note 2: Operators in expressions ([over.match.oper]) are not so constrained; see [global.functions]. — end note] The meaning of the unqualified name swap is established in an overload resolution context for swappable values ([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 [pair.astuple], [tuple.elem], [array.tuple], and [range.subrange.access] as indicated:

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

  3. Modify [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 [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 [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 [range.elements.view] as indicated:

    […]

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

    […]

Date: 2022-05-15.00:00:00

[ 2022-05-17; Reflector poll ]

Set priority to 3 after reflector poll.

Date: 2022-04-15.00:00:00

[ 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
Date: 2022-04-06.00:00:00

Currently it is not clear in [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 [variant].

History
Date User Action Args
2022-05-17 11:58:16adminsetmessages: + msg12463
2022-04-30 11:18:02adminsetmessages: + msg12440
2022-04-30 11:18:02adminsetmessages: + msg12439
2022-04-06 00:00:00admincreate