std::make_from_tuple
etc. should find all tuple-like std::get
overloadsSection: 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
.
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.
Modify 16.4.2.2 [contents] as indicated:
[…]
-3- Whenever an unqualified name other thanswap
is used in the specification of a declarationD
in Clause 17 [support] through Clause 32 [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 ofD
. [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 ofD
. [Example 1: The reference tois_array_v
in the specification ofstd::to_array
(23.3.3.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 nameswap
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-likeget
function templates. An implementation shall behave as if every tuple-likeget
function template is found in the definition of such an entity. Furthermore, an implementation shall ensure that noget
function template that is not tuple-like is found in the definition of such an entity.
Add to the end of 22.3.4 [pair.astuple], 22.4.8 [tuple.elem], 23.3.3.7 [array.tuple], and 25.5.4.3 [range.subrange.access] as indicated:
The
get
function templates specified in this section are tuple-like (16.4.2.2 [contents]).
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-likeget
function templates.template<class T, class Tuple> constexpr T make_from_tuple(Tuple&& t);-2- Mandates: If
-3- Effects: Given the exposition-only function:tuple_size_v<remove_reference_t<Tuple>>
is1
, thenreference_constructs_from_temporary_v<T, decltype(get<0>(declval<Tuple>()))>
isfalse
.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-likeget
function templates.
Add at the end of 25.5.4.1 [range.subrange.general] (after the synopsis) as indicated:
[Drafting note: Although IIUC
pair-like
is not needed to handlearray
andsubrange
.]
[…]
-?- Remarks:pair-like
selects tuple-likeget
function templates.
Add after the synopsis of 25.7.23.2 [range.elements.view] as indicated:
[…]
-?- Remarks:has-tuple-element
selects tuple-likeget
function templates. […]