Doc. no. | P3341R0 |
Date: | 2024-06-24 |
Audience: | WG21 |
Reply to: | Jonathan Wakely <lwgchair@gmail.com> |
char
to sequences of wchar_t
Section: 22.14.6.4 [format.formatter.spec] Status: Ready Submitter: Mark de Wever Opened: 2023-06-01 Last modified: 2024-03-18
Priority: 3
View other active issues in [format.formatter.spec].
View all other issues in [format.formatter.spec].
Discussion:
I noticed some interesting features introduced by the range based formatters in C++23
// Ill-formed in C++20 and C++23 const char* cstr = "hello"; char* str = const_cast<char*>(cstr); std::format(L"{}", str); std::format(L"{}",cstr); // Ill-formed in C++20 // In C++23 they give L"['h', 'e', 'l', 'l', 'o']" std::format(L"{}", "hello"); // A libc++ bug prevents this from working. std::format(L"{}", std::string_view("hello")); std::format(L"{}", std::string("hello")); std::format(L"{}", std::vector{'h', 'e', 'l', 'l', 'o'});
An example is shown here. This only shows libc++ since libstdc++ and MSVC STL have not implemented the formatting ranges papers (P2286R8 and P2585R0) yet.
The difference between C++20 and C++23 is the existence of range formatters. These formatters use the formatter specializationformatter<char, wchar_t>
which converts the sequence of char
s
to a sequence of wchar_t
s.
In this conversion same_as<char, charT>
is false
, thus the requirements
of the range-type s
and ?s
([tab:formatter.range.type]) aren't met. So
the following is ill-formed:
std::format(L"{:s}", std::string("hello")); // Not L"hello"
It is surprising that some string types can be formatted as a sequence
of wide-characters, but others not. A sequence of characters can be a
sequence UTF-8 code units. This is explicitly supported in the width
estimation of string types. The conversion of char
to wchar_t
will
convert the individual code units, which will give incorrect results for
multi-byte code points. It will not transcode UTF-8 to UTF-16/32. The
current behavior is not in line with the note in
22.14.6.4 [format.formatter.spec]/2
[Note 1: Specializations such as
formatter<wchar_t, char>
andformatter<const char*, wchar_t>
that would require implicit multibyte / wide string or character conversion are disabled. — end note]
Disabling this could be done by explicitly disabling the char
to wchar_t
sequence formatter. Something along the lines of
template<ranges::input_range R> requires(format_kind<R> == range_format::sequence && same_as<remove_cvref_t<ranges::range_reference_t<R>>, char>) struct formatter<R, wchar_t> : __disabled_formatter {};
where __disabled_formatter
satisfies 22.14.6.4 [format.formatter.spec]/5, would
do the trick. This disables the conversion for all sequences not only
the string types. So vector
, array
, span
, etc. would be disabled.
range_formatter
. This allows
users to explicitly opt in to this formatter for their own
specializations.
An alternative would be to only disable this conversion for string type
specializations (22.14.6.4 [format.formatter.spec]/2.2) where char
to
wchar_t
is used:
template<size_t N> struct formatter<charT[N], charT>; template<class traits, class Allocator> struct formatter<basic_string<charT, traits, Allocator>, charT>; template<class traits> struct formatter<basic_string_view<charT, traits>, charT>;
Disabling following the following two is not strictly required:
template<> struct formatter<char*, wchar_t>; template<> struct formatter<const char*, wchar_t>;
However, if (const
) char*
becomes an input_range
in a future version C++, these formatters would become enabled.
Disabling all five instead of the three required specializations seems like a
future proof solution.
template<> struct formatter<wchar_t, char>;
there are no issues for wchar_t
to char
conversions.
Do we want to allow string types of char
s to be formatted as
sequences of wchar_t
s?
Do we want to allow non string type sequences of char
s to be
formatted as sequences of wchar_t
s?
Should we disable char
to wchar_t
conversion in the range_formatter
?
SG16 has indicated they would like to discuss this issue during a telecon.
[2023-06-08; Reflector poll]
Set status to SG16 and priority to 3 after reflector poll.
[2023-07-26; Mark de Wever provides wording confirmed by SG16]
[2024-03-18; Tokyo: move to Ready]
Proposed resolution:
This wording is relative to N4950.
Modify 22.14.6.4 [format.formatter.spec] as indicated:
[Drafting note: The unwanted conversion happens due to the
formatter
base class specialization (22.14.7.3 [format.range.fmtdef])struct range-default-formatter<range_format::sequence, R, charT>which is defined the header
<format>
. Therefore the disabling is only needed in this header) — end drafting note]
-2- […]
Theparse
member functions of these formatters interpret the format specification as a std-format-spec as described in 22.14.2.2 [format.string.std]. [Note 1: Specializations such asformatter<wchar_t, char>
andthat would require implicit multibyte / wide string or character conversion are disabled. — end note] -?- The headerformatter<const char*, wchar_t>
<format>
provides the following disabled specializations:
(?.1) — The string type specializations
template<> struct formatter<char*, wchar_t>; template<> struct formatter<const char*, wchar_t>; template<size_t N> struct formatter<char[N], wchar_t>; template<class traits, class Allocator> struct formatter<basic_string<char, traits, Allocator>, wchar_t>; template<class traits> struct formatter<basic_string_view<char, traits>, wchar_t>;-3- For any types
T
andcharT
for which neither the library nor the user provides an explicit or partial specialization of the class templateformatter
,formatter<T, charT>
is disabled.
submdspan
preconditions do not forbid creating invalid pointerSection: 24.7.3.7.7 [mdspan.sub.sub] Status: Tentatively Ready Submitter: Mark Hoemmen Opened: 2024-03-26 Last modified: 2024-05-08
Priority: Not Prioritized
Discussion:
Oliver Lee and Ryan Wooster pointed out to us that creating a submdspan
with zero-length
tuple-like
or strided_slice
slice specifiers at the upper extent can cause
the Standard submdspan_mapping
overloads to access the input mdspan
's mapping
out of bounds.
This happens in the following line of specification (24.7.3.7.6 [mdspan.sub.map] p8 in
N4971 moved to [mdspan.sub.map.common] p8 after the merge of
P2642R6).
Let
offset
be a value of typesize_t
equal to(*this)(first_<index_type, P>(slices...)...)
.
If data_handle_type
is a pointer to an array, then the resulting offset can be larger than
required_span_size()
, thus making the pointer invalid (not just one past the end). In a
constexpr context, the result is ill-formed. With the reference
mdspan
implementation, Clang can actually report a build error (e.g., for out-of-bounds access
to a std::array
). The contributed example illustrates this.
Example 1:
auto x = std::array<int, 3>{}; auto A = mdspan{x.data(), extents{3}}; auto B = submdspan(A, pair{3, 3});B is an
Example 2:mdspan
with zero elements.auto y = std::array<int, 9>{}; auto C = mdspan{y.data(), extents{3, 3}}; auto D = submdspan(C, pair{3, 3}, pair{3, 3});A precondition for each slice specifier is (24.7.3.7.5 [mdspan.sub.extents]):
0 ≤ first_<index_type, k>(slices...) ≤ last_<k>(src.extents(), slices...) ≤ src.extent(k).Our understanding is that precondition is satisfied. In the second example,
However, the submapping offset is defined asfirst_<0>
is 3 andfirst_<1>
is also 3.(*this)(first_<index_type, P>(slices...)...)
, which then can result in an invalid data handle of thesubmdspan
, even if the data handle is never accessed/dereferenced. godbolt demo
We expect this situation to come up in practice.
Suppose we have anN x N
mdspan representing a matrix A
, and we want to partition it
into a 2 x 2
"matrix of matrices" (also called a "block matrix"). This partitioning is a
common operation in linear algebra algorithms such as matrix factorizations.
Examples of this 2 x 2
partitioning appear in P2642 and P1673.
mdspan A{A_ptr, N, N}; size_t p = partition_point(N); // integer in 0, 1, …, N (inclusive) auto A_00 = submdspan(A, tuple{0, p}, tuple{0, p}); auto A_10 = submdspan(A, tuple{p, N}, tuple{0, 0}); auto A_01 = submdspan(A, tuple{0, p}, tuple{p, N}); auto A_11 = submdspan(A, tuple{p, N}, tuple{p, N});
Table illustrating the resulting 2 x 2
block matrix follows:
A_00 |
A_01 |
A_10 |
A_11 |
It's valid for p
to be 0
. That makes every block but A_11
have zero size.
Thus, it should also be valid for p
to be N
. That makes every block but
A_00
have zero size. However, that leads to the aforementioned UB.
first_
or last_
. The definitions of
first_
and last_
are meant to turn the slice specifier into a pair of bounds.
Since submdspan(A, tuple{p, N}, tuple{p, N})
is valid even if p
equals N
,
then that strongly suggests that first_<0>
and first_<1>
should always be p
, even if p
equals N
.
As a result, we find ourselves needing to change submdspan_mapping
. This will affect both
the Standard submdspan_mapping
overloads, and any custom (user-defined) overloads.
[2024-05-08; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4971 after the merge of P2642R6.
Modify the new 24.7.3.7.6.1 [mdspan.sub.map.common] as indicated:
-8- If
first_<index_type, k>(slices...)
equalsextents().extent(k)
for any rank indexk
ofextents()
, then lLetoffset
be a value of typesize_t
equal to(*this).required_span_size()
. Otherwise, letoffset
be a value of typesize_t
equal to(*this)(first_<index_type, P>(slices...)...)
.
Modify 24.7.3.7.7 [mdspan.sub.sub] as indicated:
As a drive-by readability fix, we also propose changing a variable name in paragraph 6 as indicated below.
template<class ElementType, class Extents, class LayoutPolicy, class AccessorPolicy, class... SliceSpecifiers> constexpr auto submdspan( const mdspan<ElementType, Extents, LayoutPolicy, AccessorPolicy>& src, SliceSpecifiers... slices) -> see below;-1- Let
-2- Letindex_type
betypename Extents::index_type
.sub_map_offset
be the result ofsubmdspan_mapping(src.mapping(), slices...)
. […] -3- Constraints: […] -4- Mandates: […] -5-Preconditions: […] -6- Effects: Equivalent to:auto sub_map_resultoffset= submdspan_mapping(src.mapping(), slices...); return mdspan(src.accessor().offset(src.data(), sub_map_resultoffset.offset), sub_map_resultoffset.mapping, AccessorPolicy::offset_policy(src.accessor()));
std::basic_format_context
be default-constructible/copyable/movable?Section: 22.14.6.7 [format.context] Status: Tentatively Ready Submitter: Jiang An Opened: 2024-03-24 Last modified: 2024-03-26
Priority: Not Prioritized
View all other issues in [format.context].
Discussion:
Per 22.14.6.7 [format.context], it seems that std::basic_format_context
has a default
constructor that is effectively defaulted, which means that it is default constructible if and only
if OutIt
is default constructible. Currently only libstdc++ makes it conditionally default
constructible, while libc++ and MSVC STL (together with fmtlib) make it never default constructible.
basic_format_context
objects are supposed to be created by the implementation
in some internal way, and user codes are only supposed to modify existing basic_format_context
objects during formatting.
[2024-05-08; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4971.
Modify 22.14.6.7 [format.context] as indicated:
namespace std { template<class Out, class charT> class basic_format_context { basic_format_args<basic_format_context> args_; // exposition only Out out_; // exposition only basic_format_context(const basic_format_context&) = delete; basic_format_context& operator=(const basic_format_context&) = delete; public: using iterator = Out; using char_type = charT; template<class T> using formatter_type = formatter<T, charT>; basic_format_arg<basic_format_context> arg(size_t id) const noexcept; std::locale locale(); iterator out(); void advance_to(iterator it); }; }
reference_wrapper
comparisons are not SFINAE-friendlySection: 22.10.6.6 [refwrap.comparisons] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2024-04-19 Last modified: 2024-04-24
Priority: Not Prioritized
Discussion:
P2944R3 added these hidden friends to reference_wrapper
:
friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper);
friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, const T&);
friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper<const T>);
These functions are not templates, and so their declarations are ill-formed for any type that does have any comparison operators, e.g.
struct A { } a;
std::reference_wrapper<A> r(a);
Instantiating reference_wrapper<A>
will instantiate
the declarations of the hidden friends, which will attempt to determine the
return types of the operator<=>
functions.
That fails because synth-three-way
is constrained
and can't be called with arguments of type A
.
This can be solved by changing those functions into templates, so they aren't instantiated eagerly, e.g.,
template<class U = T>
friend constexpr synth-three-way-result<TU> operator<=>(reference_wrapper, reference_wrapper);
or by giving them a deduced return type (so that it isn't instantiated eagerly)
and constraining them to only be callable when valid:
friend constexpr synth-three-way-result<T>auto operator<=>(reference_wrapper x, reference_wrapper y)
requires requires (const T t) { synth-three-way(t, t); }
The second alternative is used in the proposed resolution.
In practice the requires-clause can be implemented more simply (and efficiently) by checking the constraints of synth-three-way directly:
requires (const T t) { { t < t } -> boolean-testable; }
but when specified in prose in a Constraints: element it seems
clearer to just use synth-three-way(x.get(), y.get())
.
The proposed resolution has been committed to libstdc++'s master branch.
[2024-05-08; Reflector poll]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 22.10.6.1 [refwrap.general] as indicated:
// [refwrap.comparisons], comparisons friend constexpr bool operator==(reference_wrapper, reference_wrapper); friend constexpr bool operator==(reference_wrapper, const T&); friend constexpr bool operator==(reference_wrapper, reference_wrapper<const T>); friend constexpr
synth-three-way-result<T>auto operator<=>(reference_wrapper, reference_wrapper); friend constexprsynth-three-way-result<T>auto operator<=>(reference_wrapper, const T&); friend constexprsynth-three-way-result<T>auto operator<=>(reference_wrapper, reference_wrapper<const T>);
Modify 22.10.6.6 [refwrap.comparisons] as indicated:
friend constexpr
synth-three-way-result<T>auto operator<=>(reference_wrapper x, reference_wrapper y);-?- Constraints: The expression
synth-three-way(x.get(), y.get())
is well-formed.-7- Returns:
synth-three-way(x.get(), y.get())
.friend constexpr
synth-three-way-result<T>auto operator<=>(reference_wrapper x, const T& y);-?- Constraints: The expression
synth-three-way(x.get(), y)
is well-formed.-8- Returns:
synth-three-way(x.get(), y)
.friend constexpr
synth-three-way-result<T>auto operator<=>(reference_wrapper x, reference_wrapper<const T> y);-9- Constraints:
is_const_v<T>
isfalse
. The expressionsynth-three-way(x.get(), y.get())
is well-formed.-10- Returns:
synth-three-way(x.get(), y.get())
.
compatible-joinable-ranges
is underconstrainedSection: 26.7.15.2 [range.join.with.view] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-04-21 Last modified: 2024-04-27
Priority: Not Prioritized
View other active issues in [range.join.with.view].
View all other issues in [range.join.with.view].
Discussion:
join_with_view
requires the value type, reference and rvalue reference of the inner range
and pattern range to share common (reference) types through compatible-joinable-ranges
.
concat_view
and generator
do, this concept only requires that
these three types be valid and does not further check the relationship between them to be compatible
with the indirectly_readable
requirement for input_iterator
.
This results in a validly-constructed join_with_view
that may not model input_range
,
which seems unintended.
The proposed resolution aliases compatible-joinable-ranges
to concatable
i.e. specialization for two ranges to fully constrain, and I believe this could also be a better fit for
LWG 3971.
Previous resolution [SUPERSEDED]:
This wording is relative to N4981.
Modify 26.7.15.2 [range.join.with.view] as indicated:
namespace std::ranges { template<class R, class P> concept compatible-joinable-ranges = concatable<R, P>; // exposition onlycommon_with<range_value_t<R>, range_value_t<P>> && common_reference_with<range_reference_t<R>, range_reference_t<P>> && common_reference_with<range_rvalue_reference_t<R>, range_rvalue_reference_t<P>>;[…] }
[2024-04-24; Hewill Kang provides improved wording]
[2024-05-08; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 26.2 [ranges.syn] as indicated:
#include <compare> // see 17.11.1 [compare.syn] #include <initializer_list> // see 17.10.2 [initializer.list.syn] #include <iterator> // see 25.2 [iterator.synopsis] namespace std::ranges { […] // 26.7.15 [range.join.with], join with viewtemplate<class R, class P> concept compatible-joinable-ranges = see below; // exposition onlytemplate<input_range V, forward_range Pattern> requiresview<V> && input_range<range_reference_t<V>> && view<Pattern> && compatible-joinable-ranges<range_reference_t<V>, Pattern>see below class join_with_view; // freestanding […] }
Modify 26.7.15.2 [range.join.with.view] as indicated:
namespace std::ranges {template<class R, class P> concept compatible-joinable-ranges = // exposition only common_with<range_value_t<R>, range_value_t<P>> && common_reference_with<range_reference_t<R>, range_reference_t<P>> && common_reference_with<range_rvalue_reference_t<R>, range_rvalue_reference_t<P>>;[…] template<input_range V, forward_range Pattern> requires view<V> && input_range<range_reference_t<V>> && view<Pattern> &&compatible-joinable-rangesconcatable<range_reference_t<V>, Pattern> class join_with_view : public view_interface<join_with_view<V, Pattern>> { […] constexpr auto begin() const requires forward_range<const V> && forward_range<const Pattern> && is_reference_v<range_reference_t<const V>> && input_range<range_reference_t<const V>> && concatable<range_reference_t<const V>, const Pattern> { return iterator<true>{*this, ranges::begin(base_)}; } […] constexpr auto end() const requires forward_range<const V> && forward_range<const Pattern> && is_reference_v<range_reference_t<const V>> && input_range<range_reference_t<const V>> && concatable<range_reference_t<const V>, const Pattern> { […] } }; }
Modify 26.7.15.3 [range.join.with.iterator] as indicated:
namespace std::ranges { template<input_range V, forward_range Pattern> requires view<V> && input_range<range_reference_t<V>> && view<Pattern> &&compatible-joinable-rangesconcatable<range_reference_t<V>, Pattern> template<bool Const> class join_with_view<V, Pattern>::iterator { […] }; }
Modify 26.7.15.4 [range.join.with.sentinel] as indicated:
namespace std::ranges { template<input_range V, forward_range Pattern> requires view<V> && input_range<range_reference_t<V>> && view<Pattern> &&compatible-joinable-rangesconcatable<range_reference_t<V>, Pattern> template<bool Const> class join_with_view<V, Pattern>::sentinel { […] }; }
concat_view
should be freestandingSection: 17.3.2 [version.syn] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-04-21 Last modified: 2024-04-21
Priority: Not Prioritized
View other active issues in [version.syn].
View all other issues in [version.syn].
Discussion:
concat_view
can be freestanding, but this never seems to come up in the discussion,
which seems to be an oversight.
[2024-04-21; Daniel comments]
The specification of some member functions of concat_view
seem to depend on freestanding-deleted
get
overloads for variant
, but so does join_with_view
, which is marked as freestanding,
so it does not seem to be a good reason to accept join_with_view
but not concat_view
as freestanding.
[2024-05-08; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 17.3.2 [version.syn] as indicated:
#define __cpp_lib_ranges_concat 202403L // freestanding, also in <ranges>
Modify 26.2 [ranges.syn] as indicated:
#include <compare> // see 17.11.1 [compare.syn] #include <initializer_list> // see 17.10.2 [initializer.list.syn] #include <iterator> // see 25.2 [iterator.synopsis] namespace std::ranges { […] // 26.7.18 [range.concat], concat view template<input_range... Views> requires see below class concat_view; // freestanding namespace views { inline constexpr unspecified concat = unspecified; } // freestanding […] }
concat_view::iterator
's conversion constructorSection: 26.7.18.3 [range.concat.iterator] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-04-26 Last modified: 2024-04-27
Priority: Not Prioritized
View other active issues in [range.concat.iterator].
View all other issues in [range.concat.iterator].
Discussion:
This conversion constructor obtains the alternative iterator of the argument
through std::get
, which will throw when the variant
is valueless.
We seem to be missing a Preconditions element here.
[2024-05-08; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 26.7.18.3 [range.concat.iterator] as indicated:
constexpr iterator(iterator<!Const> it) requires Const && (convertible_to<iterator_t<Views>, iterator_t<const Views>> && ...);-?- Preconditions:
-8- Effects: Initializesit.it_.valueless_by_exception()
isfalse
.parent_
withit.parent_
, and leti
beit.it_.index()
, initializesit_
withbase-iter(in_place_index<i>, std::get<i>(std::move(it.it_)))
.
views::concat(r)
is well-formed when r
is an output_range
Section: 26.7.18.1 [range.concat.overview] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-04-27 Last modified: 2024-04-28
Priority: Not Prioritized
Discussion:
Currently, views::concat
will return views::all(r)
when it takes only one argument,
which only requires that the type of r
models viewable_range
which includes output_range
:
std::vector<int> v;
auto r = std::views::counted(std::back_inserter(v), 3);
auto c = std::views::concat(r); // well-formed
Since concat_view
requires all ranges to be input_range
, this seems inconsistent.
We should reject the above just like views::zip_transform
still requires F
to be
move_constructible
in the case of an empty pack.
[2024-05-08; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 26.7.18.1 [range.concat.overview] as indicated:
-2- The name
views::concat
denotes a customization point object (16.3.3.3.5 [customization.point.object]). Given a pack of subexpressionsEs...
, the expressionviews::concat(Es...)
is expression-equivalent to
(2.1) —
views::all(Es...)
ifEs
is a pack with only one element whose type modelsinput_range
,(2.2) — otherwise,
concat_view(Es...)
.
views::as_rvalue
should reject non-input rangesSection: 26.7.7.1 [range.as.rvalue.overview] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-04-27 Last modified: 2024-04-28
Priority: Not Prioritized
View other active issues in [range.as.rvalue.overview].
View all other issues in [range.as.rvalue.overview].
Discussion:
views::as_rvalue(r)
equivalent to views::all(r)
when r
's reference and rvalue reference are
of the same type, which means that in this case we only need to check whether the type of r
models viewable_range
.
as_rvalue_view{r}
to be valid, which leads to
divergence when r
is not an input_range
(demo):
#include <ranges>
struct I {
int operator*();
using difference_type = int;
I& operator++();
void operator++(int);
};
std::ranges::range auto r = std::ranges::subrange{I{}, std::unreachable_sentinel}
| std::views::as_rvalue; // // well-formed in libc++/MSVC-STL, ill-formed in libstdc++
Although this is precisely a bug in libstdc++ that does not conform to the current wording, it is reasonable to
require r
to be an input_range
to be consistent with the constraints of as_rvalue_view
.
[2024-05-08; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 26.7.7.1 [range.as.rvalue.overview] as indicated:
-2- The name
views::as_rvalue
denotes a range adaptor object (26.7.2 [range.adaptor.object]). LetE
be an expression and letT
bedecltype((E))
. The expressionviews::as_rvalue(E)
is expression-equivalent to:
(2.1) —
views::all(E)
ifT
modelsinput_range
andsame_as<range_rvalue_reference_t<T>, range_reference_t<T>>
istrue
.(2.2) — Otherwise,
as_rvalue_view(E)
.
views::iota(views::iota(0))
should be rejectedSection: 26.6.4.1 [range.iota.overview] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-05-08 Last modified: 2024-05-09
Priority: Not Prioritized
Discussion:
views::iota(E)
literally means incrementing element E endlessly, but
views::iota(views::iota(0))
is currently well-formed due to CTAD,
rejecting such unreasonable spelling seems therefore reasonable.
[2024-06-24; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 26.6.4.1 [range.iota.overview] as indicated:
-1-
-2- The nameiota_view
generates a sequence of elements by repeatedly incrementing an initial value.views::iota
denotes a customization point object (16.3.3.3.5 [customization.point.object]). Given subexpressionsE
andF
, the expressionsviews::iota(E)
andviews::iota(E, F)
are expression-equivalent toiota_view<decay_t<decltype((E))>>(E)
andiota_view(E, F)
, respectively.
views::adjacent<0>
should reject non-forward rangesSection: 26.7.27.1 [range.adjacent.overview], 26.7.28.1 [range.adjacent.transform.overview] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-05-10 Last modified: 2024-06-24
Priority: Not Prioritized
View all other issues in [range.adjacent.overview].
Discussion:
Following-up LWG 4082 and LWG 4083, the current wording makes
views::adjacent<0>(r)
and views::adjacent_transform<0>(r, [] { return 0; })
well-formed even when r
is just an input range or an output range, which seems to be an oversight.
[2024-06-24; Reflector poll]
Set status to Tentatively Ready after six votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 26.7.27.1 [range.adjacent.overview] as indicated:
-2- The name
views::adjacent<N>
denotes a range adaptor object (26.7.2 [range.adaptor.object]). Given a subexpressionE
and a constant expressionN
, the expressionviews::adjacent<N>(E)
is expression-equivalent to
(2.1) —
((void)E, auto(views::empty<tuple<>>))
ifN
is equal to0
anddecltype((E))
modelsforward_range
,(2.2) — otherwise,
adjacent_view<views::all_t<decltype((E))>, N>(E)
.
Modify 26.7.28.1 [range.adjacent.transform.overview] as indicated:
-2- The name
views::adjacent_transform<N>
denotes a range adaptor object (26.7.2 [range.adaptor.object]). Given subexpressionsE
andF
and a constant expressionN
:
(2.1) — If
N
is equal to0
anddecltype((E))
modelsforward_range
,views::adjacent_transform<N>(E, F)
is expression-equivalent to((void)E, views::zip_transform(F))
, except that the evaluations ofE
andF
are indeterminately sequenced.(2.2) — Otherwise, the expression
views::adjacent_transform<N>(E, F)
is expression-equivalent toadjacent_transform_view<views::all_t<decltype((E))>, decay_t<decltype((F))>, N>(E, F)
.
ranges::ends_with
's Returns misses difference castingSection: 27.6.17 [alg.ends.with] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-05-17 Last modified: 2024-05-19
Priority: Not Prioritized
Discussion:
The Returns of the ranges version of ranges::ends_with
are specified as
ranges::equal(ranges::drop_view(ranges::ref_view(r1), N1 - N2), r2, ...)
which is not quite right
when N2
is an integer-class type and N1
is an integer type, because in this case
N1 - N2
will be an integer-class type which cannot be implicitly converted to the
difference_type
of r1
leading to the construction of drop_view
being ill-formed.
[2024-06-24; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 27.6.17 [alg.ends.with] as indicated:
template<input_range R1, input_range R2, class Pred = ranges::equal_to, class Proj1 = identity, class Proj2 = identity> requires (forward_range<R1> || sized_range<R1>) && (forward_range<R2> || sized_range<R2>) && indirectly_comparable<iterator_t<R1>, iterator_t<R2>, Pred, Proj1, Proj2> constexpr bool ranges::ends_with(R1&& r1, R2&& r2, Pred pred = {}, Proj1 proj1 = {}, Proj2 proj2 = {});-3- Let
-4- Returns:N1
beranges::distance(r1)
andN2
beranges::distance(r2)
.false
ifN1 < N2
, otherwiseranges::equal(ranges::drop_view(ranges::ref_view(r1), N1 - static_cast<decltype(N1)>(N2)), r2, pred, proj1, proj2)
basic_format_args
should not be default-constructibleSection: 22.14.8.3 [format.args] Status: Tentatively Ready Submitter: Hewill Kang Opened: 2024-05-17 Last modified: 2024-05-19
Priority: Not Prioritized
View all other issues in [format.args].
Discussion:
It's unclear why basic_format_args
originally provided a default constructor and its actual use cases,
all three major libraries declare them as default
, which allows value initialization of
basic_format_args
at compile time.
constexpr
specifier.
Additionally, the current wording only initializes the size_
member in the default constructor, which
may lead to undefined behavior when copying basic_format_args
as uninitialized data_
member
is copied. There is also an implementation divergence (demo):
#include <format>
constexpr std::format_args fmt_args; // only well-formed in MSVC-STL
One option is to add default member initializers for all members and default
the default constructor
to best match the status quo, which guarantees that basic_format_args
is constexpr
-able.
basic_format_args
has different implementation details in the three libraries,
its actual members may not be size_
and data_
. It is unnecessary to ensure that all
the internal members are initialized when default-constructed basic_format_args
, indicating that not
providing one is reasonable.
The proposed solution is to prefer the more aggressive one.
[2024-05-19; Daniel comments]
The here suggested proposal to remove the default constructor implicitly depends on the decision of
LWG 4061 to remove basic_format_context
's default constructor, since its usage
would require that the exposition-only member args_
of type basic_format_args<basic_format_context>
can be default-constructed as well.
[2024-06-24; Reflector poll]
Set status to Tentatively Ready after eight votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 22.14.8.3 [format.args] as indicated:
[…]namespace std { template<class Context> class basic_format_args { size_t size_; // exposition only const basic_format_arg<Context>* data_; // exposition only public:basic_format_args() noexcept;template<class... Args> basic_format_args(const format-arg-store<Context, Args...>& store) noexcept; basic_format_arg<Context> get(size_t i) const noexcept; }; […] }basic_format_args() noexcept;
-2- Effects Initializessize_
with0
.