Revised 2025-10-23 at 15:49:53 UTC
streamsize in iostreamsSection: 31 [input.output] Status: Open Submitter: Martin Sebor Opened: 2003-09-18 Last modified: 2018-12-09
Priority: 3
View all other issues in [input.output].
View all issues with Open status.
Discussion:
A third party test suite tries to exercise istream::ignore(N) with a negative value of
N and expects that the implementation will treat N as if it were 0. Our
implementation asserts that (N >= 0) holds and aborts the test.
I can't find anything in section 27 that prohibits such values but I don't
see what the effects of such calls should be, either (this applies to
a number of unformatted input functions as well as some member functions
of the basic_streambuf template).
[ 2009-07 Frankfurt ]
This is related to LWG 255(i).
Move to NAD Future.
[LEWG Kona 2017]
Recommend Open: We agree that we should require N >= 0 for the selected functions
[2018-12-04 Reflector prioritization]
Set Priority to 3
Proposed resolution:
I propose that we add to each function in clause 27 that takes an argument, say N, of type
streamsize a Requires clause saying that "N >= 0." The intent is to allow
negative streamsize values in calls to precision() and width() but disallow it in
calls to streambuf::sgetn(), istream::ignore(), or ostream::write().
[Kona: The LWG agreed that this is probably what we want. However, we
need a review to find all places where functions in clause 27 take
arguments of type streamsize that shouldn't be allowed to go
negative. Martin will do that review.]
TSection: 24.3.5.3 [input.iterators] Status: Open Submitter: Chris Jefferson Opened: 2004-09-16 Last modified: 2023-06-25
Priority: 3
View other active issues in [input.iterators].
View all other issues in [input.iterators].
View all issues with Open status.
Discussion:
From comp.std.c++:
I note that given an input iterator a for type T,
then *a only has to be "convertable to T",
not actually of type T.
Firstly, I can't seem to find an exact definition of "convertable to T".
While I assume it is the obvious definition (an implicit conversion), I
can't find an exact definition. Is there one?
Slightly more worryingly, there doesn't seem to be any restriction on
the this type, other than it is "convertable to T". Consider two input
iterators a and b. I would personally assume that most people would
expect *a==*b would perform T(*a)==T(*b), however it doesn't seem that
the standard requires that, and that whatever type *a is (call it U)
could have == defined on it with totally different symantics and still
be a valid inputer iterator.
Is this a correct reading? When using input iterators should I write
T(*a) all over the place to be sure that the object I'm using is the
class I expect?
This is especially a nuisance for operations that are defined to be
"convertible to bool". (This is probably allowed so that
implementations could return say an int and avoid an unnecessary
conversion. However all implementations I have seen simply return a
bool anyway. Typical implementations of STL algorithms just write
things like while(a!=b && *a!=0). But strictly
speaking, there are lots of types that are convertible to T but
that also overload the appropriate operators so this doesn't behave
as expected.
If we want to make code like this legal (which most people seem to
expect), then we'll need to tighten up what we mean by "convertible
to T".
[Lillehammer: The first part is NAD, since "convertible" is well-defined in core. The second part is basically about pathological overloads. It's a minor problem but a real one. So leave open for now, hope we solve it as part of iterator redesign.]
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-10 Santa Cruz: ]
Mark as NAD Future. We agree there's an issue, but there is no proposed solution at this time and this will be solved by concepts in the future.
[2017-02 in Kona, LEWG recommends NAD]
Has been clarified by 14. By design. Ranges might make it go away. Current wording for input iterators is more constrained.
[2017-06-02 Issues Telecon]
Move to Open. This is very similar to 2962(i), possibly a duplicate.
Marshall to research
[2017-07 Toronto Thurs Issue Prioritization]
Previous resolution [SUPERSEDED]:
Rationale:
[ San Francisco: ]
Solved by N2758.
[2023-06; Varna]
During LWG discussion of this issue it was decided to reduce the priority to 3.
Furthermore, the still presented "Solved by" comment has been recognized as being no longer true, since the referred to pre-C++11 concept paper wording N2758 is no longer part of the working paper. It also has been observed, that the "convertible tobool" part has since been resolved
by P1964 and the follow-up paper P2167.
Also LWG 3105(i) has a lot of overlap with this issue.
Proposed resolution:
Section: 28.6 [re] Status: Open Submitter: Eric Niebler Opened: 2005-07-01 Last modified: 2020-07-17
Priority: 4
View other active issues in [re].
View all other issues in [re].
View all issues with Open status.
Discussion:
A problem with TR1 regex is currently being discussed on the Boost
developers list. It involves the handling of case-insensitive matching
of character ranges such as [Z-a]. The proper behavior (according to the
ECMAScript standard) is unimplementable given the current specification
of the TR1 regex_traits<> class template. John Maddock, the author of
the TR1 regex proposal, agrees there is a problem. The full discussion
can be found at http://lists.boost.org/boost/2005/06/28850.php (first
message copied below). We don't have any recommendations as yet.
-- Begin original message --
The situation of interest is described in the ECMAScript specification (ECMA-262), section 15.10.2.15:
"Even if the pattern ignores case, the case of the two ends of a range is significant in determining which characters belong to the range. Thus, for example, the pattern /[E-F]/i matches only the letters E, F, e, and f, while the pattern /[E-f]/i matches all upper and lower-case ASCII letters as well as the symbols [, \, ], ^, _, and `."
A more interesting case is what should happen when doing a case-insensitive match on a range such as [Z-a]. It should match z, Z, a, A and the symbols [, \, ], ^, _, and `. This is not what happens with Boost.Regex (it throws an exception from the regex constructor).
The tough pill to swallow is that, given the specification in TR1, I
don't think there is any effective way to handle this situation.
According to the spec, case-insensitivity is handled with
regex_traits<>::translate_nocase(CharT) — two characters are equivalent
if they compare equal after both are sent through the translate_nocase
function. But I don't see any way of using this translation function to
make character ranges case-insensitive. Consider the difficulty of
detecting whether "z" is in the range [Z-a]. Applying the transformation
to "z" has no effect (it is essentially std::tolower). And we're not
allowed to apply the transformation to the ends of the range, because as
ECMA-262 says, "the case of the two ends of a range is significant."
So AFAICT, TR1 regex is just broken, as is Boost.Regex. One possible fix is to redefine translate_nocase to return a string_type containing all the characters that should compare equal to the specified character. But this function is hard to implement for Unicode, and it doesn't play nice with the existing ctype facet. What a mess!
-- End original message --
[ John Maddock adds: ]
One small correction, I have since found that ICU's regex package does implement this correctly, using a similar mechanism to the current TR1.Regex.
Given an expression [c1-c2] that is compiled as case insensitive it:
Enumerates every character in the range c1 to c2 and converts it to it's case folded equivalent. That case folded character is then used a key to a table of equivalence classes, and each member of the class is added to the list of possible matches supported by the character-class. This second step isn't possible with our current traits class design, but isn't necessary if the input text is also converted to a case-folded equivalent on the fly.
ICU applies similar brute force mechanisms to character classes such as [[:lower:]] and [[:word:]], however these are at least cached, so the impact is less noticeable in this case.
Quick and dirty performance comparisons show that expressions such as "[X-\\x{fff0}]+" are indeed very slow to compile with ICU (about 200 times slower than a "normal" expression). For an application that uses a lot of regexes this could have a noticeable performance impact. ICU also has an advantage in that it knows the range of valid characters codes: code points outside that range are assumed not to require enumeration, as they can not be part of any equivalence class. I presume that if we want the TR1.Regex to work with arbitrarily large character sets enumeration really does become impractical.
Finally note that Unicode has:
Three cases (upper, lower and title). One to many, and many to one case transformations. Character that have context sensitive case translations - for example an uppercase sigma has two different lowercase forms - the form chosen depends on context(is it end of a word or not), a caseless match for an upper case sigma should match either of the lower case forms, which is why case folding is often approximated by tolower(toupper(c)).
Probably we need some way to enumerate character equivalence classes, including digraphs (either as a result or an input), and some way to tell whether the next character pair is a valid digraph in the current locale.
Hoping this doesn't make this even more complex that it was already,
[ Portland: Alisdair: Detect as invalid, throw an exception. Pete: Possible general problem with case insensitive ranges. ]
[ 2009-07 Frankfurt ]
We agree that this is a problem, but we do not know the answer.
We are going to declare this NAD until existing practice leads us in some direction.
No objection to NAD Future.
Move to NAD Future.
[LEWG Kona 2017]
Recommend Open: Tim Shen proposes: forbid use of case-insensitive ranges with regex traits other than
std::regex_traits<{char, wchar_t, char16_t, char32_t}> when regex_constants::collate is specified.
[2020-07-17; Priority set to 4 in telecon]
Proposed resolution:
std::array is a sequence that doesn't satisfy the sequence requirements?Section: 23.3.3 [array] Status: Open Submitter: Bo Persson Opened: 2006-12-30 Last modified: 2022-11-12
Priority: 3
View all other issues in [array].
View all issues with Open status.
Discussion:
The <array> header is given under 23.3 [sequences].
23.3.3 [array]/paragraph 3 says:
"Unless otherwise specified, all array operations are as described in 23.2 [container.requirements]".
However, array isn't mentioned at all in section 23.2 [container.requirements].
In particular, Table 82 "Sequence requirements" lists several operations (insert, erase, clear)
that std::array does not have in 23.3.3 [array].
Also, Table 83 "Optional sequence operations" lists several operations that
std::array does have, but array isn't mentioned.
[ 2009-07 Frankfurt ]
The real issue seems to be different than what is described here. Non-normative text says that
std::arrayis a sequence container, but there is disagreement about what that really means. There are two possible interpretations:
- a sequence container is one that satisfies all sequence container requirements
- a sequence container is one that satisfies some of the sequence container requirements. Any operation that the container supports is specified by one or more sequence container requirements, unless that operation is specifically singled out and defined alongside the description of the container itself.
Move to Tentatively NAD.
[ 2009-07-15 Loïc Joly adds: ]
The section 23.2.4 [sequence.reqmts]/1 states that array is a sequence. 23.2.4 [sequence.reqmts]/3 introduces table 83, named Sequence container requirements. This seems to me to be defining the requirements for all sequences. However, array does not follow all of this requirements (this can be read in the array specific section, for the standard is currently inconsistent).
Proposed resolution 1 (minimal change):
Say that array is a container, that in addition follows only some of the sequence requirements, as described in the array section:
The library provides
fivethree basic kinds of sequence containers:,arrayvector,,forward_listlist, anddeque. In addition,arrayandforward_listfollows some of the requirements of sequences, as described in their respective sections.Proposed resolution 2 (most descriptive description, no full wording provided):
Introduce the notion of a Fixed Size Sequence, with it requirement table that would be a subset of the current Sequence container. array would be the only Fixed Size Sequence (but dynarray is in the queue for TR2). Sequence requirements would now be requirements in addition to Fixed Size Sequence requirements (it is currently in addition to container).
[ 2009-07 Frankfurt: ]
Move to NAD Editorial
[ 2009 Santa Cruz: ]
This will require a lot of reorganization. Editor doesn't think this is really an issue, since the description of array can be considered as overriding what's specified about sequences. Move to NAD.
[2022-10-27; Hubert Tong comments and requests to reopen]
This issue appears to be unresolved (should not be NAD).
As noted in 23.3.3.1 [array.overview] paragraph 3,array
does not meet 23.2.2.2 [container.reqmts] paragraph 10. This means that
array does not meet the container requirements, never mind the requirements
for sequence containers or contiguous containers.
However, there is wording that claims the opposite.
23.2.4 [sequence.reqmts] paragraph 1:
In addition,
arrayis provided as a sequence container which provides limited sequence operations because it has a fixed number of elements.
(Perhaps the above should be worded with "except".)
23.3.1 [sequences.general] paragraph 1:The headers
<array>[…] define class templates that meet the requirements for sequence containers.
23.3.3.1 [array.overview] paragraph 1:
[…] An
arrayis a contiguous container (23.2.2 [container.requirements.general]).
In this comment,
Casey suggests that the requirements be changed so that array does meet the requirements.
[Kona 2022-11-12; Set Priority to 3]
Proposed resolution:
Section: 32.6.4 [thread.mutex.requirements] Status: LEWG Submitter: Pete Becker Opened: 2008-12-05 Last modified: 2025-10-21
Priority: 4
View other active issues in [thread.mutex.requirements].
View all other issues in [thread.mutex.requirements].
View all issues with LEWG status.
Duplicate of: 961
Discussion:
32.6.4 [thread.mutex.requirements] describes the requirements for a type to be
a "Mutex type". A Mutex type can be used as the template argument for
the Lock type that's passed to condition_variable_any::wait (although
Lock seems like the wrong name here, since Lock is given a different
formal meaning in 32.6.5 [thread.lock]) and, although the WD doesn't quite say
so, as the template argument for lock_guard and unique_lock.
The requirements for a Mutex type include:
m.lock() shall be well-formed and have [described] semantics, including a return type of void.
m.try_lock() shall be well-formed and have [described] semantics, including a return type of bool.
m.unlock() shall be well-formed and have [described] semantics, including a return type of void.
Also, a Mutex type "shall not be copyable nor movable".
The latter requirement seems completely irrelevant, and the three
requirements on return types are tighter than they need to be. For
example, there's no reason that lock_guard can't be instantiated with a
type that's copyable. The rule is, in fact, that lock_guard, etc. won't
try to copy objects of that type. That's a constraint on locks, not on
mutexes. Similarly, the requirements for void return types are
unnecessary; the rule is, in fact, that lock_guard, etc. won't use any
returned value. And with the return type of bool, the requirement should
be that the return type is convertible to bool.
[ Summit: ]
Move to open. Related to conceptualization and should probably be tackled as part of that.
- The intention is not only to place a constraint on what types such as
lock_guardmay do with mutex types, but on what any code, including user code, may do with mutex types. Thus the constraints as they are apply to the mutex types themselves, not the current users of mutex types in the standard.- This is a low priority issue; the wording as it is may be overly restrictive but this may not be a real issue.
[ Post Summit Anthony adds: ]
Section 32.6.4 [thread.mutex.requirements] conflates the requirements on a generic Mutex type (including user-supplied mutexes) with the requirements placed on the standard-supplied mutex types in an attempt to group everything together and save space.
When applying concepts to chapter 30, I suggest that the concepts
LockableandTimedLockableembody the requirements for *use* of a mutex type as required byunique_lock/lock_guard/condition_variable_any. These should be relaxed as Pete describes in the issue. The existing words in 32.6.4 [thread.mutex.requirements] are requirements on all ofstd::mutex,std::timed_mutex,std::recursive_mutexandstd::recursive_timed_mutex, and should be rephrased as such.
[2017-03-01, Kona]
SG1: Agreement that we need a paper.
[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
Section: 32.6.4 [thread.mutex.requirements] Status: LEWG Submitter: Pete Becker Opened: 2009-01-07 Last modified: 2025-10-21
Priority: 4
View other active issues in [thread.mutex.requirements].
View all other issues in [thread.mutex.requirements].
View all issues with LEWG status.
Duplicate of: 936
Discussion:
32.6.4 [thread.mutex.requirements] describes required member functions of mutex types, and requires that they throw exceptions under certain circumstances. This is overspecified. User-defined types can abort on such errors without affecting the operation of templates supplied by standard-library.
[ Summit: ]
Move to open. Related to conceptualization and should probably be tackled as part of that.
[ 2009-10 Santa Cruz: ]
Would be OK to leave it as is for time constraints, could loosen later.
Mark as NAD Future.
[2017-03-01, Kona]
SG1: Agreement that we need a paper.
[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
std::vector's reallocation policy still unclearSection: 23.3.13.3 [vector.capacity] Status: Open Submitter: Daniel Krügler Opened: 2009-04-20 Last modified: 2020-07-17
Priority: 3
View other active issues in [vector.capacity].
View all other issues in [vector.capacity].
View all issues with Open status.
Discussion:
I have the impression that even the wording of current draft
N2857
does insufficiently express the intent of vector's
reallocation strategy. This has produced not too old library
implementations which release memory in the clear() function
and even modern articles about C++ programming cultivate
the belief that clear is allowed to do exactly this. A typical
example is something like this:
const int buf_size = ...;
std::vector<T> buf(buf_size);
for (int i = 0; i < some_condition; ++i) {
buf.resize(buf_size);
write_or_read_data(buf.data());
buf.clear(); // Ensure that the next round get's 'zeroed' elements
}
where still the myth is ubiquitous that buf might be
allowed to reallocate it's memory inside the for loop.
IMO the problem is due to the fact, that
std::vector
is explained in 23.3.13.3 [vector.capacity]/3 and /6 which
are describing just the effects of the reserve
function, but in many examples (like above) there
is no explicit call to reserve involved. Further-more
23.3.13.3 [vector.capacity]/6 does only mention insertions
and never mentions the consequences of erasing elements.
the effects clause of std::vector's erase overloads in
23.3.13.5 [vector.modifiers]/4 is silent about capacity changes. This
easily causes a misunderstanding, because the counter
parting insert functions described in 23.3.13.5 [vector.modifiers]/2
explicitly say, that
Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
It requires a complex argumentation chain about four
different places in the standard to provide the — possibly
weak — proof that calling clear() also does never change
the capacity of the std::vector container. Since std::vector
is the de-facto replacement of C99's dynamic arrays this
type is near to a built-in type and it's specification should
be clear enough that usual programmers can trust their
own reading.
[ Batavia (2009-05): ]
Bill believes paragraph 1 of the proposed resolution is unnecessary because it is already implied (even if tortuously) by the current wording.
Move to Review.
[ 2009-10 Santa Cruz: ]
Mark as NAD. Rationale: there is no consensus to clarify the standard, general consensus that the standard is correct as written.
[2020-05-08; Reopen after reflector discussions]
"correct as written" has been disputed.
[2020-07-17; Priority set to 3 in telecon]
Proposed resolution:
[
This is a minimum version. I also
suggest that the wording explaining the allocation strategy
of std::vector in 23.3.13.3 [vector.capacity]/3 and /6 is moved into
a separate sub paragraph of 23.3.13.3 [vector.capacity] before
any of the prototype's are discussed, but I cannot provide
reasonable wording changes now.
]
Change 23.3.13.3 [vector.capacity]/6 as follows:
It is guaranteed that no reallocation takes place during insertions or erasures that happen after a call to
reserve()until the time when an insertion would make the size of the vector greater than the value ofcapacity().
Change 23.3.13.5 [vector.modifiers]/4 as follows:
Effects: The capacity shall remain unchanged and no reallocation shall happen. Invalidates iterators and references at or after the point of the erase.
unordered complexitySection: 23.2.8 [unord.req] Status: Open Submitter: Pablo Halpern Opened: 2009-07-17 Last modified: 2020-09-06
Priority: 3
View other active issues in [unord.req].
View all other issues in [unord.req].
View all issues with Open status.
Discussion:
When I look at the unordered_* constructors, I think the complexity is poorly
described and does not follow the style of the rest of the standard.
The complexity for the default constructor is specified as constant.
Actually, it is proportional to n, but there are no invocations of
value_type constructors or other value_type operations.
For the iterator-based constructor the complexity should be:
Complexity: exactly
ncalls to constructvalue_typefromInputIterator::value_type(wheren = distance(f,l)). The number of calls tokey_equal::operator()is proportional tonin the average case andn*nin the worst case.
[ 2010 Rapperswil: ]
Concern that the current wording may require O(1) where that cannot be delivered. We need to look at both the clause 23 requirements tables and the constructor description of each unordered container to be sure.
Howard suggests NAD Editorial as we updated the container requirement tables since this issue was written.
Daniel offers to look deeper, and hopefully produce wording addressing any outstanding concerns at the next meeting.
Move to Open.
[2011-02-26: Daniel provides wording]
I strongly suggest to clean-up the differences between requirement tables and individual
specifications. In the usual way, the most specific specifications wins, which is in this
case the wrong one. In regard to the concern expressed about missing DefaultConstructible
requirements of the value type I disagree: The function argument n is no size-control
parameter, but only some effective capacity parameter: No elements will be value-initialized
by these constructors. The necessary requirement for the value type, EmplaceConstructible
into *this, is already listed in Table 103 — Unordered associative container requirements.
Another part of the proposed resolution is the fact that there is an inconsistency of the
complexity counting when both a range and a bucket count is involved compared
to constructions where only bucket counts are provided: E.g. the construction X a(n);
has a complexity of n bucket allocations, but this part of the work is omitted for
X a(i, j, n);, even though it is considerable larger (in the average case) for
n ≫ distance(i, j).
[2011-03-24 Madrid meeting]
Move to deferred
[ 2011 Bloomington ]
The proposed wording looks good. Move to Review.
[2012, Kona]
Fix up some presentation issues with the wording, combining the big-O expressions into single expressions rather than the sum of two separate big-Os.
Strike "constant or linear", prefer "linear in the number of buckets".
This allows for number of buckets being larger than requested n as well.
Default n to "unspecified" rather than "implementation-defined". It seems an un-necessary
burden asking vendors to document a quantity that is easily determined through the public API of
these classes.
Replace distance(f,l) with "number of elements in the range [f,l)"
Retain in Review with the updated wording
[2012, Portland: Move to Open]
The wording still does not call out Pablo's original concern, that the element constructor is called
no more than N times, and that the N squared term applies to moves during rehash.
Inconsistent use of O(n)+O(N) vs. O(n+N), with a preference for the former.
AJM to update wording with a reference to "no more than N element constructor calls".
Matt concerned that calling out the O(n) requirements is noise, and dangerous noise in suggesting a precision we do not mean. The cost of constructing a bucket is very different to constructing an element of user-supplied type.
AJM notes that if there are multiple rehashes, the 'n' complexity is probably not linear.
Matt suggests back to Open, Pablo suggests potentially NAD if we keep revisitting without achieving a resolution.
Matt suggests complexity we are concerned with is the number of operations, such as constructing elements, moving nodes, and comparing/hashing keys. We are less concerned with constructing buckets, which are generally noise in this bigger picture.
[2015-01-29 Telecon]
AM: essentially correct, but do we want to complicate the spec?
HH: Pablo has given us permission to NAD it JM: when I look at the first change in the P/R I find it mildly disturbing that the existing wording says you have a constant time constructor with a single element even if yourn is 10^6, so I think adding this change makes people
aware there might be a large cost in initializing the hash table, even though it doesn't show up in user-visible constructions.
HH: one way to avoid that problem is make the default ctor noexcept. Then the container isn't allowed to create
an arbitrarily large hash table
AM: but this is the constructor where the user provides n
MC: happy with the changes, except I agree with the editorial recommendation to keep the two 𝒪s separate.
JW: yes, the constant 'k' is different in 𝒪(n) and 𝒪(N)
GR: do we want to talk about buckets at all
JM: yes, good to highlight that bucket construction might be a significant cost
HH: suggest we take the suggestion to split 𝒪(n+N) to 𝒪(n)+𝒪(N) and move to Tentatively Ready
GR: 23.2.1p2 says all complexity requirements are stated solely in terms of the number of operations on the contained
object, so we shouldn't be stating complexity in terms of the hash table initialization
HH: channeling Pete, there's an implicit "unless otherwise specified" everywhere.
VV: seem to be requesting modifications that render this not Tentatively Ready
GR: I think it can't be T/R
AM: make the editorial recommendation, consider fixing 23.2.1/3 to give us permission to state complexity in terms
of bucket initialization
HH: only set it to Review after we get new wording to review
[2015-02 Cologne]
Update wording, revisit later.
Previous resolution [SUPERSEDED]:
Modify the following rows in Table 103 — Unordered associative container requirements to add the explicit bucket allocation overhead of some constructions. As editorial recommendation it is suggested not to shorten the sum
𝒪(n) + 𝒪(N)to𝒪(n + N), because two different work units are involved.
Table 103 — Unordered associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity … X(i, j, n, hf, eq)
X a(i, j, n, hf, eq)X…
Effects: Constructs an empty container with at leastn
buckets, usinghfas the hash function andeqas the key
equality predicate, and inserts elements from[i, j)into it.Average case 𝒪( n + N) (Nisdistance(i, j)),
worst case 𝒪(n) + 𝒪(N2)X(i, j, n, hf)
X a(i, j, n, hf)X…
Effects: Constructs an empty container with at leastn
buckets, usinghfas the hash function andkey_equal()as the key
equality predicate, and inserts elements from[i, j)into it.Average case 𝒪( n + N) (Nisdistance(i, j)),
worst case 𝒪(n + N2)X(i, j, n)
X a(i, j, n)X…
Effects: Constructs an empty container with at leastn
buckets, usinghasher()as the hash function andkey_equal()as the key
equality predicate, and inserts elements from[i, j)into it.Average case 𝒪( n + N) (Nisdistance(i, j)),
worst case 𝒪(n + N2)… Modify 23.5.3.2 [unord.map.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):
explicit unordered_map(size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());1 Effects: Constructs an empty
unordered_mapusing the specified hash function, key equality function, and allocator, and using at leastnbuckets. Ifnis not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in.unordered_mapmax_load_factor()returns1.0.2 Complexity:
ConstantLinear in the number of buckets.template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());3 Effects: Constructs an empty
unordered_mapusing the specified hash function, key equality function, and allocator, and using at leastnbuckets. Ifnis not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in. Then inserts elements from the rangeunordered_map[f, l).max_load_factor()returns1.0.4 Complexity:
Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear inNand in the worst case quadratic inNto insert the elements, whereNis equal to number of elements in the range[f,l).Modify 23.5.4.2 [unord.multimap.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):
explicit unordered_multimap(size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());1 Effects: Constructs an empty
unordered_multimapusing the specified hash function, key equality function, and allocator, and using at leastnbuckets. Ifnis not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in.unordered_multimapmax_load_factor()returns1.0.2 Complexity:
ConstantLinear in the number of buckets.template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());3 Effects: Constructs an empty
unordered_multimapusing the specified hash function, key equality function, and allocator, and using at leastnbuckets. Ifnis not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in. Then inserts elements from the rangeunordered_multimap[f, l).max_load_factor()returns1.0.4 Complexity:
Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear inNand in the worst case quadratic inNto insert the elements, whereNis equal to number of elements in the range[f,l).Modify 23.5.6.2 [unord.set.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):
explicit unordered_set(size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());1 Effects: Constructs an empty
unordered_setusing the specified hash function, key equality function, and allocator, and using at leastnbuckets. Ifnis not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in.unordered_setmax_load_factor()returns1.0.2 Complexity:
ConstantLinear in the number of buckets.template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());3 Effects: Constructs an empty
unordered_setusing the specified hash function, key equality function, and allocator, and using at leastnbuckets. Ifnis not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in. Then inserts elements from the rangeunordered_set[f, l).max_load_factor()returns1.0.4 Complexity:
Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear inNand in the worst case quadratic inNto insert the elements, whereNis equal to number of elements in the range[f,l).Modify 23.5.7.2 [unord.multiset.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):
explicit unordered_multiset(size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());1 Effects: Constructs an empty
unordered_multisetusing the specified hash function, key equality function, and allocator, and using at leastnbuckets. Ifnis not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in.unordered_multisetmax_load_factor()returns1.0.2 Complexity:
ConstantLinear in the number of buckets.template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());3 Effects: Constructs an empty
unordered_multisetusing the specified hash function, key equality function, and allocator, and using at leastnbuckets. Ifnis not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in. Then inserts elements from the rangeunordered_multiset[f, l).max_load_factor()returns1.0.4 Complexity:
Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear inNand in the worst case quadratic inNto insert the elements, whereNis equal to number of elements in the range[f,l).
[2019-03-17; Daniel comments and provides revised wording]
The updated wording ensures that we can now specify complexity requirements for containers even when they are not
expressed in terms of the number on the contained objects by an exception of the rule. This allows us to say that
𝒪(n) describes the complexity in terms of bucket initialization instead.
Proposed resolution:
This wording is relative to N4810.
Modify 23.2.2 [container.requirements.general] as indicated:
-2- Unless otherwise specified,
All of thecomplexity requirements in this Clause are stated solely in terms of the number of operations on the contained objects. [Example: The copy constructor of typevector<vector<int>>has linear complexity, even though the complexity of copying each containedvector<int>is itself linear. — end example]
Modify 23.2.8 [unord.req] as indicated:
-11- In Table 70:
(11.1) — […]
[…]
(11.23) — […]
(11.?) — Notwithstanding the complexity requirements restrictions of 23.2.2 [container.requirements.general], the complexity form
𝒪(n)describes the number of operations on buckets.
Modify the following rows in Table 70 — "Unordered associative container requirements" to add the explicit bucket allocation overhead of some constructions.
[Drafting note: It is kindly suggested to the Project Editor not to shorten the sum
𝒪(n) + 𝒪(N)to𝒪(n + N), because two different work units are involved. — end drafting note]
Table 70 — Unordered associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity … X()
X a;XExpects: […]
Effects: Constructs an empty container with an unspecified numbernof
buckets, usinghasher()as the hash function andkey_equal()as the key
equality predicate.constant𝒪(n)X(i, j, n, hf, eq)
X a(i, j, n, hf, eq)XExpects: […]
Effects: Constructs an empty container with at leastn
buckets, usinghfas the hash function andeqas the key
equality predicate, and inserts elements from[i, j)into it.Average case 𝒪( n) + 𝒪(N) (N
isdistance(i, j)), worst case
𝒪(n) + 𝒪(N2)X(i, j, n, hf)
X a(i, j, n, hf)XExpects: […]
Effects: Constructs an empty container with at leastn
buckets, usinghfas the hash function andkey_equal()as the key
equality predicate, and inserts elements from[i, j)into it.Average case 𝒪( n) + 𝒪(N) (N
isdistance(i, j)), worst case
𝒪(n) + 𝒪(N2)X(i, j, n)
X a(i, j, n)XExpects: […]
Effects: Constructs an empty container with at leastn
buckets, usinghasher()as the hash function andkey_equal()as the key
equality predicate, and inserts elements from[i, j)into it.Average case 𝒪( n) + 𝒪(N) (N
isdistance(i, j)), worst case
𝒪(n) + 𝒪(N2)X(i, j)
X a(i, j)XExpects: […]
Effects: Constructs an empty container with an unspecified numbernof
buckets, usinghasher()as the hash function andkey_equal()as the key
equality predicate, and inserts elements from[i, j)into it.Average case 𝒪( n) + 𝒪(N) (N
isdistance(i, j)), worst case
𝒪(n) + 𝒪(N2)…
Modify 23.5.3.1 [unord.map.overview], class template unordered_map, as indicated:
// 23.5.3.2 [unord.map.cnstr], construct/copy/destroy […] template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); […] unordered_map(initializer_list<value_type> il, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); […]
Modify 23.5.3.2 [unord.map.cnstr] as indicated:
unordered_map() : unordered_map(size_type(see belowunspecified)) { } explicit unordered_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());-1- Effects: Constructs an empty
-?- Ensures:unordered_mapusing the specified hash function, key equality predicate, and allocator, and using at leastnbuckets.For the default constructor, the number of buckets is implementation-defined.max_load_factor()returns1.0.max_load_factor() == 1.0-2- Complexity:ConstantLinear in the number of buckets.template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_map(initializer_list<value_type> il, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());-3- Effects: Constructs an empty
-?- Ensures:unordered_mapusing the specified hash function, key equality predicate, and allocator, and using at leastnbuckets.IfThen inserts elements from the rangenis not provided, the number of buckets is implementation-defined.[f, l)for the first form, or from the range[il.begin(), il.end())for the second form.max_load_factor()returns1.0.max_load_factor() == 1.0-4- Complexity:Average case linear, worst case quadraticLinear in the number of buckets, plus 𝒪(N) (average case) or 𝒪(N2) (worst case) whereNis the number of insertions.
Modify 23.5.4.1 [unord.multimap.overview], class template unordered_multimap, as indicated:
// 23.5.4.2 [unord.multimap.cnstr], construct/copy/destroy […] template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); […] unordered_multimap(initializer_list<value_type> il, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); […]
Modify 23.5.4.2 [unord.multimap.cnstr] as indicated:
unordered_multimap() : unordered_multimap(size_type(see belowunspecified)) { } explicit unordered_multimap(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());-1- Effects: Constructs an empty
-?- Ensures:unordered_multimapusing the specified hash function, key equality predicate, and allocator, and using at leastnbuckets.For the default constructor, the number of buckets is implementation-defined.max_load_factor()returns1.0.max_load_factor() == 1.0-2- Complexity:ConstantLinear in the number of buckets.template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_multimap(initializer_list<value_type> il, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());-3- Effects: Constructs an empty
-?- Ensures:unordered_multimapusing the specified hash function, key equality predicate, and allocator, and using at leastnbuckets.IfThen inserts elements from the rangenis not provided, the number of buckets is implementation-defined.[f, l)for the first form, or from the range[il.begin(), il.end())for the second form.max_load_factor()returns1.0.max_load_factor() == 1.0-4- Complexity:Average case linear, worst case quadraticLinear in the number of buckets, plus 𝒪(N) (average case) or 𝒪(N2) (worst case) whereNis the number of insertions.
Modify 23.5.6.1 [unord.set.overview], class template unordered_set, as indicated:
// 23.5.6.2 [unord.set.cnstr], construct/copy/destroy […] template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); […] unordered_set(initializer_list<value_type> il, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); […]
Modify 23.5.6.2 [unord.set.cnstr] as indicated:
unordered_set() : unordered_set(size_type(see belowunspecified)) { } explicit unordered_set(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());-1- Effects: Constructs an empty
-?- Ensures:unordered_setusing the specified hash function, key equality predicate, and allocator, and using at leastnbuckets.For the default constructor, the number of buckets is implementation-defined.max_load_factor()returns1.0.max_load_factor() == 1.0-2- Complexity:ConstantLinear in the number of buckets.template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_set(initializer_list<value_type> il, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());-3- Effects: Constructs an empty
-?- Ensures:unordered_setusing the specified hash function, key equality predicate, and allocator, and using at leastnbuckets.IfThen inserts elements from the rangenis not provided, the number of buckets is implementation-defined.[f, l)for the first form, or from the range[il.begin(), il.end())for the second form.max_load_factor()returns1.0.max_load_factor() == 1.0-4- Complexity:Average case linear, worst case quadraticLinear in the number of buckets, plus 𝒪(N) (average case) or 𝒪(N2) (worst case) whereNis the number of insertions.
Modify 23.5.6.1 [unord.set.overview], class template unordered_multiset, as indicated:
// 23.5.7.2 [unord.multiset.cnstr], construct/copy/destroy […] template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); […] unordered_multiset(initializer_list<value_type> il, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); […]
Modify 23.5.7.2 [unord.multiset.cnstr] as indicated:
unordered_multiset() : unordered_multiset(size_type(see belowunspecified)) { } explicit unordered_multiset(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());-1- Effects: Constructs an empty
-?- Ensures:unordered_multisetusing the specified hash function, key equality predicate, and allocator, and using at leastnbuckets.For the default constructor, the number of buckets is implementation-defined.max_load_factor()returns1.0.max_load_factor() == 1.0-2- Complexity:ConstantLinear in the number of buckets.template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); unordered_multiset(initializer_list<value_type> il, size_type n =see belowunspecified, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());-3- Effects: Constructs an empty
-?- Ensures:unordered_multisetusing the specified hash function, key equality predicate, and allocator, and using at leastnbuckets.IfThen inserts elements from the rangenis not provided, the number of buckets is implementation-defined.[f, l)for the first form, or from the range[il.begin(), il.end())for the second form.max_load_factor()returns1.0.max_load_factor() == 1.0-4- Complexity:Average case linear, worst case quadraticLinear in the number of buckets, plus 𝒪(N) (average case) or 𝒪(N2) (worst case) whereNis the number of insertions.
Section: 24.3 [iterator.requirements] Status: Open Submitter: Daniel Krügler Opened: 2009-09-19 Last modified: 2025-03-13
Priority: 4
View all other issues in [iterator.requirements].
View all issues with Open status.
Discussion:
The terms valid iterator and singular aren't properly defined. The fuzziness of those terms became even worse after the resolution of 208(i) (including further updates by 278(i)). In 24.3 [iterator.requirements] as of N2723 the standard says now:
5 - These values are called past-the-end values. Values of an iterator
ifor which the expression*iis defined are called dereferenceable. The library never assumes that past-the-end values are dereferenceable. Iterators can also have singular values that are not associated with any container. [...] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value and the assignment of a non-singular value to an iterator that holds a singular value. [...] Dereferenceable values are always non-singular.10 - An invalid iterator is an iterator that may be singular.
First, issue 208(i) intentionally removed the earlier constraint that past-the-end values are always non-singular. The reason for this was to support null pointers as past-the-end iterators of e.g. empty sequences. But there seem to exist different views on what a singular (iterator) value is. E.g. according to the SGI definition a null pointer is not a singular value:
Dereferenceable iterators are always nonsingular, but the converse is not true. For example, a null pointer is nonsingular (there are well defined operations involving null pointers) even thought it is not dereferenceable.
and proceeds:
An iterator is valid if it is dereferenceable or past-the-end.
Even if the standard prefers a different meaning of singular here, the change was incomplete, because by restricting feasible expressions of singular iterators to destruction and assignment isn't sufficient for a past-the-end iterator: Of-course it must still be equality-comparable and in general be a readable value.
Second, the standard doesn't clearly say whether a past-the-end value is a valid iterator or not. E.g. 26.11 [specialized.algorithms]/1 says:
In all of the following algorithms, the formal template parameter
ForwardIteratoris required to satisfy the requirements of a forward iterator (24.1.3) [..], and is required to have the property that no exceptions are thrown from [..], or dereference of valid iterators.
The standard should make better clear what "singular pointer" and "valid iterator" means. The fact that the meaning of a valid value has a core language meaning doesn't imply that for an iterator concept the term "valid iterator" has the same meaning.
Let me add a final example: In 99 [allocator.concepts.members] of N2914 we find:
pointer X::allocate(size_type n);11 Returns: a pointer to the allocated memory. [Note: if
n == 0, the return value is unspecified. —end note][..]
void X::deallocate(pointer p, size_type n);Preconditions:
pshall be a non-singular pointer value obtained from a call toallocate()on this allocator or one that compares equal to it.
If singular pointer value would include null pointers this make the
preconditions
unclear if the pointer value is a result of allocate(0): Since the return value
is unspecified, it could be a null pointer. Does that mean that programmers
need to check the pointer value for a null value before calling deallocate?
[ 2010-11-09 Daniel comments: ]
A later paper is in preparation.
[ 2010 Batavia: ]
Doesn't need to be resolved for Ox
[2014-02-20 Re-open Deferred issues as Priority 4]
Consider to await the paper.
Proposed resolution:
Section: 26 [algorithms] Status: Open Submitter: Alisdair Meredith Opened: 2009-10-15 Last modified: 2020-09-06
Priority: 3
View other active issues in [algorithms].
View all other issues in [algorithms].
View all issues with Open status.
Discussion:
The library has many algorithms that take a source range represented by a pair of iterators, and the start of some second sequence given by a single iterator. Internally, these algorithms will produce undefined behaviour if the second 'range' is not as large as the input range, but none of the algorithms spell this out in Requires clauses, and there is no catch-all wording to cover this in clause 17 or the front matter of 25.
There was an attempt to provide such wording in paper n2944 but this seems incidental to the focus of the paper, and getting the wording of this issue right seems substantially more difficult than the simple approach taken in that paper. Such wording will be removed from an updated paper, and hopefully tracked via the LWG issues list instead.
It seems there are several classes of problems here and finding wording to solve all in one paragraph could be too much. I suspect we need several overlapping requirements that should cover the desired range of behaviours.
Motivating examples:
A good initial example is the swap_ranges algorithm. Here there is a
clear requirement that first2 refers to the start of a valid range at
least as long as the range [first1, last1). n2944 tries to solve this
by positing a hypothetical last2 iterator that is implied by the
signature, and requires distance(first2,last2) < distance(first1,last1).
This mostly works, although I am uncomfortable assuming that last2 is
clearly defined and well known without any description of how to obtain
it (and I have no idea how to write that).
A second motivating example might be the copy algorithm. Specifically,
let us image a call like:
copy(istream_iterator<int>(is),istream_iterator(),ostream_iterator<int>(os));
In this case, our input iterators are literally simple InputIterators,
and the destination is a simple OutputIterator. In neither case am I
happy referring to std::distance, in fact it is not possible for the
ostream_iterator at all as it does not meet the requirements. However,
any wording we provide must cover both cases. Perhaps we might deduce
last2 == ostream_iterator<int>{}, but that might not always be valid for
user-defined iterator types. I can well imagine an 'infinite range'
that writes to /dev/null and has no meaningful last2.
The motivating example in n2944 is std::equal,
and that seems to fall somewhere between the two.
Outlying examples might be partition_copy that takes two output
iterators, and the _n algorithms where a range is specified by a
specific number of iterations, rather than traditional iterator pair.
We should also not accidentally apply inappropriate constraints to
std::rotate which takes a third iterator that is not intended to be a
separate range at all.
I suspect we want some wording similar to:
For algorithms that operate on ranges where the end iterator of the second range is not specified, the second range shall contain at least as many elements as the first.
I don't think this quite captures the intent yet though. I am not sure
if 'range' is the right term here rather than sequence. More awkwardly,
I am not convinced we can describe an Output sequence such as produce by
an ostream_iterator as "containing elements", at least not as a
precondition to the call before they have been written.
Another idea was to describe require that the trailing iterator support
at least distance(input range) applications of operator++ and may be
written through the same number of times if a mutable/output iterator.
We might also consider handling the case of an output range vs. an input range in separate paragraphs, if that simplifies how we describe some of these constraints.
[ 2009-11-03 Howard adds: ]
Moved to Tentatively NAD Future after 5 positive votes on c++std-lib.
[LEWG Kona 2017]
Recommend Open: The design is clear here; we just need wording
[2019-01-20 Reflector prioritization]
Set Priority to 3
Rationale:
Does not have sufficient support at this time. May wish to reconsider for a future standard.
Proposed resolution:
vector<bool> iterators are not random accessSection: 23.3.14 [vector.bool] Status: Open Submitter: BSI Opened: 2010-08-25 Last modified: 2020-09-06
Priority: 3
View other active issues in [vector.bool].
View all other issues in [vector.bool].
View all issues with Open status.
Discussion:
Addresses GB-118
vector<bool> iterators are not random access iterators
because their reference type is a special class, and not
bool &. All standard libary operations taking iterators
should treat this iterator as if it was a random access iterator, rather
than a simple input iterator.
[ Resolution proposed in ballot comment ]
Either revise the iterator requirements to support proxy iterators
(restoring functionality that was lost when the Concept facility was
removed) or add an extra paragraph to the vector<bool>
specification requiring the library to treat vector<bool>
iterators as-if they were random access iterators, despite having the wrong
reference type.
[ Rapperswil Review ]
The consensus at Rapperswil is that it is too late for full support for
proxy iterators, but requiring the library to respect vector<bool>
iterators as-if they were random access would be preferable to flagging
this container as deliberately incompatible with standard library algorithms.
Alisdair to write the note, which may become normative Remark depending on the preferences of the project editor.
[ Post-Rapperswil Alisdair provides wording ]
Initial wording is supplied, deliberately using Note in preference to
Remark although the author notes his preference for Remark. The
issue of whether iterator_traits<vector<bool>>::iterator_category
is permitted to report random_access_iterator_tag or must report
input_iterator_tag is not addressed.
[ Old Proposed Resolution: ]
Insert a new paragraph into 23.3.14 [vector.bool] between p4 and p5:
[Note All functions in the library that take a pair of iterators to denote a range shall treat
vector<bool>iterators as-if they were random access iterators, even though thereferencetype is not a true reference.-- end note]
[ 2010-11 Batavia: ]
Closed as NAD Future, because the current iterator categories cannot correctly describe
vector<bool>::iterator. But saying that they are Random Access Iterators is also incorrect, because it is not too hard to create a corresponding test that fails. We should deal with the more general proxy iterator problem in the future, and see no benefit to take a partial workaround specific tovector<bool>now.
[2017-02 in Kona, LEWG recommends NAD]
D0022 Proxy Iterators for the Ranges Extensions - as much a fix as we’re going to get for vector<bool>.
[2017-06-02 Issues Telecon]
P0022 is exploring a resolution. We consider this to be fairly important issue
Move to Open, set priority to 3
Proposed resolution:
Rationale:
No consensus to make this change at this time.
Section: 32.5.4 [atomics.order] Status: LEWG Submitter: Canada Opened: 2010-08-25 Last modified: 2025-10-21
Priority: 4
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with LEWG status.
Duplicate of: 1458
Discussion:
Addresses CA-21, GB-131
32.5.5 [atomics.lockfree] p.8 states:
An atomic store shall only store a value that has been computed from constants and program input values by a finite sequence of program evaluations, such that each evaluation observes the values of variables as computed by the last prior assignment in the sequence.
... but 6.10.1 [intro.execution] p.13 states:
If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced. [ Note: The execution of unsequenced evaluations can overlap. — end note ]
Overlapping executions can make it impossible to construct the sequence described in 32.5.5 [atomics.lockfree] p.8. We are not sure of the intention here and do not offer a suggestion for change, but note that 32.5.5 [atomics.lockfree] p.8 is the condition that prevents out-of-thin-air reads.
For an example, suppose we have a function invocation f(e1,e2). The evaluations of e1 and e2 can overlap. Suppose that the evaluation of e1 writes y and reads x whereas the evaluation of e2 reads y and writes x, with reads-from edges as below (all this is within a single thread).
e1 e2
Wrlx y-- --Wrlx x
rf\ /rf
X
/ \
Rrlx x<- ->Rrlx y
This seems like it should be allowed, but there seems to be no way to produce a sequence of evaluations with the property above.
In more detail, here the two evaluations, e1 and e2, are being executed as the arguments of a function and are consequently not sequenced-before each other. In practice we'd expect that they could overlap (as allowed by 6.10.1 [intro.execution] p.13), with the two writes taking effect before the two reads. However, if we have to construct a linear order of evaluations, as in 32.5.5 [atomics.lockfree] p.8, then the execution above is not permited. Is that really intended?
[ Resolution proposed by ballot comment ]
Please clarify.
[2011-03-09 Hans comments:]
I'm not proud of 32.5.4 [atomics.order] p9 (formerly p8), and I agree with the comments that this
isn't entirely satisfactory. 32.5.4 [atomics.order] p9 was designed to preclude
out-of-thin-air results for races among memory_order_relaxed atomics, in spite of
the fact that Java experience has shown we don't really know how to do that adequately. In
the long run, we probably want to revisit this.
6.10.1 [intro.execution] p15 states: "If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined." I think the examples presented here have undefined behavior as a result. It's not completely clear to me whether examples can be constructed that exhibit this problem, and don't have undefined behavior.
This comment seems to be using a different meaning of "evaluation"
from what is used elsewhere in the standard. The sequence of evaluations
here doesn't have to consist of full expression evaluations. They
can be evaluations of operations like lvalue to rvalue conversion,
or individual assignments. In particular, the reads and writes
executed by e1 and e2 in the example could be treated as separate
evaluations for purposes of producing the sequence.
The definition of "sequenced before" in 6.10.1 [intro.execution] makes
little sense if the term "evaluation" is restricted to any notion
of complete expression. Perhaps we should add yet another note
to clarify this? 32.5.4 [atomics.order] p10 probably leads to
the wrong impression here.
[2011-03-24 Madrid]
Moved to NAD Future
[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
Section: 32.4.3 [thread.thread.class] Status: LEWG Submitter: INCITS Opened: 2010-08-25 Last modified: 2025-10-21
Priority: 4
View all issues with LEWG status.
Discussion:
Addresses US-183
There is no way to join a thread with a timeout.
[ Resolution proposed by ballot comment: ]
Add
join_forandjoin_until. Or decide one should never join a thread with a timeout sincepthread_joindoesn't have a timeout version.
[ 2010 Batavia ]
The concurrency working group deemed this an extension beyond the scope of C++0x.
Rationale:
The LWG does not wish to make a change at this time.
[2017-03-01, Kona]
SG1 recommends: Close as NAD
There has not been much demand for it, and it would usually be difficult to deal withthread_local destructor races.
It can be approximated with a condition variable wait followed by an unconditional join. Adding it would create
implementation issues on Posix. As always, this may be revisited if we have a paper exploring the issues in detail.
[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
Section: 32.6 [thread.mutex] Status: LEWG Submitter: INCITS Opened: 2010-08-25 Last modified: 2025-10-21
Priority: 4
View all other issues in [thread.mutex].
View all issues with LEWG status.
Discussion:
Addresses US-185
Cooperate with WG14 to improve interoperability between
the C++0x and C1x threads APIs. In particular, C1x
mutexes should be conveniently usable with a C++0x
lock_guard. Performance overheads for this combination
should be considered.
[ Resolution proposed by ballot comment: ]
Remove
C++0xtimed_mutexandtimed_recursive_mutexif that facilitates development of more compatible APIs.
[ 2010 Batavia ]
The concurrency sub-group reviewed the options, and decided that closer harmony should wait until both standards are published.
Rationale:
The LWG does not wish to make any change at this time.
[2017-03-01, Kona]
SG1 recommends: Close as NAD
Papers about C compatibility are welcome, but there may be more pressing issues. C threads are not consistently available at this point, so there seems to be little demand to fix this particular problem.[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
mutex, recursive_mutex, is_locked functionSection: 32.6.4 [thread.mutex.requirements] Status: LEWG Submitter: INCITS Opened: 2010-08-25 Last modified: 2025-10-21
Priority: 4
View other active issues in [thread.mutex.requirements].
View all other issues in [thread.mutex.requirements].
View all issues with LEWG status.
Discussion:
Addresses US-189
mutex and recursive_mutex should have an is_locked()
member function. is_locked allows a user to test a lock
without acquiring it and can be used to implement a lightweight
try_try_lock.
[ Resolution proposed by ballot comment: ]
Add a member function:
bool is_locked() const;to
std::mutexandstd::recursive_mutex. These functions return true if the current thread would not be able to obtain a mutex. These functions do not synchronize with anything (and, thus, can avoid a memory fence).
[ 2010 Batavia ]
The Concurrency subgroup reviewed this issue and deemed it to be an extension to be handled after publishing C++0x.
Rationale:
The LWG does not wish to make a change at this time.
[2017-03-01, Kona]
SG1 recommends: Close as NAD
Several participants voiced strong objections, based on either memory model issues or lock elision. No support. It is already possible to write a wrapper that explicitly tracks ownership for testing in the owning thread, which may have been part of the intent here.[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
Section: 23.2.2 [container.requirements.general] Status: Open Submitter: Mike Spertus Opened: 2010-10-16 Last modified: 2019-01-20
Priority: 3
View other active issues in [container.requirements.general].
View all other issues in [container.requirements.general].
View all issues with Open status.
Discussion:
Addresses US-104, US-141
The standard doesn't say that containers should use abstract pointer types internally. Both Howard and Pablo agree that this is the intent. Further, it is necessary for containers to be stored, for example, in shared memory with an interprocess allocator (the type of scenario that allocators are intended to support).
In spite of the (possible) agreement on intent, it is necessary to make this explicit:
An implementations may like to store the result of dereferencing the pointer (which is a raw reference) as an optimization, but that prevents the data structure from being put in shared memory, etc. In fact, a container could store raw references to the allocator, which would be a little weird but conforming as long as it has one by-value copy. Furthermore, pointers to locales, ctypes, etc. may be there, which also prevents the data structure from being put in shared memory, so we should make explicit that a container does not store raw pointers or references at all.
[ Pre-batavia ]
This issue is being opened as part of the response to NB comments US-104/141. See paper N3171 in the pre-Batavia mailing.
[2011-03-23 Madrid meeting]
Deferred
[ 2011 Batavia ]
This may be an issue, but it is not clear. We want to gain a few years experience with the C++11 allocator model to see if this is already implied by the existing specification.
[LEWG Kona 2017]
Status to Open: Acknowledged, need wording: (N4618 numbering) 23.2.1 container.requirements.general p8 first sentence. Replace non-normative note with requirement.
See discussion on LEWG Wiki
[2019-01-20 Reflector prioritization]
Set Priority to 3
Proposed resolution:
Add to the end of 23.2.2 [container.requirements.general] p. 8:
[..] In all container types defined in this Clause, the member
get_allocator()returns a copy of the allocator used to construct the container or, if that allocator has been replaced, a copy of the most recent replacement. The container may not store internal objects whose types are of the formT *orT &except insofar as they are part of the item type or members.
Section: 24.3.5.4 [output.iterators] Status: Open Submitter: Daniel Krügler Opened: 2011-02-27 Last modified: 2025-03-13
Priority: 3
View other active issues in [output.iterators].
View all other issues in [output.iterators].
View all issues with Open status.
Discussion:
During the Pittsburgh meeting the proposal N3066 became accepted because it fixed several severe issues related to the iterator specification. But the current working draft (N3225) does not reflect all these changes. Since I'm unaware whether every correction can be done editorial, this issue is submitted to take care of that. To give one example: All expressions of Table 108 — "Output iterator requirements" have a post-condition that the iterator is incrementable. This is impossible, because it would exclude any finite sequence that is accessed by an output iterator, such as a pointer to a C array. The N3066 wording changes did not have these effects.
[2011-03-01: Daniel comments:]
This issue has some overlap with the issue 2038(i) and I would prefer if we could solve both at one location. I suggest the following approach:
The terms dereferencable and incrementable could be defined in a more
general way not restricted to iterators (similar to the concepts HasDereference and
HasPreincrement from working draft N2914). But on the other hand, all current usages of
dereferencable and incrementable are involved with types that satisfy
iterator requirements. Thus, I believe that it is sufficient for C++0x to add corresponding definitions to
24.3.1 [iterator.requirements.general] and to let all previous usages of these terms refer to this
sub-clause. Since the same problem occurs with the past-the-end iterator, this proposal suggest providing
similar references to usages that precede its definition as well.
We also need to ensure that all iterator expressions get either an operational semantics in terms of others or we need to add missing pre- and post-conditions. E.g. we have the following ones without semantics:
*r++ = o // output iterator *r-- // bidirectional iterator
According to the SGI specification these correspond to
{ *r = o; ++r; } // output iterator
{ reference tmp = *r; --r; return tmp; } // bidirectional iterator
respectively. Please note especially the latter expression for bidirectional iterator. It fixes a problem
that we have for forward iterator as well: Both these iterator categories provide stronger guarantees
than input iterator, because the result of the dereference operation is reference, and not
only convertible to the value type (The exact form from the SGI documentation does not correctly refer to
reference).
[2011-03-14: Daniel comments and updates the suggested wording]
In addition to the before mentioned necessary changes there is another one need, which
became obvious due to issue 2042(i): forward_list<>::before_begin() returns
an iterator value which is not dereferencable, but obviously the intention is that it should
be incrementable. This leads to the conclusion that imposing dereferencable as a requirement
for the expressions ++r is wrong: We only need the iterator to be incrementable. A
similar conclusion applies to the expression --r of bidirectional iterators.
[ 2011 Bloomington ]
Consensus this is the correct direction, but there are (potentially) missing incrementable preconditions on some table rows, and the Remarks on when an output iterator becomes dereferencable are probably better handled outside the table, in a manner similar to the way we word for input iterators.
There was some concern about redundant pre-conditions when the operational semantic is defined in terms of operations that have preconditions, and a similar level of concern over dropping such redundancies vs. applying a consistent level of redundant specification in all the iterator tables. Wording clean-up in either direction would be welcome.
[2011-08-18: Daniel adapts the proposed resolution to honor the Bloomington request]
There is only a small number of further changes suggested to get rid of superfluous
requirements and essentially non-normative assertions. Operations should not have extra
pre-conditions, if defined by "in-terms-of" semantics, see e.g. a != b or a->m
for Table 107. Further, some remarks, that do not impose anything or say nothing new have been removed,
because I could not find anything helpful they provide.
E.g. consider the remarks for Table 108 for the operations dereference-assignment and
preincrement: They don't provide additional information say nothing surprising. With the
new pre-conditions and post-conditions it is implied what the remarks intend to say.
[ 2011-11-03: Some observations from Alexander Stepanov via c++std-lib-31405 ]
The following sentence is dropped from the standard section on OutputIterators:
"In particular, the following two conditions should hold: first, any iterator value should be assigned through before it is incremented (this is, for an output iteratori, i++; i++; is not a valid code
sequence); second, any value of an output iterator may have at most
one active copy at any given time (for example, i = j; *++i = a; *j = b;
is not a valid code sequence)."
[ 2011-11-04: Daniel comments and improves the wording ]
In regard to the first part of the comment, the intention of the newly proposed wording was to make clear that for the expression
*r = o
we have the precondition dereferenceable and the post-condition incrementable. And for the expression
++r
we have the precondition incrementable and the post-condition dereferenceable
or past-the-end. This should not allow for a sequence like i++; i++;
but I agree that it doesn't exactly say that.
++r:
"Post: any copies of the previous value of r are no longer
required to be dereferenceable or incrementable."
The proposed has been updated to honor the observations of Alexander Stepanov.
[2015-02 Cologne]
The matter is complicated, Daniel volunteers to write a paper.
Proposed resolution:
Add a reference to 24.3.1 [iterator.requirements.general] to the following parts of the library preceding Clause 24 Iterators library: (I stopped from 23.2.8 [unord.req] on, because the remaining references are the concrete containers)
16.4.4.3 [swappable.requirements] p5:
-5- A type
Xsatisfying any of the iterator requirements (24.2) isValueSwappableif, for any dereferenceable (24.3.1 [iterator.requirements.general]) objectxof typeX,*xis swappable.
16.4.4.6 [allocator.requirements], Table 27 — "Descriptive variable definitions",
row with the expression c:
a dereferenceable (24.3.1 [iterator.requirements.general]) pointer of type
C*
20.2.3.3 [pointer.traits.functions]:
Returns: The first template function returns a dereferenceable (24.3.1 [iterator.requirements.general]) pointer to
robtained by callingPtr::pointer_to(r); […]
27.4.3.4 [string.iterators] p. 2:
Returns: An iterator which is the past-the-end value (24.3.1 [iterator.requirements.general]).
28.3.4.6.2.3 [locale.time.get.virtuals] p. 11:
iter_type do_get(iter_type s, iter_type end, ios_base& f, ios_base::iostate& err, tm *t, char format, char modifier) const;Requires:
tshall be dereferenceable (24.3.1 [iterator.requirements.general]).
23.2.2 [container.requirements.general] p. 6:
[…]
end()returns an iterator which is the past-the-end (24.3.1 [iterator.requirements.general]) value for the container. […]
23.2.4 [sequence.reqmts] p. 3:
[…]
qdenotes a valid dereferenceable (24.3.1 [iterator.requirements.general]) const iterator toa, […]
23.2.7 [associative.reqmts] p. 8 (I omit intentionally one further reference in the same sub-clause):
[…]
qdenotes a valid dereferenceable (24.3.1 [iterator.requirements.general]) const iterator toa, […]
23.2.8 [unord.req] p. 10 (I omit intentionally one further reference in the same sub-clause):
[…]
qandq1are valid dereferenceable (24.3.1 [iterator.requirements.general]) const iterators toa, […]
Edit 24.3.1 [iterator.requirements.general] p. 5 as indicated (The intent is to properly define incrementable and to ensure some further library guarantee related to past-the-end iterator values):
-5- Just as a regular pointer to an array guarantees that there is a pointer value pointing past the last element of the array, so for any iterator type there is an iterator value that points past the last element of a corresponding sequence. These values are called past-the-end values. Values of an iterator
ifor which the expression*iis defined are called dereferenceable. Values of an iteratorifor which the expression++iis defined are called incrementable. The library never assumes that past-the-end values are dereferenceable or incrementable. Iterators can also have singular values that are not associated with any sequence. […]
Modify the column contents of Table 106 — "Iterator requirements", 24.3.5.2 [iterator.iterators], as indicated:
Table 106 — Iterator requirements Expression Return type Operational semantics Assertion/note
pre-/post-condition*rreferencepre: ris dereferenceable.++rX&pre: ris incrementable.
Modify the column contents of Table 107 — "Input iterator requirements",
24.3.5.3 [input.iterators], as indicated [Rationale: The wording changes attempt
to define a minimal "independent" set of operations, namely *a and ++r, and
to specify the semantics of the remaining ones. This approach seems to be in agreement with the
original SGI specification
— end rationale]:
Table 107 — Input iterator requirements (in addition to Iterator) Expression Return type Operational semantics Assertion/note
pre-/post-conditiona != bcontextually
convertible tobool!(a == b)pre:(a, b)is in the domain
of==.*aconvertible to Tpre: ais dereferenceable.
The expression
(void)*a, *ais equivalent
to*a.
Ifa == band(a,b)is in
the domain of==then*ais
equivalent to*b.a->m(*a).mpre:ais dereferenceable.++rX&pre: risdereferenceableincrementable.
post:ris dereferenceable or
ris past-the-end.
post: any copies of the
previous value ofrare no
longer required either to be
dereferenceable, incrementable,
or to be in the domain of==.(void)r++(void)++requivalent to(void)++r*r++convertible to T{ T tmp = *r;
++r;
return tmp; }
Modify the column contents of Table 108 — "Output iterator requirements",
24.3.5.4 [output.iterators], as indicated [Rationale: The wording changes attempt
to define a minimal "independent" set of operations, namely *r = o and ++r,
and to specify the semantics of the remaining ones. This approach seems to be in agreement with
the original SGI specification
— end rationale]:
Table 108 — Output iterator requirements (in addition to Iterator) Expression Return type Operational semantics Assertion/note
pre-/post-condition*r = oresult is not used pre: ris dereferenceable.
Remark: After this operation
ris not required to be
dereferenceable and any copies of
the previous value ofrare no
longer required to be dereferenceable
or incrementable.
post:ris incrementable.++rX&pre: ris incrementable.
&r == &++r.
Remark: After this operationRemark: After this operation
ris not required to be
dereferenceable.
ris not required to be
incrementable and any copies of
the previous value ofrare no
longer required to be dereferenceable
or incrementable.
post:ris dereferenceable
orris past-the-endincrementable.
r++convertible to const X&{ X tmp = r;
++r;
return tmp; }Remark: After this operation
ris not required to be
dereferenceable.
post:ris incrementable.*r++ = oresult is not used { *r = o; ++r; }Remark: After this operation
ris not required to be
dereferenceable.
post:ris incrementable.
Modify the column contents of Table 109 — "Forward iterator requirements",
24.3.5.5 [forward.iterators], as indicated [Rationale: Since the return type of the
expression *r++ is now guaranteed to be type reference, the implied operational
semantics from input iterator based on value copies is wrong — end rationale]
Table 109 — Forward iterator requirements (in addition to input iterator) Expression Return type Operational semantics Assertion/note
pre-/post-conditionr++convertible to const X&{ X tmp = r;
++r;
return tmp; }*r++reference { reference tmp = *r;
++r;
return tmp; }
Modify the column contents of Table 110 — "Bidirectional iterator requirements", 24.3.5.6 [bidirectional.iterators], as indicated:
Table 110 — Bidirectional iterator requirements (in addition to forward iterator) Expression Return type Operational semantics Assertion/note
pre-/post-condition--rX&pre: there exists ssuch that
r == ++s.
post:risdereferenceableincrementable.
--(++r) == r.
--r == --simpliesr == s.
&r == &--r.r--convertible to const X&{ X tmp = r;
--r;
return tmp; }*r--reference { reference tmp = *r;
--r;
return tmp; }
incrementable iteratorSection: 24.3.5.4 [output.iterators] Status: Open Submitter: Pete Becker Opened: 2011-02-27 Last modified: 2016-01-28
Priority: 3
View other active issues in [output.iterators].
View all other issues in [output.iterators].
View all issues with Open status.
Discussion:
In comp.lang.c++, Vicente Botet raises the following questions:
"In "24.2.4 Output iterators" there are 3 uses of incrementable. I've not found the definition. Could some one point me where it is defined?
Something similar occurs with dereferenceable. While the definition is given in "24.2.1 In general" it is used several times before. Shouldn't these definitions be moved to some previous section?"
He's right: both terms are used without being properly defined.
There is no definition of "incrementable". While there is a definition of "dereferenceable", it is, in fact, a definition of "dereferenceable iterator". "dereferenceable" is used throughout Clause 23 (Containers) before its definition in Clause 24. In almost all cases it's referring to iterators, but in 16.4.4.3 [swappable.requirements] there is a mention of "dereferenceable object"; in 16.4.4.6 [allocator.requirements] the table of Descriptive variable definitions refers to a "dereferenceable pointer"; 20.2.3.3 [pointer.traits.functions] refers to a "dereferenceable pointer"; in 28.3.4.6.2.3 [locale.time.get.virtuals]/11 (do_get)
there is a requirement that a pointer "shall be dereferenceable". In those specific cases
it is not defined.
[2011-03-02: Daniel comments:]
I believe that the currently proposed resolution of issue 2035(i) solves this issue as well.
[ 2011 Bloomington ]
Agree with Daniel, this will be handled by the resolution of 2035(i).
Proposed resolution:
Section: 21.3.6.4 [meta.unary.prop] Status: Open Submitter: Daniel Krügler Opened: 2011-08-20 Last modified: 2016-01-28
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with Open status.
Discussion:
The currently agreed on proposed wording for 2015(i) using
remove_all_extents<T>::type instead of the "an array of
unknown bound" terminology in the precondition should be extended to
some further entries especially in Table 49, notably the
is_*constructible, is_*assignable, and
is_*destructible entries. To prevent ODR violations, incomplete
element types of arrays must be excluded for value-initialization and
destruction for example. Construction and assignment has to be honored,
when we have array-to-pointer conversions or pointer conversions of
incomplete pointees in effect.
[2012, Kona]
The issue is that in three type traits, we are accidentally saying that in certain circumstances the type must give a specified answer when given an incomplete type. (Specifically: an array of unknown bound of incomplete type.) The issue asserts that there's an ODR violation, since the trait returns false in that case but might return a different version when the trait is completed.
Howard argues: no, there is no risk of an ODR violation.
is_constructible<A[]> must return false regardless of whether
A is complete, so there's no reason to forbid an array of unknown bound of
incomplete types. Same argument applies to is_assignable. General agreement
with Howard's reasoning.
There may be a real issue for is_destructible. None of us are sure what
is_destructible is supposed to mean for an array of unknown bound
(regardless of whether its type is complete), and the standard doesn't make it clear.
The middle column doesn't say what it's supposed to do for incomplete types.
In at least one implementation, is_destructible<A[]> does return true
if A is complete, which would result in ODR violation unless we forbid it for
incomplete types.
Move to open. We believe there is no issue for is_constructible or
is_assignable, but that there is a real issue for is_destructible.
Proposed resolution:
valarray assignments with mask_array index?Section: 29.6.8 [template.mask.array] Status: Open Submitter: Thomas Plum Opened: 2011-12-10 Last modified: 2016-01-28
Priority: 4
View all issues with Open status.
Discussion:
Recently I received a Service Request (SR) alleging that one of our testcases causes an
undefined behavior. The complaint is that 29.6.8 [template.mask.array] in C++11
(and the corresponding subclause in C++03) are interpreted by some people to require that
in an assignment "a[mask] = b", the subscript mask and the rhs b
must have the same number of elements.
"the expression
a[mask] = b;"
but the semicolon cannot be part of an expression. The correction could omit the semicolon, or change the word "expression" to "assignment" or "statement".
Here is the text of the SR, slightly modified for publication:Subject: SR01174 LVS _26322Y31 has undefined behavior [open]
[Client:]
The test case t263.dir/_26322Y31.cpp seems to be illegal as it has an undefined behaviour. I searched into the SRs but found SRs were not related to the topic explained in this mail (SR00324, SR00595, SR00838).const char vl[] = {"abcdefghijklmnopqrstuvwxyz"}; const char vu[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"}; const std::valarray<char> v0(vl, 27), vm5(vu, 5), vm6(vu, 6); std::valarray<char> x = v0; […] const bool vb[] = {false, false, true, true, false, true}; const std::valarray<bool> vmask(vb, 6); x = v0; x[vmask] = vm5; // ***** HERE.... steq(&x[0], "abABeCghijklmnopqrstuvwxyz"); x2 = x[vmask]; // ***** ....AND HERE […]This problem has already been discussed between [experts]: See thread http://gcc.gnu.org/ml/libstdc++/2009-11/threads.html#00051 Conclusion http://gcc.gnu.org/ml/libstdc++/2009-11/msg00099.html
[Plum Hall:]
Before I log this as an SR, I need to check one detail with you. I did read the email thread you mentioned, and I did find a citation (see INCITS ISO/IEC 14882-2003 Section 26.3.2.6 on valarray computed assignments): Quote: "If the array and the argument array do not have the same length, the behavior is undefined", But this applies to computed assignment (*=,+=, etc), not to simple assignment. Here is the C++03 citation re simple assignment: 26.3.2.2 valarray assignment [lib.valarray.assign]valarray<T>& operator=(const valarray<T>&);1 Each element of the
*thisarray is assigned the value of the corresponding element of the argument array. The resulting behavior is undefined if the length of the argument array is not equal to the length of the*thisarray.In the new C++11 (N3291), we find ...
26.6.2.3 valarray assignment [valarray.assign]valarray<T>& operator=(const valarray<T>& v);1 Each element of the
*thisarray is assigned the value of the corresponding element of the argument array. If the length ofvis not equal to the length of*this, resizes*thisto make the two arrays the same length, as if by callingresize(v.size()), before performing the assignment.So it looks like the testcase might be valid for C++11 but not for C++03; what do you think?
[Client:]
I quite agree with you but the two problems I mentioned:x[vmask] = vm5; // ***** HERE.... […] x2 = x[vmask]; // ***** ....AND HERErefer to
[Plum Hall:]mask_arrayassignment hence target the C++03 26.3.8 paragraph. Correct?
I mentioned the contrast between C++03 26.3.2.2 para 1 versus C++11 26.6.2.3 para 1. But in C++03 26.3.8, I don't find any corresponding restriction. Could you quote the specific requirement you're writing about? [Client:]
I do notice the difference between c++03 26.3.2.2 and c++11 26.6.2.3 about assignments between different sizedvalarrayand I perfectly agree with you. But, as already stated, this is not a simplevalarrayassignment but amask_arrayassignment (c++03 26.3.8 / c++11 26.6.8). See c++11 quote below: 26.6.8 Class template mask_array
26.6.8.1 Class template mask_array overview
[....]
This template is a helper template used by the mask subscript operator:
mask_array<T> valarray<T>::operator[](const valarray<bool>&).It has reference semantics to a subset of an array specified by a boolean mask. Thus, the expression
a[mask] = b;has the effect of assigning the elements ofbto the masked elements ina(those for which the corresponding element inmaskis true.)26.6.8.2 mask_array assignment
void operator=(const valarray<T>&) const; const mask_array& operator=(const mask_array&) const;1 These assignment operators have reference semantics, assigning the values of the argument array elements to selected elements of the
valarray<T>object to which it refers.In particular, [one of the WG21 experts] insisted on the piece "the elements of
That is why I reported the test t263.dir/_26322Y31.cpp having an undefined behaviour. [Plum Hall:]b".
OK, I can see that I will have to ask WG21; I will file an appropriate issue with the Library subgroup. In the meantime, I will mark this testcase as "DISPUTED" so that it is not required for conformance testing, until we get a definitive opinion.
[2012, Kona]
Moved to Open.
There appears to be a real need for clarification in the standard, and implementations differ in their current interpretation. This will need some research by implementers and a proposed resolution before further discussion is likely to be fruitful.
Proposed resolution:
is_nothrow_constructible and destructorsSection: 21.3.6.4 [meta.unary.prop] Status: Open Submitter: Dave Abrahams Opened: 2011-12-09 Last modified: 2023-05-25
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with Open status.
Discussion:
IMO if we specified is_[nothrow_]constructible in terms of a variable
declaration whose validity requires destructibility, it is clearly a bug
in our specification and a failure to realize the actual original
intent. The specification should have been in terms of placement-new.
is_constructible.
The design of is_constructible was also impacted by the previous
Constructible concept that explicitly contained destruction semantics,
because during conceptification of the library it turned out to simplify
the constraints in the library because you did not need to add
Destructible all the time. It often was implied but never spoken out
in C++03.
Pure construction semantics was considered as useful as well, so HasConstructor
did also exist and would surely be useful as trait as well.
Another example that is often overlooked: This also affects wrapper types like pair,
tuple, array that contain potentially more than one type:
This is easy to understand if you think of T1 having a deleted destructor
and T2 having a constructor that may throw: Obviously the compiler has
potentially need to use the destructor of T1 in the constructor
of std::pair<T1, T2> to ensure that the core language requirements
are satisfied (All previous fully constructed sub-objects must be destructed).
The core language also honors this fact in [class.copy] p11:
A defaulted copy/move constructor for a class
Xis defined as deleted (9.6.3 [dcl.fct.def.delete]) ifXhas:
[…]
— any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
[…]
Dave:
This is about is_nothrow_constructible in particular. The fact that it is
foiled by not having a noexcept dtor is a defect.
[2012, Kona]
Move to Open.
is_nothrow_constructible is defined in terms of is_constructible, which is defined
by looking at a hypothetical variable and asking whether the variable definition is known not to
throw exceptions. The issue claims that this also examines the type's destructor, given the context,
and thus will return false if the destructor can potentially throw. At least one
implementation (Howard's) does return false if the constructor is noexcept(true)
and the destructor is noexcept(false). So that's not a strained interpretation.
The issue is asking for this to be defined in terms of placement new, instead of in terms
of a temporary object, to make it clearer that is_nothrow_constructible looks at the
noexcept status of only the constructor, and not the destructor.
Sketch of what the wording would look like:
require is_constructible, and then also require that a placement new operation
does not throw. (Remembering the title of this issue... What does this imply for swap?
If we accept this resolution, do we need any changes to swap?
STL argues: no, because you are already forbidden from passing anything with a throwing
destructor to swap.
Dietmar argues: no, not true. Maybe statically the destructor can conceivably throw for some
values, but maybe there are some values known not to throw. In that case, it's correct to
pass those values to swap.
[2017-01-27 Telecon]
Gave the issue a better title
This issue interacts with 2827(i)
Ville would like "an evolution group" to take a look at this issue.
[2020-08; LWG reflector]
A poll was taken to close the issue as NAD, but only gained three votes in favour (and one vote against, which was subsequently withdrawn).
[2022-03; LWG reflector]
A poll was taken to close the issue as NAD, with six votes in favour. (and one vote against, subsequently withdrawn).
"Write a paper if you want something else. These traits have well established meaning now." "Minimizing requirements is not as important a concern for standard library concepts as as minimizing the number of concepts. Requirements like 'I need to construct but not destroy an object' are niche enough that we don't need to support them."
[2022-11-30; LWG telecon]
Alisdair intends to write a paper for this.
[2023-05-25; May 2023 mailing]
Alisdair provided P2842R0.
Proposed resolution:
ios_base manipulators should have showgrouping/noshowgroupingSection: 28.3.4.3.3.3 [facet.num.put.virtuals], 31.5.2.2.2 [ios.fmtflags], 31.5.5.1 [fmtflags.manip] Status: Open Submitter: Benjamin Kosnik Opened: 2011-12-15 Last modified: 2023-02-07
Priority: 3
View other active issues in [facet.num.put.virtuals].
View all other issues in [facet.num.put.virtuals].
View all issues with Open status.
Discussion:
Iostreams should include a manipulator to toggle grouping on/off for locales that support grouped digits. This has come up repeatedly and been deferred. See LWG 826(i) for the previous attempt.
If one is using a locale that supports grouped digits, then output will always include the generated grouping characters. However, very plausible scenarios exist where one might want to output the number, un-grouped. This is similar to existing manipulators that toggle on/off the decimal point, numeric base, or positive sign. See some user commentary here.[21012, Kona]
Move to Open.
This is a feature request.
Walter is slightly uncomfortable with processing feature requests through the issues lists.
Alisdair says this is far from the first feature request that has come in from the issues list.
STL: The fact that you can turn off grouping on hex output is compelling.
Marshall: if we add this flag, we'll need to update tables 87-91 as well.
STL: If it has been implemented somewhere, and it works, we'd be glad to add it.
Howard: We need to say what the default is.
Alisdair sumarizes:
(1) We want clear wording that says what the effect is of turning the flag off;
(2) what the default values are, and
(3) how this fits into tables 87-90. (and 128)
[Issaquah 2014-02-10-12: Move to LEWG]
Since this issue was filed, we have grown a new working group that is better placed to handle feature requests.
We will track such issues with an LEWG status until we get feedback from the Library Evolution Working Group.
[Issaquah 2014-02-12: LEWG discussion]
| SF | F | N | A | SA |
| 2 | 4 | 1 | 0 | 0 |
Think about the ABI break for adding a flag. But this could be mitigated by putting the data into an iword instead of a flag.
This needs to change Stage 2 in [facet.num.put.virtuals].
Previous resolution, which needs the above corrections:
This wording is relative to the FDIS.
Insert in 28.3.4.3.3.3 [facet.num.put.virtuals] paragraph 5:
Stage 1: The first action of stage 1 is to determine a conversion specifier. The tables that describe this determination use the following local variables
fmtflags flags = str.flags() ; fmtflags basefield = (flags & (ios_base::basefield)); fmtflags uppercase = (flags & (ios_base::uppercase)); fmtflags floatfield = (flags & (ios_base::floatfield)); fmtflags showpos = (flags & (ios_base::showpos)); fmtflags showbase = (flags & (ios_base::showbase)); fmtflags showgrouping = (flags & (ios_base::showgrouping));Change header
<ios>synopsis, [iostreams.base.overview] as indicated:#include <iosfwd> namespace std { […] // 27.5.6, manipulators: […] ios_base& showpoint (ios_base& str); ios_base& noshowpoint (ios_base& str); ios_base& showgrouping (ios_base& str); ios_base& noshowgrouping(ios_base& str); ios_base& showpos (ios_base& str); ios_base& noshowpos (ios_base& str); […] }Change class
ios_basesynopsis, 31.5.2 [ios.base] as indicated:namespace std { class ios_base { public: class failure; // 27.5.3.1.2 fmtflags typedef T1 fmtflags; […] static constexpr fmtflags showpoint = unspecified ; static constexpr fmtflags showgrouping = unspecified ; static constexpr fmtflags showpos = unspecified ; […] }; }Add a new entry to Table 122 — "
fmtflagseffects" as indicated:
Table 122 — fmtflagseffectsElement Effect(s) if set […]showpointgenerates a decimal-point character unconditionally in generated floatingpoint output showgroupinggenerates grouping characters unconditionally in generated output […]After [ios::fmtflags] p12 insert the following:
ios_base& showgrouping(ios_base& str);-?- Effects: Calls
-?- Returns:str.setf(ios_base::showgrouping).str.ios_base& noshowgrouping(ios_base& str);-?- Effects: Calls
-?- Returns:str.unsetf(ios_base::showgrouping).str.
Proposed resolution:
Section: 16.3.2 [structure] Status: Open Submitter: Jens Maurer Opened: 2012-03-08 Last modified: 2024-10-05
Priority: 3
View all issues with Open status.
Discussion:
The front matter in clause 17 should clarify that postconditions will not hold if a standard library function exits via an exception. Postconditions or guarantees that apply when an exception is thrown (beyond the basic guarantee) are described in an "Exception safety" section.
[ 2012-10 Portland: Move to Open ]
Consensus that we do not clearly say this, and that we probably should. A likely location to describe the guarantees of postconditions could well be a new sub-clause following 99 [res.on.required] which serves the same purpose for requires clauses. However, we need such wording before we can make progress.
Also, see 2137(i) for a suggestion that we want to see a paper resolving both issues together.
[2015-05-06 Lenexa: EricWF to write paper addressing 2136 and 2137]
MC: Idea is to replace all such "If no exception" postconditions with "Exception safety" sections.
[2021-06-20; Daniel comments]
An informal editorial change suggestion has recently been made whose editorial implementation would promote the idea that the default assumption is that Postconditions: are only met if the function doesn't exit with an exception.
After analyzing all current existing Postconditions: elements the following seems to hold: Affected by this issue are only non-noexcept functions and mostly non-constructor functions (unless the
Postconditions: element says something about the value of its arguments). Most existing
Postconditions seem to be intended to apply only in non-exceptional cases. I found some where
this is presumably not intended, namely those of the expressions os << x and
is >> v in Tables [tab:rand.req.eng] and [tab:rand.req.dist], maybe also
30.11.2.4 [time.zone.db.remote] p4.
Nonetheless, the editorial change seems to be applicable even without having this issue resolved, because
it doesn't actually change the normative state by itself.
[2024-10-03; Jonathan adds wording]
Proposed resolution:
This wording is relative to N4988.
Change 16.3.2.4 [structure.specifications] as indicated:
(3.6) — Postconditions: the conditions (sometimes termed observable results) established by the function when a call to it returns normally.
Section: 28.6.7.3 [re.regex.assign] Status: Open Submitter: Jonathan Wakely Opened: 2012-03-08 Last modified: 2024-10-03
Priority: 3
View all other issues in [re.regex.assign].
View all issues with Open status.
Discussion:
The post-conditions of basic_regex<>::assign 28.6.7.3 [re.regex.assign] p16 say:
If no exception is thrown,
flags()returnsfandmark_count()returns the number of marked sub-expressions within the expression.
The default expectation in the library is that post-conditions only hold, if there is no failure (see also 2136(i)), therefore the initial condition should be removed to prevent any misunderstanding.
[ 2012-10 Portland: Move to Open ]
A favorable resolution clearly depends on a favorable resolution to 2136(i). There is also a concern that this is just one example of where we would want to apply such a wording clean-up, and which is really needed to resolve both this issue and 2136(i) is a paper providing the clause 17 wording that gives the guarantee for postcondition paragraphs, and then reviews clauses 18-30 to apply that guarantee consistently. We do not want to pick up these issues piecemeal, as we risk opening many issues in an ongoing process.
[2015-05-06 Lenexa: EricWF to write paper addressing 2136 and 2137]
[2024-10-03; Jonathan comments]
I could find no other cases in the entire standard where we say something like this in a Postconditions: element. 31.6.3.5.4 [streambuf.virt.pback] p2 says "Postconditions: On return, the constraints of [...]" which is probably redundant (postconditions are always "on return").
Proposed resolution:
This wording is relative to N3376.
template <class string_traits, class A> basic_regex& assign(const basic_string<charT, string_traits, A>& s, flag_type f = regex_constants::ECMAScript);[…]
-15- Effects: Assigns the regular expression contained in the strings, interpreted according the flags specified inf. If an exception is thrown,*thisis unchanged. -16- Postconditions:If no exception is thrown,flags()returnsfandmark_count()returns the number of marked sub-expressions within the expression.
CopyConstructible/MoveConstructible/CopyAssignable/MoveAssignable/Destructible?Section: 16.4.4.2 [utility.arg.requirements] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-03-23 Last modified: 2024-12-04
Priority: 3
View other active issues in [utility.arg.requirements].
View all other issues in [utility.arg.requirements].
View all issues with Open status.
Discussion:
According to 16.4.4.2 [utility.arg.requirements] p1
The template definitions in the C++ standard library refer to various named requirements whose details are set out in tables 17-24. In these tables,
Tis an object or reference type to be supplied by a C++ program instantiating a template;a,b, andcare values of type (possiblyconst)T;sandtare modifiable lvalues of typeT;udenotes an identifier;rvis an rvalue of typeT; andvis an lvalue of type (possiblyconst)Tor an rvalue of typeconst T.
Is it really intended that T may be a reference type? If so, what should a, b, c,
s, t, u, rv, and v mean? For example, are "int &" and
"int &&" MoveConstructible?
std::swap and std::for_each.
Can we use reference types there?
#include <iostream>
#include <utility>
int main()
{
int x = 1;
int y = 2;
std::swap<int &&>(x, y); // undefined?
std::cout << x << " " << y << std::endl;
}
#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>
struct F
{
void operator()(int n)
{
std::cout << n << std::endl;
++count;
}
int count;
} f;
int main()
{
int arr[] = { 1, 2, 3 };
auto&& result = std::for_each<int *, F &&>( // undefined?
std::begin(arr),
std::end(arr),
std::move(f));
std::cout << "count: " << result.count << std::endl;
}
Are these forms of usage well-defined?
Let's also consider the following constructor ofstd::thread:
template <class F, class ...Args> explicit thread(F&& f, Args&&... args);Requires:
Fand eachTiinArgsshall satisfy theMoveConstructiblerequirements.
When the first argument of this constructor is an lvalue (e.g. a name of a global function), template argument for F
is deduced to be lvalue reference type. What should "MoveConstructible" mean with regard to an lvalue reference
type? Maybe the wording should say that std::decay<F>::type and each std::decay<Ti>::type (where
Ti is an arbitrary item in Args) shall satisfy the MoveConstructible requirements?
[2013-03-15 Issues Teleconference]
Moved to Open.
The questions raised by the issue are real, and should have a clear answer.
[2015-10, Kona Saturday afternoon]
STL: std::thread needs to be fixed, and anything behaving like it needs to be fixed, rather than reference types. std::bind gets this right. We need to survey this. GR: That doesn't sound small to me. STL: Seach for CopyConstructible etc. It may be a long change, but not a hard one.
MC: It seems that we don't have a PR. Does anyone have one? Is anyone interested in doing a survey?
[2016-03, Jacksonville]
Casey volunteers to make a survey
[2016-06, Oulu]
During an independent survey performed by Daniel as part of the analysis of LWG 2716(i), some overlap was found between these two issues. Daniel suggested to take responsibility for surveying LWG 2146(i) and opined that the P/R of LWG 2716(i) should restrict to forwarding references, where the deduction to lvalue references can happen without providing an explicit template argument just by providing an lvalue function argument.
[2018-06, Rapperwsil]
Jonathan says that this will be covered by his Omnibus requirements paper.
[2019 Cologne Wednesday night]
Daniel will start working on this again; Marshall to provide rationale why some of the examples are not well-formed.
[2020-10-02; Issue processing telecon: change from P2 to P3]
For the examples given in the original report, the for_each
case is now banned, because 26.2 [algorithms.requirements] p15
forbids explicit template argument lists. std::thread's constructor
has also been fixed to describe the requirements on decay_t<T>
instead of T.
We believe we're more careful these days about using remove_cvref
or decay as needed, but there are still places where we incorrectly
state requirements in terms of types that might be references.
The swap case still needs solving. Still need a survey.
[2024-03-15; LWG 4047(i) addresses the swap part]
[2024-12-04; Daniel comments]
The mentioned requirement sets have been renamed a while ago to:
Cpp17CopyConstructible
Cpp17MoveConstructible
Cpp17CopyAssignable
Cpp17MoveAssignable
Cpp17Destructible
Proposed resolution:
Section: 16.4.4.3 [swappable.requirements], 23.2.2 [container.requirements.general] Status: LEWG Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2020-09-06
Priority: 3
View all other issues in [swappable.requirements].
View all issues with LEWG status.
Discussion:
Sub-clause 16.4.4.3 [swappable.requirements] defines two notions of swappability: a binary version defining when two objects are swappable with one another, and a unary notion defining whether an object is swappable (without qualification), with the latter definition requiring that the object satisfy the former with respect to all values of the same type.
LetT be a container type based on a non-propagating allocator whose instances do not necessarily
compare equal. Then sub-clause 23.2.2 [container.requirements.general] p7 implies that no object t
of type T is swappable (by the unary definition).
Throughout the standard it is the unary definition of "swappable" that is listed as a requirement (with the
exceptions of 22.2.2 [utility.swap] p4, 22.3.2 [pairs.pair] p31, 22.4.4.4 [tuple.swap] p2,
26.7.3 [alg.swap] p2, and 26.7.3 [alg.swap] p6, which use the binary definition). This renders
many of the mutating sequence algorithms of sub-clause 26.7 [alg.modifying.operations], for example,
inapplicable to sequences of standard container types, even where every element of the sequence is swappable
with every other.
Note that this concern extends beyond standard containers to all future allocator-based types.
Resolution proposal:
I see two distinct straightforward solutions:
I favor the latter solution, for reasons detailed in the following issue.
[ 2012-10 Portland: Move to Open ]
The issue is broader than containers with stateful allocotors, although they are the most obvious
example contained within the standard itself. The basic problem is that once you have a stateful
allocator, that does not propagate_on_swap, then whether two objects of this type can be
swapped with well defined behavior is a run-time property (the allocators compare equal) rather
than a simple compile-time property that can be deduced from the type. Strictly speaking, any
type where the nature of swap is a runtime property does not meet the swappable
requirements of C++11, although typical sequences of such types are going to have elements that
are all swappable with any other element in the sequence (using our other term of art
for specifying requirements) as the common case is a container of elements who all share the
same allocator.
The heart of the problem is that the swappable requirments demand that any two objects
of the same type be swappable with each other, so if any two such objects would not
be swappable with each other, then the whole type is never swappable. Many
algorithms in clause 25 are specified in terms of swappable which is essentially an
overspecification as all they actually need is that any element in the sequence is swappable
with any other element in the sequence.
At this point Howard joins the discussion and points out that the intent of introducing the
two swap-related terms was to support vector<bool>::reference types, and we are
reading something into the wording that was never intended. Consuses is that regardless of
the intent, that is what the words today say.
There is some support to see a paper reviewing the whole of clause 25 for this issue, and other select clauses as may be necessary.
There was some consideration to introducing a note into the front of clause 25 to indicate
swappable requirements in the clause should be interpreted to allow such awkward
types, but ultimately no real enthusiasm for introducing a swappable for clause 25
requirement term, especially if it confusingly had the same name as a term used with a
subtly different meaning through the rest of the standard.
There was no enthusiasm for the alternate resolution of requiring containers with unequal
allocators that do not propagate provide a well-defined swap behavior, as it is not
believed to be possible without giving swap linear complexity for such values,
and even then would require adding the constraint that the container element types are
CopyConstructible.
Final conclusion: move to open pending a paper from a party with a strong interest in stateful allocators.
[2016-03 Jacksonville]
Alisdair says that his paper P0178 addresses this.
[2016-06 Oulu]
P0178 reviewed, and sent back to LEWG for confirmation.
Thursday Morning: A joint LWG/LEWG meeting declined to adopt P0178.
[2017-02 in Kona, LEWG responds]
Note in the issue that this is tracked here
[2017-06-02 Issues Telecon]
Leave as LEWG; priority 3
Proposed resolution:
Apply P0178.
swap contractSection: 22.2.2 [utility.swap], 16.4.4.3 [swappable.requirements], 23.2.2 [container.requirements.general] Status: LEWG Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2020-10-02
Priority: 2
View all other issues in [utility.swap].
View all issues with LEWG status.
Discussion:
Sub-clause 22.2.2 [utility.swap] defines a non-member 'swap' function with defined behavior for
all MoveConstructible and MoveAssignable types. It does not guarantee
constant-time complexity or noexcept in general, however this definition does
render all objects of MoveConstructible and MoveAssignable type swappable
(by the unary definition of sub-clause 16.4.4.3 [swappable.requirements]) in the absence of
specializations or overloads.
swap function defined in Table 96, however,
defines semantics incompatible with the generic non-member swap function,
since it is defined to call a member swap function whose semantics are
undefined for some values of MoveConstructible and MoveAssignable types.
The obvious (perhaps naive) interpretation of sub-clause 16.4.4.3 [swappable.requirements] is as a guide to
the "right" semantics to provide for a non-member swap function (called in
the context defined by 16.4.4.3 [swappable.requirements] p3) in order to provide interoperable
user-defined types for generic programming. The standard container types don't follow these guidelines.
More generally, the design in the standard represents a classic example of "contract narrowing". It
is entirely reasonable for the contract of a particular swap overload to provide more
guarantees, such as constant-time execution and noexcept, than are provided by the swap
that is provided for any MoveConstructible and MoveAssignable types, but it is not
reasonable for such an overload to fail to live up to the guarantees it provides for general types when
it is applied to more specific types. Such an overload or specialization in generic programming is akin
to an override of an inherited virtual function in OO programming: violating a superclass contract in a
subclass may be legal from the point of view of the language, but it is poor design and can easily lead
to errors. While we cannot prevent user code from providing overloads that violate the more general
swap contract, we can avoid doing so within the library itself.
My proposed resolution is to draw a sharp distinction between member swap functions, which provide
optimal performance but idiosyncratic contracts, and non-member swap functions, which should always
fulfill at least the contract of 22.2.2 [utility.swap] and thus render objects swappable. The member
swap for containers with non-propagating allocators, for example, would offer constant-time
guarantees and noexcept but would only offer defined behavior for values with allocators that compare
equal; non-member swap would test allocator equality and then dispatch to either member swap or
std::swap depending on the result, providing defined behavior for all values (and rendering the type
"swappable"), but offering neither the constant-time nor the noexcept guarantees.
[2013-03-15 Issues Teleconference]
Moved to Open.
This topic deserves more attention than can be given in the telecon, and there is no proposed resolution.
[2016-03 Jacksonville]
Alisdair says that his paper P0178 addresses this.
[2016-08 Chicago]
Send to LEWG
[2016-06 Oulu]
P0178 reviewed, and sent back to LEWG for confirmation.
Thursday Morning: A joint LWG/LEWG meeting declined to adopt P0178.
[2020-10-02; remove P0178 as Proposed Resolution]
Proposed resolution:
std::array<T,0> initialization work when T is not default-constructible?Section: 23.3.3.5 [array.zero] Status: Open Submitter: Daryle Walker Opened: 2012-05-08 Last modified: 2021-03-14
Priority: 3
View other active issues in [array.zero].
View all other issues in [array.zero].
View all issues with Open status.
Discussion:
Objects of std::array<T, N> are supposed to be initialized with aggregate initialization (when
not the destination of a copy or move). This clearly works when N is positive. What happens when N
is zero? To continue using an (inner) set of braces for initialization, a std::array<T, 0> implementation
must have an array member of at least one element, and let default initialization take care of those secret elements.
This cannot work when T has a set of constructors and the default constructor is deleted from that set.
Solution: Add a new paragraph in 23.3.3.5 [array.zero]:
The unspecified internal structure of array for this case shall allow initializations like:
array<T, 0> a = { };and said initializations must be valid even when
Tis not default-constructible.
[2012, Portland: Move to Open]
Some discussion to understand the issue, which is that implementations currently have freedom to implement
an empty array by holding a dummy element, and so might not support value initialization, which is
surprising when trying to construct an empty container. However, this is not mandated, it is an unspecified
implementation detail.
Jeffrey points out that the implication of 23.3.3.1 [array.overview] is that this initialization syntax
must be supported by empty array objects already. This is a surprising inference that was not
obvious to the room, but consensus is that the reading is accurate, so the proposed resolution is not necessary,
although the increased clarity may be useful.
Further observation is that the same clause effectively implies that T must always be DefaultConstructible,
regardless of N for the same reasons - as an initializer-list may not supply enough values, and the
remaining elements must all be value initialized.
Concern that we are dancing angels on the head of pin, and that relying on such subtle implications in wording is not helpful. We need a clarification of the text in this area, and await wording.
[2015-02 Cologne]
DK: What was the outcome of Portland? AM: Initially we thought we already had the intended behaviour.
We concluded that T must always be DefaultConstructible, but I'm not sure why. GR: It's p2 in
std::array, "up to N". AM: That wording already implies that "{}" has to work when N
is zero. But the wording of p2 needs to be fixed to make clear that it does not imply that T must be
DefaultConstructible.
[2015-10, Kona Saturday afternoon]
MC: How important is this? Can you not just use default construction for empty arrays?
TK: It needs to degenerate properly from a pack. STL agrees.
JW: Yes, this is important, and we have to make it work.
MC: I hate the words "initialization like".
JW: I'll reword this.
WEB: Can I ask that once JW has reworded this we move it to Review rather than Open?
MC: We'll try to review it in a telecon and hopefully get it to tentatively ready.
STL: Double braces must also work: array<T, 0> a = {{}};.
Jonathan to reword.
[2018-03-14 Wednesday evening issues processing]
Jens suggested that we remove the requirement that begin() == end() == unique-value,
specifically the unique value part.
Previous resolution [SUPERSEDED]:
This wording is relative to N3376.
Add the following new paragraph between the current 23.3.3.5 [array.zero] p1 and p2:
-1-
-?- The unspecified internal structure ofarrayshall provide support for the special caseN == 0.arrayfor this case shall allow initializations like:array<T, 0> a = { };and said initializations must be valid even when
-2- In the case thatTis not default-constructible.N == 0,begin() == end() ==unique value. The return value ofdata()is unspecified. -3- The effect of callingfront()orback()for a zero-sized array is undefined. -4- Member functionswap()shall have a noexcept-specification which is equivalent tonoexcept(true).
[2018-06-14, Jonathan Wakely comments and provides revised wording]
The new wording does not address the 2018-03-14 suggestion from Jens to remove the unique value. It wasn't clear to me that there was consensus to make that change, and it would be a change in behaviour not just a clarification of the existing wording.
Previous resolution [SUPERSEDED]:
This wording is relative to N4750.
Modify 23.3.3.5 [array.zero] as indicated:
-1-
-?- A zero-sizedarrayshallprovides support for the special case of a zero-sizedarraythat is always empty, i.e.N == 0, with the properties described in this subclause.arraytype is an aggregate that meets theDefaultConstructible(Table 22) andCopyConstructible(Table 24) requirements. There is a single element of the aggregate, of an unspecifiedDefaultConstructibletype. [Note: This allows initialization of the formarray<T, 0> a = {{}};. There is no requirement forTto beDefaultConstructible. — end note] -2-In the case thatN == 0,begin() == end() ==unique valuebegin()andend()return non-dereferenceable iterators such thatbegin() == end()anda.begin() != b.begin()whereaandbare distinct objects of the same zero-sizedarraytype. The return value ofdata()is unspecified. -3- The effect of callingfront()orback()for a zero-sized array is undefined. -4- Member functionswap()shall havehas constant complexity and a non-throwing exception specification.
[2018-08-30, Jonathan revises wording following feedback from Daniel Kruegler and Tim Song.]
Daniel noted that it's undefined to compare iterators from different containers,
so a.begin() != b.begin() can't be used. That means whether the iterators
from different containers are unique is unobservable anyway.
We can say they don't share the same underlying sequence, which tells users they can't compare them
and tells implementors they can't return value-initialized iterators.
Tim noted that it's not sufficient to say the unspecified type in a zero-sized array is DefaultConstructible,
it also needs to be constructible from = {}. Also, a zero-sized array should be CopyAssignable.
Previous resolution [SUPERSEDED]:
This wording is relative to N4762.
Modify 23.3.3.5 [array.zero] as indicated:
-1-
-?- A zero-sizedarrayshallprovides support for the special case of a zero-sizedarraythat is always empty, i.e.N == 0, with the properties described in this subclause.arraytype is an aggregate that meets the Cpp17DefaultConstructible (Table 24) and Cpp17CopyConstructible (Table 26) and Cpp17CopyAssignable (Table 28) requirements. There is a single element of the aggregate, of an unspecified Cpp17DefaultConstructible type that is copy-list-initializable from an empty list. [Note: This allows initialization of the formarray<T, 0> a = {{}};. There is no requirement forTto be Cpp17DefaultConstructible. — end note] -2-In the case thatN == 0,begin() == end() ==unique valuebegin()andend()return non-dereferenceable iterators such thatbegin() == end(). Whenaandbare distinct objects of the same zero-sizedarraytype,a.begin()andb.begin()are not iterators over the same underlying sequence. [Note: Thereforebegin()does not return a value-initialized iterator — end note]. The return value ofdata()is unspecified. -3- The effect of callingfront()orback()for a zero-sized array is undefined. -4- Member functionswap()shall havehas constant complexity and a non-throwing exception specification.
[2021-03-14; Johel Ernesto Guerrero Peña comments and provides improved wording]
The currently proposed wording specifies:
There is a single element of the aggregate, of an unspecified Cpp17DefaultConstructible type that is copy-list-initializable from an empty list.
This doesn't specify which expressions involving zero-sized array specializations are constant expressions.
23.3.3.1 [array.overview] p4 specifies array<T, 0> to be a structural type when T
is a structural type. This requires that its single element, let's call it single-element, be a
structural type. But that says nothing about which of the special member functions of single-element
are constant expressions. By being a structural type, single-element is permitted to be implemented
as a literal class type. To meet this requirement, single-element can be implemented to have one
constexpr constructor that is not a copy or move constructor (6.9.1 [basic.types.general] p10), so
its default constructor needn't be constexpr. This is unlike non-zero-sized array specializations, which
inherit these properties from T. Furthermore, this permits an implementation of single-element
whose default constructor stores the result of std::source_location::current() in a data member
(as exemplified in the specification for current). Cpp17DefaultConstructible doesn't
require the default constructor to produce equal values. The simplest way to solve these issues and any other
that might arise from future changes and oversights would be to specify single-element as an empty
aggregate type. Then the wording from 23.3.3.2 [array.cons] p1 makes it clear that all the special
member functions are constant expressions. It would also mean that the default constructor produces
template-argument-equivalent values.
Proposed resolution:
This wording is relative to N4878.
Modify 23.3.3.5 [array.zero] as indicated:
-1-
-?- A zero-sizedarrayshallprovides support for the special case of a zero-sizedarraythat is always empty, i.e.N == 0, with the properties described in this subclause.arraytype is an aggregate that meets the Cpp17DefaultConstructible (Table 29 [tab:cpp17.defaultconstructible]) and Cpp17CopyConstructible (Table 31 [tab:cpp17.copyconstructible]) and Cpp17CopyAssignable (Table 33 [tab:cpp17.copyassignable]) requirements. There is a single element of the aggregate, of an unspecified empty aggregate type. [Note: This allows initialization of the formarray<T, 0> a = {{}};. There is no requirement forTto be Cpp17DefaultConstructible. — end note] -2-In the case thatN == 0,begin() == end() ==unique valuebegin()andend()return non-dereferenceable iterators such thatbegin() == end(). Whenaandbare distinct objects of the same zero-sizedarraytype,a.begin()andb.begin()are not iterators over the same underlying sequence. [Note: Thereforebegin()does not return a value-initialized iterator — end note].. The return value ofdata()is unspecified. -3- The effect of callingfront()orback()for a zero-sized array is undefined. -4- Member functionswap()shall havehas constant complexity and a non-throwing exception specification.
std::vectorSection: 23.3.13.3 [vector.capacity] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-05-08 Last modified: 2022-11-06
Priority: 3
View other active issues in [vector.capacity].
View all other issues in [vector.capacity].
View all issues with Open status.
Discussion:
There are various operations on std::vector that can cause elements of the vector to be
moved from one location to another. A move operation can use either rvalue or const lvalue as
argument; the choice depends on the value of !is_nothrow_move_constructible<T>::value &&
is_copy_constructible<T>::value, where T is the element type. Thus, some operations
on std::vector (e.g. 'resize' with single parameter, 'reserve', 'emplace_back') should have
conditional requirements. For example, let's consider the requirement for 'reserve' in N3376 –
23.3.13.3 [vector.capacity]/2:
Requires:
Tshall beMoveInsertableinto*this.
This requirement is not sufficient if an implementation is free to select copy constructor when
!is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value
evaluates to true. Unfortunately, is_copy_constructible cannot reliably determine whether
T is really copy-constructible. A class may contain public non-deleted copy constructor whose
definition does not exist or cannot be instantiated successfully (e.g.,
std::vector<std::unique_ptr<int>> has copy constructor, but this type is not
copy-constructible). Thus, the actual requirements should be:
if !is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value
then T shall be CopyInsertable into *this;
otherwise T shall be MoveInsertable into *this.
Maybe it would be useful to introduce a new name for such conditional requirement (in addition to
"CopyInsertable" and "MoveInsertable").
[2016-08 Chicago]
The problem does not appear to be as severe as described. The MoveInsertable
requirements are consistently correct, but an issue may arise on the
exception-safety guarantees when we check for
is_copy_constructible_v<T>. The problem, as described, is
typically for templates that appear to have a copy constructor, but one that
fails to compile once instantiated, and so gives a misleading result for the
trait.
In general, users should not provide such types, and the standard would not
serve users well by trying to address support for such types. However, the
standard should not be providing such types either, such as
vector<unique_ptr<T>>. A possible resolution would be
to tighten the constraints in Table 80 — Container Requirements, so that if
the Requirements for the copy constructor/assingment operator of a container
are not satisfied, that operation shall be deleted.
A futher problem highlighted by this approach is that there are no constraints on
the copy-assignment operator, so that vector<unique_ptr<T>>
should be CopyAssignable! However, we can lift the equivalent constraints from
the Allocator-aware container requirements.
[08-2016, Chicago]
Fri PM: Move to Open
[2017-11 Albuquerque Saturday issues processing]
There's a bunch of uses of "shall" here that are incorrect. Also, CopyInsertable contains some semantic requirements, which can't be checked at compile time, so 'ill-formed' is not possible for detecting that.
[2018-06 Rapperswil Wednesday issues processing]
Daniel to provide updated wording.
[2018-06-12, Daniel provides revised wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N4606.
23.2.2 [container.requirements.general] Table 80 — Container requirements Expression Return type Operational semantics Assertion/note/pre-/post-condition Complexity X(a)Requires: TisCopyInsertableintoX(see below)., otherwise this expression shall be ill-formed.
post:a == X(a).linear X u(a)X u = a;Requires: TisCopyInsertableintoX(see below)., otherwise this expression shall be ill-formed.
post:u == a.linear ... ... ... ... ... r = aX&Requires: TisCopyInsertableintoXandCopyAssignable, otherwise this expression shall be ill-formed.
post:r == a.linear
23.2.2 [container.requirements.general] Table 83 — Allocator-aware container requirements Expression Return type Operational semantics Assertion/note/pre-/post-condition Complexity a = tX&Requires: TisCopyInsertableintoXandCopyAssignable., otherwise this expression shall be ill-formed
post:r == a.linear
[2018-08-23 Batavia Issues processing. Priority to 3]
Changed CopyInsertable -> Cpp17CopyInsertable in the resolution.
Tim says that the wording is not quite right — it imposes additional requirements.
[2022-11-06; Daniel comments]
This issue has considerable overlap with LWG 3758(i).
Proposed resolution:
This wording is relative to N4750.
The revised wording below uses the new Mandates: element introduced by adopting P0788R3 at the Rapperswil meeting 2018 and which will become a new term of art with Jonathan's omnibus paper throughout the Standard Library.
| Expression | Return type | Operational semantics | Assertion/note/pre-/post-condition | Complexity |
X(a) |
Mandates: Syntactic requirements of Tis Cpp17CopyInsertable into X (see below).Requires: T is Cpp17CopyInsertable into
X post: a == X(a).
|
linear | ||
X u(a)X u = a; |
Mandates: Syntactic requirements of Tis Cpp17CopyInsertable into X (see below).Requires: T is Cpp17CopyInsertable into
X post: u == a.
|
linear | ||
| ... | ... | ... | ... | ... |
r = a |
X& |
Mandates: Syntactic requirements of Tis Cpp17CopyInsertable into X (see below) and
CopyAssignable.Requires: T is Cpp17CopyInsertable into X
and CopyAssignable.post: r == a. |
linear |
| Expression | Return type | Operational semantics | Assertion/note/pre-/post-condition | Complexity |
a = t |
X& |
Mandates: Syntactic requirements of T isCpp17CopyInsertable into X and CopyAssignable.Requires: T is Cpp17CopyInsertable into X and
CopyAssignable.post: r == a. |
linear |
operator + in the description of the algorithmsSection: 26 [algorithms] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-08-01 Last modified: 2018-06-12
Priority: 4
View other active issues in [algorithms].
View all other issues in [algorithms].
View all issues with Open status.
Discussion:
According to 26.1 [algorithms.general]/12,
In the description of the algorithms operators
+and-are used for some of the iterator categories for which they do not have to be defined. In these cases the semantics ofa+nis the same as that ofX tmp = a; advance(tmp, n); return tmp;
There are several places where such operator + is applied to an output iterator — for example, see the
description of std::copy:
template<class InputIterator, class OutputIterator> OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result);-1- Effects: Copies elements in the range
[first,last)into the range[result,result + (last - first))starting fromfirstand proceeding tolast. For each non-negative integern < (last - first), performs*(result + n) = *(first + n).
std::advance is not supposed to be applicable to output iterators, so we need a different method of description.
[2014-06-07 Daniel comments and provides wording]
The specification for output iterators is somewhat tricky, because here a sequence of increments is required to be combined with intervening assignments to the dereferenced iterator. I tried to respect this fact by using a conceptual assignment operation as part of the specification.
Another problem in the provided as-if-code is the question which requirements are imposed onn. Unfortunately,
the corresponding function advance is completely underspecified in this regard, so I couldn't borrow wording
from it. We cannot even assume here that n is the difference type of the iterator, because for output iterators there is
no requirements for this associated type to be defined. The presented wording attempts to minimize assumptions, but still
can be considered as controversial.
[2018-06 Rapperswil Wednesday issues processing]
Status to Open
Proposed resolution:
This wording is relative to N4606.
Change 26.1 [algorithms.general] around p12 as indicated:
-12- In the description of the algorithms operators
+and-are used for some of the iterator categories for which they do not have to be defined. In these cases the semantics ofa+nis the same as that ofX tmp = a; advance(tmp, n); return tmp;when
Xmeets the input iterator requirements (24.3.5.3 [input.iterators]), otherwise it is the same as that ofX tmp = a; for (auto i = n; i; ++tmp, (void) --i) *tmp = Expr(i); return tmp;where
Expr(i)denotes the(n-i)th expression that is assigned to for the corresponding algorithm; and that ofb-ais the same as ofreturn distance(a, b);
swap breaks unordered containers' stateSection: 23.2.8.2 [unord.req.except] Status: Open Submitter: Alisdair Meredith Opened: 2012-09-23 Last modified: 2019-07-22
Priority: 3
View all issues with Open status.
Discussion:
The hash functor and key-comparison functor of unordered containers are allowed to throw on swap.
23.2.8.2 [unord.req.except]p3 "For unordered associative containers, no swap function throws
an exception unless that exception is thrown by the swap of the container's Hash or Pred object (if any)."
In such a case we must offer the basic exception safety guarantee, where both objects are left in valid
but unspecified states, and no resources are leaked. This yields a corrupt, un-usable container if the
first swap succeeds, but the second fails by throwing, as the functors form a matched pair.
So our basic scenario is first, swap the allocators if the allocators propagate on swap, according to
allocator_traits. Next we swap the pointers to our internal hash table data structures, so that
they match the allocators that allocated them. (Typically, this operation cannot throw). Now our containers
are back in a safely destructible state if an exception follows.
Next, let's say we swap the hash functor, and that throws. We have a corrupt data structure, in that the buckets are not correctly indexed by the correct functors, lookups will give unpredicatable results etc. We can safely restore a usable state by forcibly clearing each container - which does not leak resources and leaves us with two (empty but) usable containers.
Now let us assume that the hasher swap succeeds. Next we swap the equality comparator functor, and this too could throw. The important point to bear in mind is that these two functors form an important pairing - two objects that compare equal by the equality functor must also hash to the same value. If we swap one without the other, we most likely leave the container in an unusable state, even if we clear out all elements.
1. A colleague pointed out that the solution for this is to dynamically allocate the two functors, and then
we need only swap pointers, which is not a throwing operation. And if we don't want to allocate on default
construction (a common QoI request), we might consider moving to a dynamically allocated functors whenever
swap is called, or on first insertion. Of course, allocating memory in swap is a whole
new can of worms, but this does not really sound like the design we had intended.
2. The simplest option is to say that we do not support hasher or equality functors that throw on ADL
swap. Note that the requirement is simply to not throw, rather than to be explicitly
marked as noexcept. Throwing functors are allowed, so long as we never use values that
would actually manifest a throw when used in an unordered container.
Pablo went on to give me several more options, to be sure we have a full set to consider:
3. Disallow one or the other functor from throwing. In that case, the possibly-throwing functor must be swapped first, then the other functor, the allocator, and the data pointer(s) afterwards (in any order -- there was a TC that allocator assignment and swap may not throw if the corresponding propagation trait is true.). Of course, the question becomes: which functor is allowed to throw and which one is not?
4. Require that any successful functor swap be reliably reversible.
This is very inventive. I know of no other place in the standard where
such a requirement is stated, though I have occasionally wanted such a
guarantee.
5. Allow a failed swap to leave the containers in a state where future insertions may fail for reasons other than is currently allowed. Specifically, if the hash and equality functors are out of sync, all insertions will fail. Presumably some "incompletely swapped" exception would be thrown. This is "slightly" inventive, although people have been discussing "radioactive" states for a while.
[2013-03-15 Issues Teleconference]
Moved to Open.
[2019 Cologne Wednesday night]
Billy to write resolution for option #2. This may require a paper.
Proposed resolution:
max_load_factor(z) makes no strong guarantees, but bans useful behaviorSection: 23.2.8 [unord.req] Status: Open Submitter: Alisdair Meredith Opened: 2012-10-09 Last modified: 2016-12-10
Priority: 3
View other active issues in [unord.req].
View all other issues in [unord.req].
View all issues with Open status.
Discussion:
The user cannot specify a max_load_factor for their unordered container
at construction, it must be supplied after the event, when the container is
potentially not empty. The contract for this method is deliberately vague, not
guaranteeing to use the value supplied by the user, and any value actually used
will be used as a ceiling that the container will attempt to respect.
The only guarantee we have is that, if user requests a max_load_factor
that is less than the current load_factor, then the operation will take
constant time, thus outlawing an implementation that chooses to rehash and so
preserve as a class invariant that load_factor < max_load_factor.
Reasonable options conforming to the standard include ignoring the user's request
if the requested value is too low, or deferring the rehash to the next insert
operation and allowing the container to have a strange state (wrt max_load_factor)
until then - and there is still the question of rehashing if the next insert
is for a duplicate key in a unique container.
Given the deliberate vagueness of the current wording, to support a range of reasonable (but not perfect) behaviors, it is not clear why the equally reasonable rehash to restore the constraint should be outlawed. It is not thought that this is a performance critical operation, where users will be repeatedly setting low load factors on populated containers, in a tight or (less unlikely) an instant response scenario.
[2013-03-15 Issues Teleconference]
Moved to Open.
Alisdair to provide wording.
[2016-11-12, Issaquah]
Sat PM: Howard to provide wording
[2016-11-17 Howard provided wording.]
The provided wording is consistent with LWG discussion in Issaquah. An implementation of the proposed wording would be setting
max_load_factor()tomax(z, load_factor()). This preserves the container invariant:load_factor() <= max_load_factor()And it preserves the existing behavior that no rehash is done by this operation.
If it is desired to change the
max_load_factor()to something smaller than the currentload_factor()that can be done by first reducing the currentload_factor()by either increasingbucket_count()(viarehashorreserve), or decreasingsize()(e.g.erase), and then changingmax_load_factor().This resolution reaffirms that
load_factor() <= max_load_factor()is a container invariant which can never be violated.
[2016-11-27, Nico comments]
Current implementations behave differently.
In regard to the sentence"The only guarantee we have is that, if user requests aNote that the current spec says that there is constant complexity without any precondition. So, rehashing to keep the invariant would violate the spec (which is probably not be the intention). This issue is related to LWG 2199(i).max_load_factorthat is less than the currentload_factor, then the operation will take constant time, thus outlawing an implementation that chooses to rehash and so preserve as a class invariant thatload_factor < max_load_factor."
Proposed resolution:
Modify Table 87 as follows:
| Expression | Return type | Assertion/note pre-/post-condition | Complexity |
|---|---|---|---|
a.max_load_factor(z)
|
void
|
Pre:
Post:
Note: |
Constant |
asyncSection: 32.10.9 [futures.async] Status: Deferred Submitter: Detlef Vollmann Opened: 2012-10-19 Last modified: 2016-01-28
Priority: 4
View other active issues in [futures.async].
View all other issues in [futures.async].
Discussion:
promise, packaged_task, and async are the only
places where a shared state is actually supposed to be allocated. Accordingly,
promise and packaged_task are "allocator-aware". But
function template async provides no way to provide an allocator.
[2013-09 Chicago]
Matt: deprecate async
Nico: read my paper Alisdair: defer issues to wait for polymorphic allocators Alisdair: defer, active topic of research Deferred[2014-02-20 Re-open Deferred issues as Priority 4]
[2015-05 Lenexa, SG1 response]
We want whatever status approximates: "will not fix; we're working on a replacement facility and don't want to add features to a broken one"
Proposed resolution:
initializer_list constructor requirementsSection: 23.2.4 [sequence.reqmts], 23.2.7 [associative.reqmts], 23.2.8 [unord.req], 29.5.3.2 [rand.req.seedseq] Status: Open Submitter: Jeffrey Yasskin Opened: 2012-10-21 Last modified: 2020-09-06
Priority: 3
View other active issues in [sequence.reqmts].
View all other issues in [sequence.reqmts].
View all issues with Open status.
Discussion:
In 23.2.4 [sequence.reqmts] p3, we have "il designates an object of type
initializer_list<value_type>", and then several functions that take
'il' as an argument. However, an expression like {1, 2, 'a'} is not
an object of type initializer_list<int> unless it's used to initialize
an explicitly-typed variable of that type. I believe we want:
std::vector<int> v;
v = {1, 2, 'a'};
to compile portably, so we should say something different when defining 'il'. The
same phrasing happens in 23.2.7 [associative.reqmts], 23.2.8 [unord.req], and
29.5.3.2 [rand.req.seedseq].
initializer_list<exact_type>.
[2013-03-15 Issues Teleconference]
Moved to Open.
This is definitely not NAD
Should copy the suggested wording as the proposed resolution.
[2019-03-26; Daniel comments and provides wording]
The 2013-03-15 comment is confusing, since it recommends to "copy the suggested wording as the proposed resolution".
I couldn't find such wording in the issue nor in the associated wiki, so I provided that wording out of myself.
The tricky part is to define which kind of braced-init-list we want to allow. As Tim Song pointed out, we
still need the existing support for std::initializer_list<value_type> as well, because otherwise
existing semantics based on expressions such as li.begin() won't work anymore.
The below suggested wording restricts supported braced-init-lists to every initializer list that can be used
to copy-list-initialize an object of type std::initializer_list<value_type> by saying:
"
bildesignates any braced-init-list suitable to copy-list-initialize an object of typeinitializer_list<value_type>(9.5.5 [dcl.init.list])"
As a drive-by fix, the provided wording adds another initialization "expression" that makes the construction of the form
std::vector<int> v = {1, 2, 'a'};
valid (We just miss a copy-initialization case).
Proposed resolution:
This wording is relative to N4810.
[Drafting note: We need to special-case the "expression"
X u = bil;below, because for empty braced-init-list the effects are those of calling the default constructor. — end drafting note]
Modify 23.2.4 [sequence.reqmts] as indicated:
-3- In Tables 66 and 67, […]
ildesignates an objectvalue of typeinitializer_list<value_type>,bildesignates any braced-init-list suitable to copy-list-initialize an object of typeinitializer_list<value_type>(9.5.5 [dcl.init.list]), […]
Modify Table 66 — "Sequence container requirements (in addition to container)" as indicated:
Table 66 — Sequence container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-condition[…]X(il)
X u = il;Equivalent to X(il.begin(), il.end())
orX u(il.begin(), il.end());, respectivelyX(bil)Equivalent to X(initializer_list<value_type>(bil))X u = bil;If bilis empty, equivalent toX u;, otherwise
equivalent toX u = initializer_list<value_type>(bil);a = ilX&[…] a = bilX&Equivalent to a = initializer_list<value_type>(bil)[…]a.insert(p, il)iterator[…] a.insert(p, bil)iteratorEquivalent to a.insert(p, initializer_list<value_type>(bil))[…]a.assign(il)void[…] a.assign(bil)voidEquivalent to a.assign(initializer_list<value_type>(bil))[…]
Modify 23.2.7 [associative.reqmts] as indicated:
-8- In Table 69, […]
ildesignates an objectvalue of typeinitializer_list<value_type>,bildesignates any braced-init-list suitable to copy-list-initialize an object of typeinitializer_list<value_type>(9.5.5 [dcl.init.list]), […]
Modify Table 69 — "Associative container requirements (in addition to container)" as indicated:
Table 69 — Associative container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-conditionComplexity […]X(il)
X u = il;same as X(il.begin(), il.end())
orX u(il.begin(), il.end());, respectivelysame as X(il.begin(), il.end())
orX u(il.begin(), il.end());, respectivelyX(bil)Equivalent to X(initializer_list<value_type>(bil))X u = bil;If bilis empty, equivalent toX u;, otherwise
equivalent toX u = initializer_list<value_type>(bil);X(il,c)same as X(il.begin(), il.end(), c)same as X(il.begin(), il.end(), c)X(bil, c)Equivalent to X(initializer_list<value_type>(bil), c)a = ilX&[…] […] a = bilX&Equivalent to a = initializer_list<value_type>(bil)[…]a.insert(il)voidequivalent to a.insert(il.begin(), il.end())a.insert(bil)voidEquivalent to a.insert(initializer_list<value_type>(bil))[…]a.assign(il)void[…] a.assign(bil)voidEquivalent to a.assign(initializer_list<value_type>(bil))[…]
Modify 23.2.8 [unord.req] p11's bullet list as indicated:
-11- In Table 70:
(11.1) — […]
[…]
(11.14) —
ildenotes a value of typeinitializer_list<value_type>,(11.?) —
bildenotes any braced-init-list suitable to copy-list-initialize an object of typeinitializer_list<value_type>(9.5.5 [dcl.init.list]),[…]
Modify Table 70 — "Unordered associative container requirements (in addition to container)" as indicated:
[Drafting note: There is a preexisting issue with Table 70, that there is no symbol
uspecified ("udenotes the name of a variable being declared"), so existing initialization forms with a named variable are currently always written as "X a[…]" whereais defined as "adenotes a value of typeX", the wording below follows this existing practice but the author of this wording would like to kindly ask the Project Editor to introduce said symboluand apply it to all existing and new such named initialization forms instead. — end drafting note]
Table 70 — Unordered associative container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-conditionComplexity […]X(il)
X a = il;XSame as X(il.begin(), il.end())
orX a(il.begin(), il.end());, respectivelySame as X(il.begin(), il.end())
orX a(il.begin(), il.end());, respectivelyX(bil)XEquivalent to X(initializer_list<value_type>(bil))X a = bil;XIf bilis empty, equivalent toX a;, otherwise
equivalent toX a = initializer_list<value_type>(bil);X(il, n)XSame as X(il.begin(), il.end(), n)Same as X(il.begin(), il.end(), n)X(bil, n)XEquivalent to X(initializer_list<value_type>(bil), n)X(il, n, hf)XSame as X(il.begin(), il.end(), n, hf)Same as X(il.begin(), il.end(), n, hf)X(bil, n, hf)XEquivalent to X(initializer_list<value_type>(bil), n, hf)X(il, n, hf, eq)XSame as X(il.begin(), il.end(), n, hf, eq)Same as X(il.begin(), il.end(), n, hf, eq)X(bil, n, hf, eq)XEquivalent to X(initializer_list<value_type>(bil), n, hf, eq)[…]a = ilX&[…] […] a = bilX&Equivalent to a = initializer_list<value_type>(bil)[…]a.insert(il)voidSame as a.insert(il.begin(), il.end()).Same as a.insert(il.begin(), il.end()).a.insert(bil)voidEquivalent to a.insert(initializer_list<value_type>(bil))[…]
Modify 29.5.3.2 [rand.req.seedseq] p2's bullet list as indicated:
-2- A class
Ssatisfies the requirements of a seed sequence if the expressions shown in Table 82 are valid and have the indicated semantics, and […] In that Table and throughout this subclause:
(2.1) — […]
(2.?) —
udenotes the name of a variable being declared,[…]
(2.6) —
ilis a value ofinitializer_list<T>.;(2.?) —
bildenotes any braced-init-list suitable to copy-list-initialize an object of typeinitializer_list<T>(9.5.5 [dcl.init.list]).
Modify Table 82 — "Seed sequence requirements" as indicated:
Table 82 — Seed sequence requirements Expression Return type Pre/post-condition Complexity […]S(il)
S u = il;Same as S(il.begin(), il.end())
orS u(il.begin(), il.end());, respectivelysame as S(il.begin(), il.end())
orS u(il.begin(), il.end());, respectivelyS(bil)Equivalent to S(initializer_list<T>(bil))S u = bil;If bilis empty, equivalent toS u;, otherwise
equivalent toS u = initializer_list<T>(bil);[…]
basic_ios::init call restrictionsSection: 31.5.4.2 [basic.ios.cons] Status: Open Submitter: Andrey Semashev Opened: 2012-11-09 Last modified: 2021-07-31
Priority: 4
View all other issues in [basic.ios.cons].
View all issues with Open status.
Discussion:
There is an ambiguity in how std::basic_ios::init method (31.5.4.2 [basic.ios.cons])
can be used in the derived class. The Standard only specify the state of the basic_ios
object after the call completes. However, in basic_ios default constructor description
(31.5.4.2 [basic.ios.cons]) there is this sentence:
Effects: Constructs an object of class
basic_ios(31.5.2.8 [ios.base.cons]) leaving its member objects uninitialized. The object shall be initialized by callingbasic_ios::initbefore its first use or before it is destroyed, whichever comes first; otherwise the behavior is undefined.
This restriction hints that basic_ios::init should be called exactly
once before the object can be used or destroyed, because basic_ios::init
may not know whether it was called before or not (i.e. whether its members are actually
uninitialized or are initialized by the previous call to basic_ios::init). There
is no such restriction in the basic_ios::init preconditions so it is not clear whether it is
allowed to call basic_ios::init multiple times or not.
basic_ios::init is called multiple times, while GCC 4.7 and STLPort
reinitialize the basic_ios object correctly without memory leak or any
other undesired effects. There was a discussion of this issue on Boost
developers mailing list,
and there is a test case
that reproduces the problem. The test case is actually a bug report for my Boost.Log library,
which attempts to cache basic_ostream-derived objects internally to avoid expensive construction
and destruction. My stream objects allowed resetting the stream buffer pointers the stream
is attached to, without requiring to destroy and construct the stream.
My personal view of the problem and proposed resolution follows.
While apparently the intent of basic_ios::init is to provide a way to
initialize basic_ios after default construction, I see no reason to
forbid it from being called multiple times to reinitialize the stream.
Furthermore, it is possible to implement a conforming basic_ios that
does not have this restriction.
The quoted above section of the Standard that describes the effects of
the default constructor is misleading. The Standard does not mandate
any data members of basic_ios or ios_base (31.5.2 [ios.base]), which
it derives from. This means that the implementation is allowed to use
non-POD data members with default constructors that initialize the
members with particular default values. For example, in the case of
Microsoft Visual C++ STL the leaked memory is an std::locale instance
that is dynamically allocated during basic_ios::init, a raw pointer to
which is stored within ios_base. It is possible to store e.g. an
unique_ptr instead of a raw pointer as a member of ios_base, the smart
pointer will default initialize the underlying raw pointer on default
construction and automatically destroy the allocated object upon being
reset or destroyed, which would eliminate the leak and allow
basic_ios::init to be called multiple times. This leads to conclusion
that the default constructor of basic_ios cannot leave "its member
objects uninitialized" but instead performs default initialization of
the member objects, which would mean the same thing in case of POD types.
However, I feel that restricting ios_base and basic_ios members to
non-POD types is not acceptable. Since multiple calls to basic_ios::init are
not forbidden by the Standard, I propose to correct the basic_ios default
constructor description so that it is allowed to destroy basic_ios object
without calling basic_ios::init. This would imply that any raw members of
basic_ios and ios_base should be initialized to values suitable for
destruction (essentially, this means only initializing raw pointers to NULL). The new
wording could look like this:
Effects: Constructs an object of class
basic_ios(31.5.2.8 [ios.base.cons]) initializing its member objects to unspecified state, only suitable forbasic_iosdestruction. The object shall be initialized by callingbasic_ios::initbefore its first use; otherwise the behavior is undefined.
This would remove the hint that basic_ios::init must be called exactly
once. Also, this would remove the requirement for basic_ios::init to
be called at all before the destruction. This is also an important issue because
the derived stream constructor may throw an exception before it manages to call
basic_ios::init (for example, if the streambuf constructor throws), and
in this case the basic_ios destructor has undefined behavior.
basic_ios::init multiple times, a remark
or a footnote for basic_ios::init postconditions could be added to explicitly
state the semantics of calling it multiple times. The note could read as follows:
The function can be called multiple times during the object lifetime. Each subsequent call reinitializes the object to the described in postconditions initial state.
[2013-04-20, Bristol]
Alisdair: The current wording is unclear but the proposed resolution is wrong
Solution: Clarify thatinit must be called once and only once. Move then to review.
[2021-07-29 Tim comments]
The requirement that "init must be called once and only once" conflicts
with the disposition of LWG 135(i).
Proposed resolution:
This wording is relative to N3485.
Edit 31.5.4.2 [basic.ios.cons] as indicated:
basic_ios();-2- Effects: Constructs an object of class
basic_ios(31.5.2.8 [ios.base.cons])leaving its member objects uninitializedinitializing its member objects to unspecified state, only suitable forbasic_iosdestruction. The object shall be initialized by callingbasic_ios::initbefore its first useor before it is destroyed, whichever comes first; otherwise the behavior is undefined.void init(basic_streambuf<charT,traits>* sb);Postconditions: The postconditions of this function are indicated in Table 128.
-?- Remarks: The function can be called multiple times during the object lifetime. Each subsequent call reinitializes the object to the described in postconditions initial state.
CopyConstructibleSection: 23.2.7 [associative.reqmts], 23.2.8 [unord.req] Status: Open Submitter: Alisdair Meredith Opened: 2012-11-14 Last modified: 2015-10-22
Priority: 3
View other active issues in [associative.reqmts].
View all other issues in [associative.reqmts].
View all issues with Open status.
Discussion:
The requirements on the functors used to arrange elements in the various associative and unordered containers are given by a set of expressions in tables 102 — Associative container requirements, and 103 — Unordered associative container requirements. In keeping with Library convention these expressions make the minimal requirements necessary on their types. For example, we have the following 3 row extracts for the unordered containers:
| Expression | Assertion/note pre-/post-condition |
|
Requires: hasher and key_equal are CopyConstructible.
|
|
Requires: hasher is CopyConstructible and
key_equal is DefaultConstructible.
|
|
Requires: hasher and key_equal are DefaultConstructible.
|
However, the signature for each class template requires that the functors must effectively be
CopyConstructible for each of these expressions:
template <class Key,
class T,
class Hash = hash<Key>,
class Pred = std::equal_to<Key>,
class Allocator = std::allocator<std::pair<const Key, T> > >
class unordered_map
{
...
// construct/destroy/copy
explicit unordered_map(size_type n = see below,
const hasher& hf = hasher(),
const key_equal& eql = key_equal(),
const allocator_type& a = allocator_type());
...
}
The letter of the standard can be honored as long as implementors recognize
their freedom to split this one signature into multiple overloads, so that
the documented default arguments (requiring a CopyConstructible functor)
are not actually passed as default arguments.
As we look into the requirements for the copy constructor and copy-assignment operator, the requirements are even more vague, as the explicit requirements on the functors are not called out, other than saying that the functors are copied.
Must the functors be CopyAssignable? Or is CopyConstructible
sufficient in this case? Do we require that the functors be Swappable
so that the copy-swap idiom can be deployed here? Note that a type that is both
CopyConstructible and CopyAssignable is still not guaranteed to
be Swappable as the user may delete the swap function for their
type in their own namespace, which would be found via ADL.
Some clean-up of the requirements table looks necessary, to at least document the
assignment behavior. In addition, we should have clear guidance on whether these
functors should always be CopyConstructible, as suggested by the class
template definitions, or if the requirement tables are correct and we should
explicitly split up the constructors in the (unordered) associative containers
to no longer use default (function) arguments to obtain their defaulted functors.
I recommend the simplest solution would be to always require that the functors
for (unordered) associative containers be CopyConstructible, above the
requirements tables themselves, so that the issue need not be addressed within
the tables. I suggest that the assignment operators for these containers add
the requirement that the functors be Swappable, rather than forwarding
the corresponding Assignable requirement.
[2013-03-15 Issues Teleconference]
Moved to Open.
Alisdair to propose wording.
[2014-06-08, Daniel comments]
The area of this issue partially overlaps what LWG 2227(i) addresses.
[2015-10-20, Daniel comments]
The revised resolution of LWG 2227(i) should resolve this issue as well. It follows the recommendations
of the submitter to require CopyConstructible requirements for the function objects owned by containers,
but it does not impose any further fundamental requirements.
Proposed resolution:
See the resolution of LWG 2227(i).
regex_replace(basic_string) allocator handlingSection: 28.6.10.4 [re.alg.replace] Status: New Submitter: Jeffrey Yasskin Opened: 2012-11-26 Last modified: 2016-01-28
Priority: 3
View all other issues in [re.alg.replace].
View all issues with New status.
Discussion:
template <class traits, class charT, class ST, class SA>
basic_string<charT, ST, SA>
regex_replace(const basic_string<charT, ST, SA>& s,
const basic_regex<charT, traits>& e,
const charT* fmt,
regex_constants::match_flag_type flags =
regex_constants::match_default);
and friends are documented as
Constructs an empty string result of type
basic_string<charT, ST, SA>and callsregex_replace(back_inserter(result), s.begin(), s.end(), e, fmt, flags).
This appears to require the result to have a default-constructed allocator, which isn't even possible for all allocator types. I suspect the allocator should be copied from 's' instead. Possibly there should be an additional defaulted argument to override the allocator of the result.
Proposed resolution:
operator== for regex_token_iteratorSection: 28.6.11.2.3 [re.tokiter.comp] Status: Open Submitter: Pete Becker Opened: 2012-11-21 Last modified: 2024-10-03
Priority: 3
View all issues with Open status.
Discussion:
Consider the following example:
std::string str0("x");
std::regex rg0("a");
std::regex_token_iterator it0(str0.begin(), str0.end(), rg0, -1); // points at "x" in str0
std::string str1("x");
std::regex rg1("b");
std::regex_token_iterator it1(str1.begin(), str1.end(), rg1, -1); // points at "x" in str1
28.6.11.2.3 [re.tokiter.comp] p1 says that it0.operator==(it1) returns true "if
*this and right are both suffix iterators and suffix == right.suffix"; both
conditions are satisfied in this example. It does not say that they must both be iterators
into the same sequence, nor does it say (as general iterator requirements do) that they must
both be in the domain of == in order for the comparison to be meaningful. It's a
simple statement: they're equal if the strings they point at compare equal. Given this being
a valid comparison, the obtained result of "true" looks odd.
[2014-02-10]
Priority set to 2
[2018-08-20 Casey adds a proposed resolution]
Priority changed to 3.
Marshall notes that iterator comparisons typically require the iterators to denote elements of the same sequence.Previous resolution [SUPERSEDED]:
This wording is relative to N4762.
Modify 28.6.11.2.3 [re.tokiter.comp] as follows:
bool operator==(const regex_token_iterator& right) const;-?- Expects:
*thisandrightare both end-of-sequence iterators or both have the same underlying sequence.-1- Returns:
trueif*thisandrightare both end-of-sequence iterators, or if […]bool operator!=(const regex_token_iterator& right) const;-?- Expects:
*thisandrightare both end-of-sequence iterators or both have the same underlying sequence.-2- Returns:
!(*this == right).
[2018-08-23 Casey revises the P/R in response to LWG feedback]
Previous resolution [SUPERSEDED]:
This wording is relative to N4762.
Modify 28.6.11.2.3 [re.tokiter.comp] as follows:
bool operator==(const regex_token_iterator& right) const;-?- Expects: At least one of
*thisandrightis an end-of-sequence iterator, or both*thisandrighthave the same underlying sequence.-1- Returns:
trueif*thisandrightare both end-of-sequence iterators, or if […]bool operator!=(const regex_token_iterator& right) const;-?- Expects: At least one of
*thisandrightis an end-of-sequence iterator, or both*thisandrighthave the same underlying sequence.-2- Returns:
!(*this == right).
[2024-10-03; Jonathan rebases the wording on the latest WP]
Proposed resolution:
This wording is relative to N4988.
Modify 28.6.11.2.3 [re.tokiter.comp] as follows:
bool operator==(const regex_token_iterator& right) const;-?- Preconditions: At least one of
*thisandrightis an end-of-sequence iterator, or*thisandrighthave the same underlying sequence.-1- Returns:
trueif*thisandrightare both end-of-sequence iterators, or if*thisandrightare both suffix iterators andsuffix == right.suffix; otherwise returnsfalseif*thisorrightis an end-of-sequence iterator or a suffix iterator. Otherwise returnstrueifposition == right.position,N == right.N, andsubs == right.subs. Otherwise returnsfalse.
Section: 23.2.7 [associative.reqmts] Status: Open Submitter: Juan Soulie Opened: 2012-12-19 Last modified: 2019-04-23
Priority: 3
View other active issues in [associative.reqmts].
View all other issues in [associative.reqmts].
View all issues with Open status.
Discussion:
Table 102 in 23.2.7 [associative.reqmts]/8 states on expression a.key_comp() that it
"returns the comparison object out of which a was constructed". At the same time,
23.2.2 [container.requirements.general]/8 states (starting in the third line) that
"...Any Compare, Pred, or Hash objects belonging to a and b
shall be swappable and shall be exchanged by unqualified calls to non-member swap...". This is
problematic for any compliant implementation, since once swapped the container cannot return the comparison
object out of which it was constructed unless incurring in storing an otherwise needless object.
hasher
and key_equal members of unordered containers (they propagate), but it says nothing about stateful
comparison objects of (ordered) associative containers, except for the statement in
23.2.2 [container.requirements.general]/8 referred above and only related to swap.
For example, it is unclear to me what is specified to happen on an assignment: should the comparison object
be copied/moved along with the elements, or should the left-hand side object keep its own?
Maybe this has been intentionally left unspecified with the purpose of compatibility with C++98, which I
understand it specified that comparison objects were kept for the entire life of the container (like allocators)
— an unfortunate choice. But anyway, the segment of 23.2.2 [container.requirements.general] quoted
above seems to break any possible backwards compatibility with C++98 in this regard.
Therefore, taking into consideration consistency with how this is dealed with for unordered associative
containers, I propose that Table 102 is modified as follows:
The row for expression a.key_comp() is changed so that its "assertion/note pre-/post-condition" reads
"Returns a's comparison object."
A new row is added at the appropriate location (which I believe would be after "X(il)" row), with:
Table 102 — Associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity X(b)
X a(b)XCopy constructor. In addition to
the requirements of Table 96, copies
the comparison object.Linear in b.size()a = bX&Copy assignment operator. In addition to
the requirements of Table 96, copies the
comparison object.Linear in a.size()andb.size()
[2013-03-15 Issues Teleconference]
Moved to Review.
[2013-04-18, Bristol]
STL: can't believe we don't specify this already. this is totally necessary
Alisdair: how does it do this? copy construction? assignment? Also need it for move. STL: we already specify this for constructing from a comparator, not during copy construction though. Jonathan: don't like wording, should say "key_compare is CopyConstructible. Uses b.key_comp()
as a comparison object."
STL: we get it right for unordered!
Jonathan: can't wordsmith this now, but I think implementations do the right thing.
Alisdair: not sure what right thing is for moves. Also we say nothing about propagating allocators to functors.
Moved to Open.
[2015-02 Cologne]
TK: There's no need for fine-grained propagate/not-propagate control. If you don't want to propagate the predicate, you can simply construct or insert from an iterator range.
VV: libstdc++ already implements the resolution of this issue. GR: There are a couple of other problems. We don't specify move constructor and move assignment for maps. Those are just general. TK: General container requirements already describe the semantics for {copy,move}-{construction,assignment}, so it doesn't seem that there's room for choice instd::map assignments. unordered_map is different, though.
[Note: Check what general container requirements say about container equality.]
DK will draft wording. The decision is to unambiguously make all {copy,move}-{construction,assignment} operations endow the
LHS with the exact state of the RHS, including all predicates and hash function states.
Conclusion: Update wording, revisit later.
[2015-05-06 Lenexa: Waiting for updated wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N3485.
Change Table 102 as indicated:
Table 102 — Associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity …X(il)Same as X(il.begin(), il.end()).same as X(il.begin(), il.end()).X(b)
X a(b)Copy constructor. In addition to
the requirements of Table 96, copies
the comparison object.Linear in b.size()a = bX&Copy assignment operator. In addition to
the requirements of Table 96, copies the
comparison object.Linear in a.size()andb.size()…a.key_comp()X::key_comparerReturnsthea's comparison object
out of which a was constructed.constant
[2015-10-19 Daniel comments and provides alternative wording]
The current standard is especially unclear in regard to what effects move operations of unordered/associative containers should have. We have one example that is standardized exactly in this way by looking at 23.6.4.3 [priqueue.cons.alloc] p7:
template <class Alloc> priority_queue(priority_queue&& q, const Alloc& a);-7- Effects: Initializes
cwithstd::move(q.c)as the first argument andaas the second argument, and initializescompwithstd::move(q.comp)
A similarly comparable example are the move-operations of std::unique_ptr in regard to the deleter
(when this is no a reference), which also respect move-capabilities of that function object.
When an associative container is constructed by passing a comparison object the container shall not store a pointer or reference to the passed object, even if that object is passed by reference. When an associative container is copied, either through a copy constructor or an assignment operator, the target container shall then use the comparison object from the container being copied, as if that comparison object had been passed to the target container in its constructor.
The second sentence of this wording is problematic for several reasons:
It only talks about copy operations, not about move operations, except that the term "assignment" without leading "copy" is a bit ambigious (albeit it seems clear in the complete context).
It is not really clear how to interpret "as if that comparison object had been passed to the target container in its constructor" for an assignment operation. A possible but not conclusive interpretation could be that this is wording supporting a "copy-via-swap" idiom.
There does not exist similar wording for unordered containers, except that Table 102 provides entries for copy construction and copy assignment of the containers whose wording just talks of "copies" in either case.
Existing implementations differ already:
Visual Studio 2015 uses copy construction and copy assignment for the two copy operations but uses swap operations for the move operations.
GCC's libstdc++ performs copy construction and copy assignment for the two copy operations and for the two move operations, respectively
clang++'s libc++ performs copy/move construction and copy/move assignment for the corresponding four copy/move operations
The alternative wording provided below attempts to clarify that container copy/move operations perform the corresponding copy/move operations on the owned function objects.
In addition the wording also resolves LWG 2215(i): I believe that the current wording should require that container function objects should meet theCopyConstructible requirements. Adding
this general requirement also fixes the underspecified requirements of the accessor functions key_comp() and
value_comp().
I don't think that a general requirement for Swappable is needed, only the member swap function currently requires this.
Nonetheless the wording below does support stateful functors that are also moveable or move-assignable,
therefore the specified semantics in terms of move operations.
I should add the following warning, though: If this proposed wording would be accepted, there is a little chance of
code breakage, because the current wording can be read that in general there is no requirement that the
container functors are CopyConstructible. The following code example is accepted by gcc + libstd++:
#include <map>
#include <utility>
#include <iostream>
struct Cmp {
Cmp() = default;
Cmp(const Cmp&) = delete;
Cmp(Cmp&&) = delete;
Cmp& operator=(const Cmp&) = delete;
Cmp& operator=(Cmp&&) = delete;
template<class T>
bool operator()(const T& x, const T& y) const
{
return x < y;
}
};
typedef std::map<int, int, Cmp> MyMap;
int main() {
MyMap m;
std::cout << (m.find(12) == m.end()) << std::endl;
}
Previous resolution [SUPERSEDED]:
This wording is relative to N4527.
Change 23.2.7 [associative.reqmts] p8 as indicated:
-8- In Table 101,
Xdenotes an associative container class,adenotes a value of typeX,bdenotes a possiblyconstvalue of typeX,rvdenotes a non-constrvalue of typeX,udenotes the name of a variable being declared, […]Change Table 101 as indicated:
Table 101 — Associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity …X::key_compareCompareRequires: CompareisCopyConstructible.
defaults toless<key_type>compile time X(c)
X u(c);Requires:Effects: Constructs an empty container.key_compareisCopyConstructible.
Uses a copy ofcas a comparison object.[…] …X(i,j,c)
X u(i,j,c);Requires: key_compareisCopyConstructible.value_typeisEmplaceConstructibleintoXfrom*i.
Effects: Constructs an empty container and inserts elements
from the range[i, j)into it; usescas a comparison object.[…] …X(il)Same as X(il.begin(), il.end()).same as X(il.begin(), il.end()).X(b)
X a(b)(In addition to the requirements of Table 95)
Effects: Copy constructs the comparison object ofafrom
the comparison object ofb.Linear in b.size()X(rv)
X a(rv)(In addition to the requirements of Table 95 and Table 98)
Effects: Move constructs the comparison object ofafrom
the comparison object ofrv.constant a = bX&(In addition to the requirements of Table 95 and Table 98)
Requires:key_compareisCopyAssignable.
Effects: Copy assigns the comparison object ofb
to the comparison object ofa.Linear in a.size()andb.size()a = rvX&(In addition to the requirements of Table 95 and Table 98)
Requires:key_compareisMoveAssignable.
Effects: Move assigns from the comparison object ofrv
to the comparison object ofa.Linear …a.key_comp()X::key_comparerReturnsthea's comparison object
out of which a was constructed.constant Change 23.2.7 [associative.reqmts] p12 as indicated:
-12- When an associative container is constructed by passing a comparison object the container shall not store a pointer or reference to the passed object, even if that object is passed by reference.
When an associative container is copied, either through a copy constructor or an assignment operator, the target container shall then use the comparison object from the container being copied, as if that comparison object had been passed to the target container in its constructor.Change 23.2.8 [unord.req] p11 as indicated:
-11- In Table 102:
Xdenotes an unordered associative container class,adenotes a value of typeX,bdenotes a possiblyconstvalue of typeX,rvdenotes a non-constrvalue of typeX, […]Change Table 102 as indicated:
Table 102 — Unordered associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity …X::hasherHashRequires: HashisCopyConstructible.
Hashshall be a unary function object type
such that the expressionhf(k)has typestd::size_t.compile time X::key_equalPredRequires: PredisCopyConstructible.
Predshall be a binary predicate that takes
two arguments of typeKey.
Predis an equivalence relation.compile time …X(n, hf, eq)
X a(n, hf, eq)XRequires:Effects: […]hasherandkey_equalareCopyConstructible.[…] X(n, hf)
X a(n, hf)XRequires: hasherisCopyConstructibleandkey_equalisDefaultConstructible.
Effects: […][…] …X(i, j, n, hf, eq)
X a(i, j, n, hf, eq)XRequires: hasherandkey_equalareCopyConstructible.value_typeisEmplaceConstructibleintoXfrom*i.
Effects: […][…] X(i, j, n, hf)
X a(i, j, n, hf)XRequires: hasherisCopyConstructibleandkey_equalisDefaultConstructible.
value_typeisEmplaceConstructibleintoXfrom*i.
Effects: […][…] …X(b)
X a(b)XCopy constructor. In addition(In addition to the requirements of Table 95)
to the requirements of Table 95,
copies the hash function,
predicate, and maximum load
factor.
Effects: Copy constructs the hash function, predicate, and maximum load factor
ofafrom the corresponding objects ofb.Average case linear in
b.size(),
worst case quadratic.X(rv)
X a(rv)X(In addition to the requirements of Table 95 and Table 98)
Effects: Move constructs the hash function, predicate, and maximum load factor
ofafrom the corresponding objects ofrv.constant a = bX&Copy assignment operator. In(In addition to the requirements of Table 95 and Table 98)
addition to the requirements of
Table 95, copies the hash
function, predicate, and
maximum load factor.
Requires:hasherandkey_equalareCopyAssignable.
Effects: Copy assigns the hash function, predicate, and maximum load factor
ofbto the corresponding objects ofa.Average case linear in
b.size(),
worst case quadratic.a = rvX&(In addition to the requirements of Table 95 and Table 98)
Requires:hasherandkey_equalareMoveAssignable.
Effects: Move assigns the hash function, predicate, and maximum load factor
fromrvto the corresponding objects ofa.Linear …
[2016-08-07]
Daniel removes the previously proposed wording to work on revised wording.
[2019-04-22, Billy comments]
In addition to the Cpp17CopyConstructible discussion going on there, I think we need to require that
calling the comparison function when Compare itself is const needs to produce the same answer
as if Compare is non-const.
Proposed resolution:
kill_dependency unconditionally noexceptSection: 32.5.2 [atomics.syn], 32.5.4 [atomics.order] Status: SG1 Submitter: Daniel Krügler Opened: 2013-01-19 Last modified: 2025-10-20
Priority: 4
View other active issues in [atomics.syn].
View all other issues in [atomics.syn].
View all issues with SG1 status.
Discussion:
The "magic" kill_dependency function is a function without any constraints on the template parameter T
and is specified as
template <class T> T kill_dependency(T y) noexcept;-14- Effects: The argument does not carry a dependency to the return value (1.10).
-15- Returns:y.
I wonder whether the unconditional noexcept is really intended here:
Assume we have some type U that has a potentially throwing move
constructor (or it has a potentially throwing copy constructor and no
move constructor), for any "normal" function template with the same
signature and the same effects (modulo the dependency magic) this
would mean that it cannot safely be declared noexcept because of the
return statement being part of the complete function call affected by
noexcept (The by-value function argument is irrelevant in this
context). In other words it seems that a function call such as
struct S {
...
S(const S& r) { if(some condition) throw Something(); }
...
};
int main() {
S s1 = ...;
S s2 = std::kill_dependency(s1);
}
would be required to call std::terminate if the copy constructor of S throws during the return
of std::kill_dependency.
Make the exception-specification a constrained one in regard via std::is_nothrow_move_constructible:
template <class T> T kill_dependency(T y) noexcept(see below);
This is similar to the approach taken for function templates such as std::swap.
Use perfect forwarding (This needs further wording to correct the effects):
template <class T> T&& kill_dependency(T&& y) noexcept;
Impose constraints on the template arguments in regard to throwing exceptions while copying/moving.
Keep the state as it is but possibly add a note about a call of std::terminate in above scenario.
A second problem is that the current wording is not clear whether it is well-defined to call the function with types that are reference types, such as in the following example:
#include <atomic>
int main()
{
int a = 12;
int& b = std::kill_dependency<int&>(a);
}
It is unclear what kind of dependency is killed here. This is presumably a core language problem, but could affect the possible resolutions of the problem.
[2014-11 Urbana]
Recommend using a revised example:
int lookup(class D* p)
{
class E* q = p->a.load(memory_order_consume);
int y = std::kill_dependency(q->y);
}
[2015-02 Cologne]
Handed over to SG1.
[2025-10-20; kill_dependency is deprecated so set to priority 4]
Proposed resolution:
<cuchar> macrosSection: 27.5 [c.strings] Status: New Submitter: Jason Merrill Opened: 2013-01-29 Last modified: 2016-01-28
Priority: 4
View other active issues in [c.strings].
View all other issues in [c.strings].
View all issues with New status.
Discussion:
Apparently C1X changes __STDC_UTF_16__ and __STDC_UTF_32__ from macros
defined in uchar.h (and reflected in C++ by Table 79) to be predefined by the compiler.
Do we want to do the same?
Proposed resolution:
Section: 27.5 [c.strings] Status: Open Submitter: Johannes Schaub Opened: 2013-02-02 Last modified: 2016-08-09
Priority: 3
View other active issues in [c.strings].
View all other issues in [c.strings].
View all issues with Open status.
Discussion:
The non-explicit nature of the iterator-pair constructor of containers, such a
template <class InputIterator> vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
can be selected in unexpected situations, leading to a hard runtime error, as demonstrated by the following example:
#include <vector>
void f(std::vector<char> v){ /* ... */}
int main() {
f({"A", "B"});
}
The actually intended initializer-list constructor isn't feasible here, so the best match is the constructor template
template <class InputIterator> vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
This compiles, but will result in code running amok. The potential trap (that cannot be easily detected by the library implementation) could be reduced by making this constructor explicit. It would still have the effect to be selected here, but the code would be ill-formed, so the programmer gets a clear message here.
[2014-06 Rapperswil]
JW: can't fix this, don't want to touch this, Do The Right Thing clause has been a source of tricky issues. only really happens with string literals, that's the only way to create an array that isn't obviously an array
GR: want to see paper AM: is it only string literals, or also UDLs? STL: maybe, but we don't need to deal with that. This is only a problem in a very specific case Leave as Open.Proposed resolution:
numeric_limits::is_iec559 misnamedSection: 17.3.5 [numeric.limits] Status: New Submitter: Pete Becker Opened: 2013-03-08 Last modified: 2018-11-08
Priority: 4
View other active issues in [numeric.limits].
View all other issues in [numeric.limits].
View all issues with New status.
Discussion:
This member should probably be named "is_ieee754". Or at least the standard should explain that IEC-559 no longer exists, and that it's been superseded by IEEE-754.
[2016-06, Oulu]
The ISO version of the standard is ISO/IEC/IEEE 60559:2011,
which C11 Annex F refers to as IEC 60559
(although C still refers to it as IEC 559 in the __STDC_IEC_559__ macro).
Proposed resolution:
unique_ptr<T>::get_deleter()(p) to be able to destroy the unique_ptrSection: 20.3.1.3 [unique.ptr.single] Status: Open Submitter: Rob Desbois Opened: 2013-05-15 Last modified: 2017-03-21
Priority: 3
View other active issues in [unique.ptr.single].
View all other issues in [unique.ptr.single].
View all issues with Open status.
Discussion:
N3337 20.3.1.3.6 [unique.ptr.single.modifiers] contains 2 non-normative notes stating:
[para 4]: "The order of these operations is significant because the call to
get_deleter()may destroy*this."[para 5]: "The postcondition does not hold if the call to
get_deleter()destroys*thissincethis->get()is no longer a valid expression."
It seems this wording was created to resolve 998(i) due to the possibility that a unique_ptr may be
destroyed through deletion of its stored pointer where that directly or indirectly refers to the same unique_ptr.
If unique_ptr is required to support circular references then it seems this must be normative text: an implementation
is currently allowed to operate on *this after the assignment and deletion specified in para 4, since this is only
'disallowed' by the non-normative note.
I propose the following draft rewording:
[para 4]: Effects: assigns p to the stored pointer, and then if the old value of the stored pointer, old_p, was not
equal to nullptr, calls get_deleter()(old_p). No operation shall be performed after the call to
get_deleter()(old_p) that requires *this to be valid, because the deletion may destroy *this if it is
referred to directly or indirectly by the stored pointer. [Note: The order of these operations is significant
because the call to
get_deleter() may destroy *this. — end note]
get_deleter()(old_p) destroyed *this, none. Otherwise,
get() == p. get_deleter()
destroys *this since this->get() is no longer a valid expression. — end note]I expect it will also be necessary to amend the requirements for a deleter, so in addition:
20.3.1.3 [unique.ptr.single] [para 1]: The default type for the template parameter D is default_delete.
A client-supplied template argument D shall be a function object type (20.10), lvalue-reference to function, or
lvalue-reference to function object type for which, given a value d of type D and a value ptr of type
unique_ptr<T, D>::pointer, the expression d(ptr) is valid and has the effect of disposing of the pointer
as appropriate for that deleter. Where D is not an lvalue reference type, d(ptr) shall be valid if ptr
refers directly or indirectly to the invoking unique_ptr object.
[2013-10-05, Stephan T. Lavavej comments and provides alternative wording]
In Chicago, we determined that the original proposed change to 20.3.1.3 [unique.ptr.single]/1 was insufficient, because
d might be a reference to a deleter functor that's destroyed during self-destruction.
X from doing various things, through the principle of "nothing allows X to fail in that situation".
For example, v.push_back(v[0]) is required to work for non-empty vectors because nothing allows that to fail. In this case,
the intent to allow self-destruction is already clear.
Additionally, we did not believe that 20.3.1.3.6 [unique.ptr.single.modifiers]/5 had to be changed. The current note is slightly
squirrely but it does not lead to confusion for implementers or users.
Previous resolution from Rob Desbois:
Edit 20.3.1.3 [unique.ptr.single] p1 as indicated:
The default type for the template parameter
Disdefault_delete. A client-supplied template argumentDshall be a function object type (20.10), lvalue-reference to function, or lvalue-reference to function object type for which, given a valuedof typeDand a valueptrof typeunique_ptr<T, D>::pointer, the expressiond(ptr)is valid and has the effect of disposing of the pointer as appropriate for that deleter. WhereDis not an lvalue reference type,d(ptr)shall be valid ifptrrefers directly or indirectly to the invokingunique_ptrobject.Edit 20.3.1.3.6 [unique.ptr.single.modifiers] p4+5 as indicated:
void reset(pointer p = pointer()) noexcept;-3- Requires: The expression
-4- Effects: assignsget_deleter()(get())shall be well formed, shall have well-defined behavior, and shall not throw exceptions.pto the stored pointer, and then if the old value of the stored pointer,old_p, was not equal tonullptr, callsget_deleter()(old_p). No operation shall be performed after the call toget_deleter()(old_p)that requires*thisto be valid, because the deletion may destroy*thisif it is referred to directly or indirectly by the stored pointer.[Note: The order of these operations is significant because the call to-5- Postconditions: If the callget_deleter()may destroy*this. — end note]get_deleter()(old_p)destroyed*this, none. Otherwise,get() == p.[Note: The postcondition does not hold if the call toget_deleter()destroys*thissincethis->get()is no longer a valid expression. — end note]
Previous resolution [SUPERSEDED]:
This wording is relative to N3691.
Edit 20.3.1.3 [unique.ptr.single] p1 as indicated:
The default type for the template parameter
Disdefault_delete. A client-supplied template argumentDshall be a function object type (20.10), lvalue-reference to function, or lvalue-reference to function object type for which, given a valuedof typeDand a valueptrof typeunique_ptr<T, D>::pointer, the expressiond(ptr)is valid and has the effect of disposing of the pointer as appropriate for that deleter.d(ptr)shall be valid even if it triggers the destruction ofdor (ifDis an lvalue reference to function object type) the function object thatdrefers to.
[2015-05, Lenexa]
After some discussion in Lenexa there was some wavering on if the added sentence is necessary. Here is example code that
demonstrates why the extra sentence is necessary. In this example the call to d(ptr) is valid, however the deleter
references *this after destructing its element:
#include <cassert>
#include <memory>
#include <iostream>
class Deleter
{
int state_ = 0;
enum
{
destructed = -4,
self_move_assigned = -3,
move_assigned_from = -2,
move_constructed_from = -1
};
public:
~Deleter() {state_ = destructed;}
Deleter() = default;
Deleter(Deleter const&) = default;
Deleter& operator=(Deleter const&) = default;
Deleter(Deleter&& a) noexcept
: state_(a.state_)
{a.state_ = move_constructed_from;}
Deleter& operator=(Deleter&& a) noexcept
{
if (this == &a)
state_ = self_move_assigned;
else
{
state_ = a.state_;
a.state_ = move_assigned_from;
}
return *this;
}
Deleter(int state)
: state_(state)
{
assert(state >= 0);
}
template <class T>
void
operator()(T* t) const
{
std::cout << "Deleter beginning operator()(T*)\n";
std::cout << "The deleter = " << *this << '\n';
std::cout << "Deleter about to destruct the X.\n";
delete t;
std::cout << "Deleter has destructed the X.\n";
std::cout << "The deleter = " << *this << '\n';
std::cout << "Deleter ending operator()(T*)\n";
}
friend
std::ostream&
operator<<(std::ostream& os, const Deleter& a)
{
switch (a.state_)
{
case destructed:
os << "**destructed**";
break;
case self_move_assigned:
os << "self_move_assigned";
break;
case move_assigned_from:
os << "move_assigned_from";
break;
case move_constructed_from:
os << "move_constructed_from";
break;
default:
os << a.state_;
break;
}
return os;
}
};
struct X
{
Deleter deleter_{1};
};
int main()
{
auto xp = new X;
{
std::unique_ptr<X, Deleter&> p(xp, xp->deleter_);
std::cout << "unique_ptr is constructed.\n";
std::cout << "The deleter = " << p.get_deleter() << '\n';
std::cout << "Destructing unique_ptr...\n";
}
std::cout << "unique_ptr is destructed.\n";
}
Which outputs:
unique_ptr is constructed. The deleter = 1 Destructing unique_ptr... Deleter beginning operator()(T*) The deleter = 1 Deleter about to destruct the X. Deleter has destructed the X. The deleter = **destructed** Deleter ending operator()(T*) unique_ptr is destructed.
The line "The deleter = **destructed**" represents the deleter referencing itself after it has been destructed by the
d(ptr) expression, but prior to that call returning.
The expression
d(ptr)shall not refer to the objectdafter it executesptr->~T().
[2015-07, Telecon]
Geoffrey: Deleter may or may not execute ~T().
Alisdair: After the destructor after the element has run. Say it in words instead of code.
Howard will provide updated wording. Perhaps need both normative and non-normative wording.
[2015-08-03, Howard updates P/R per telecon discussion.]
[2017-03-04, Kona]
This is related to 2751(i), which has been suggested NAD.
STL wants "Effects equivalent to" here - say it in code. Marshall to research.
Proposed resolution:
This wording is relative to N4431.
Edit 20.3.1.3 [unique.ptr.single] p1 as indicated:
The default type for the template parameter
Disdefault_delete. A client-supplied template argumentDshall be a function object type (20.9), lvalue-reference to function, or lvalue-reference to function object type for which, given a valuedof typeDand a valueptrof typeunique_ptr<T, D>::pointer, the expressiond(ptr)is valid and has the effect of disposing of the pointer as appropriate for that deleter. The expressiond(ptr), if it destructs the object referred to byptr, shall not refer to the objectdafter it destructs*ptr. [Note: The object being destructed may control the lifetime ofd. — end note]
Section: 32.5.4 [atomics.order] Status: Open Submitter: Brian Demsky Opened: 2013-06-17 Last modified: 2016-01-28
Priority: 4
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with Open status.
Discussion:
I believe that the following variation on IRIW should admit executions in
which c1 = d1 = 5 and c2 = d2 = 0. If this is allowed, then what is sequence of
program evaluations for 32.5.4 [atomics.order] p9 that justifies the store to z? It seems that
32.5.4 [atomics.order] p9 should not allow this execution because one of the stores to x or y has
to appear earlier in the sequence, each of the fetch_adds reads the previous load in the thread (and thus must
appear later in the sequence), and 32.5.4 [atomics.order] p9 states that each load must read from the last prior
assignment in the sequence.
atomic_int x;
atomic_int y;
atomic_int z;
int c1, c2, d1, d2;
static void a(void* obj)
{
atomic_store_explicit(&x, 5, memory_order_relaxed);
}
static void b(void* obj)
{
atomic_store_explicit(&y, 5, memory_order_relaxed);
}
static void c(void* obj)
{
c1 = atomic_load_explicit(&x, memory_order_relaxed);
// this could also be an atomic load if the address depends on c1:
c2 = atomic_fetch_add_explicit(&y, c1, memory_order_relaxed);
}
static void d(void* obj)
{
d1 = atomic_load_explicit(&y, memory_order_relaxed);
d2 = atomic_fetch_add_explicit(&x, d1, memory_order_relaxed);
}
int user_main(int argc, char** argv)
{
thrd_t t1, t2, t3, t4;
atomic_init(&x, 0);
atomic_init(&y, 0);
printf("Main thread: creating 4 threads\n");
thrd_create(&t1, (thrd_start_t)&a, NULL);
thrd_create(&t2, (thrd_start_t)&b, NULL);
thrd_create(&t3, (thrd_start_t)&c, NULL);
thrd_create(&t4, (thrd_start_t)&d, NULL);
thrd_join(t1);
thrd_join(t2);
thrd_join(t3);
thrd_join(t4);
printf("c1=%d c2=%d\n",c1,c2);
printf("d1=%d d2=%d\n",d1,d2);
// Can this store write 1000 (i.e., c1=d1=5, c2=d2=0)?
atomic_store(&z, (c1+d1)*100+c2+d2);
printf("Main thread is finished\n");
return 0;
}
It seems that the easiest fix is to allow a load in 32.5.4 [atomics.order] p9 to read from any prior store in the evaluation order.
That said, I would personally advocate the following: It seems to me that C/C++ atomics are in a bit of different situation than Java because:People are expected to use relaxed C++ atomics in potentially racy situations, so it isn't clear that semantics as complicated as the JMM's causality would be sane.
People who use C/C++ atomics are likely to be experts and use them in a very controlled fashion. I would be really surprised if compilers would find any real wins by optimizing the use of atomics.
Why not do something like:
There is satisfaction DAG of all program evaluations. Each evaluation observes the values of variables as computed by some prior assignment in the DAG. There is an edgex->y between two evaluations x and y if:
the evaluation y observes a value computed by the evaluation x or
the evaluation y is an atomic store, the evaluation x is an atomic load, and
there is a condition branch c that may depend (intrathread dependence) on x
and x-sb->c and c-sb->y.
This seems to allow reordering of relaxed atomics that processors do without extra fence instructions, allows most reorderings by the compiler, and gets rid of satisfaction cycles.
[2015-02 Cologne]
Handed over to SG1.
[2015-05 Lenexa, SG1 response]
This was partially addressed (weasel-worded) in C++14 (See N3786). The remainder is an open research problem. N3710 outlines a "solution" that doesn't have a consensus behind it because it costs performance. We have no better solution at the moment.
Proposed resolution:
partial_sort_copy underspecified for ranges of two different typesSection: 26.8.2.4 [partial.sort.copy] Status: New Submitter: Matt Austern Opened: 2013-06-26 Last modified: 2016-01-28
Priority: 3
View all issues with New status.
Discussion:
The signature of this function is:
template<class InputIterator, class RandomAccessIterator>
RandomAccessIterator
partial_sort_copy(InputIterator first, InputIterator last,
RandomAccessIterator result_first,
RandomAccessIterator result_last);
(and the usual overload for an explicitly provided comparison function). The standard says nothing about requirements
in the case where the input type (iterator_traits<InputIterator>::value_type) and the output type
(iterator_traits<RandomAccessIterator>::value_type) are different.
Proposed resolution:
Section: 23.2.2 [container.requirements.general] Status: New Submitter: Matt Austern Opened: 2013-06-26 Last modified: 2016-01-28
Priority: 4
View other active issues in [container.requirements.general].
View all other issues in [container.requirements.general].
View all issues with New status.
Discussion:
Consider the following code snippet:
#include <vector>
#include <algorithm>
int main() {
std::vector<int> v1(100, 3);
std::vector<int> v2(100);
copy(v1.begin(), v1.end(), v2.begin());
}
It compiles without error on my desktop. Is it required to? I can't find evidence from the standard that it is.
In my test std::copy was found by argument-dependent lookup because the implementation I used made
std::vector<int>::iterator a user-defined type defined in namespace std. But the standard
only requires std::vector<int>::iterator to be an implementation specified random access iterator
type. I can't find anything requiring it to be a user-defined type at all (and in fact there are reasonable implementation
where it isn't), let alone a user defined type defined in a specific namespace.
Since the defining namespace of container iterators is visible to users, should the standard say anything about what that namespace is?
Proposed resolution:
stringbuf::underflow() underspecifiedSection: 31.8.2.5 [stringbuf.virtuals] Status: Open Submitter: Sergey Zubkov Opened: 2013-08-29 Last modified: 2018-06-12
Priority: 4
View other active issues in [stringbuf.virtuals].
View all other issues in [stringbuf.virtuals].
View all issues with Open status.
Discussion:
In 31.8.2.5 [stringbuf.virtuals]/1, basic_stringbuf::underflow() is specified to unconditionally
return traits::eof() when a read position is not available.
basic_stringbuf require, and existing libraries implement it so that this function makes
a read position available if possible to do so, e.g. if some characters were inserted into the stream since the
last call to overflow(), resulting in pptr() > egptr(). Compare to the conceptually similar
99 [depr.strstreambuf.virtuals]/15.
[2018-06-06, Billy argues for NAD]
The existing "Any character in the underlying buffer which has been initialized is considered to be part of the input sequence."
sentence already describes what the stringbuf is supposed to do to the get area. The specific mechanism that the
stringbuf uses to alter the get area is unspecified because the mechanism by which the stringbuf
remembers the "high water mark" is unspecified.
stringstream s; s << "Hello"; s.seekp(0); string x; s >> x;
Before this P/R, this will store Hello in x, because the characters Hello are initialized.
After this P/R, the "written put area" is empty, so it will store the empty string in x.
[2018-06 Rapperswil Wednesday issues processing]
Billy to provide rationale for closing as NAD.
Proposed resolution:
This wording is relative to N3691.
Change 31.8.2.5 [stringbuf.virtuals] as indicated:
int_type underflow();-1- Returns: If the input sequence has a read position available or the function makes a read position available (as described below), returns
-?- The function can make a read position available only iftraits::to_int_type(*gptr()). Otherwise, returnstraits::eof(). Any character in the underlying buffer which has been initialized is considered to be part of the input sequence.(mode & ios_base::in) != 0and if the write next pointerpptr()is not null and is greater than the current read end pointeregptr(). To make a read position available, the function alters the read end pointeregptr()to equalpptr().
constexpr guarantees of defaulted functions still insufficientSection: 22.3.2 [pairs.pair], 22.4.4.2 [tuple.cnstr], 30.5 [time.duration] Status: Open Submitter: Daniel Krügler Opened: 2013-09-09 Last modified: 2020-06-13
Priority: 3
View other active issues in [pairs.pair].
View all other issues in [pairs.pair].
View all issues with Open status.
Discussion:
During the acceptance of N3471 and
some similar constexpr papers, specific wording was added to pair, tuple, and other templates
that were intended to impose implementation constraints that ensure that the observable constexpr "character"
of a defaulted function template is solely determined by the required expressions of the user-provided types when instantiated,
for example:
The defaulted move and copy constructor, respectively, of pair shall be a
constexprfunction if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for aconstexprfunction.
This wording doesn't require enough, especially since the core language via CWG 1358 does now support constexpr
function template instantiations, even if such function cannot appear in a constant expression (as specified in 7.7 [expr.const])
or as a constant initializer of that object (as specified in [basic.start.init]). The wording should be
improved and should require valid uses in constant expressions and as constant initializers instead.
[Lenexa 2015-05-05]
STL : notice order of move/copy and copy/move with "respectively".
General word-smithing; ask for updated wording
Are we happy with this with changes we are suggesting?
unanimous
[2016-12-14, Daniel comments]
LWG 2833(i) overlaps considerably and both should be resolved together.
Previous resolution from Daniel [SUPERSEDED]:This wording is relative to N3691.
Change 22.3.2 [pairs.pair] p2 as indicated:
-2-
The defaulted move and copy constructor, respectively, of pair shall be aAn invocation of the move or copy constructor ofconstexprfunction if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for aconstexprfunctionpairshall be a constant expression (7.7 [expr.const]) if all required element-wise initializations would be constant expressions. An invocation of the move or copy constructor ofpairshall be a constant initializer for thatpairobject ( [basic.start.init]) if all required element-wise initializations would be constant initializers for the respective subobjects.Change 22.4.4.2 [tuple.cnstr] p2 as indicated:
-2-
The defaulted move and copy constructor, respectively, ofAn invocation of the move or copy constructor oftupleshall be aconstexprfunction if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for aconstexprfunction. The defaulted move and copy constructor oftuple<>shall beconstexprfunctionstupleshall be a constant expression (7.7 [expr.const]) if all required element-wise initializations would be constant expressions. An invocation of the move or copy constructor oftupleshall be a constant initializer for thattupleobject ( [basic.start.init]) if all required element-wise initializations would be constant initializers for the respective subobjects. An invocation of the move or copy constructor oftuple<>shall be a constant expression, or a constant initializer for thattuple<>object, respectively, if the function argument would be constant expression.Change 30.5 [time.duration] p7 as indicated:
-7- Remarks:
The defaulted copy constructor of duration shall be aAn invocation of the copy constructor ofconstexprfunction if and only if the required initialization of the memberrep_for copy and move, respectively, would satisfy the requirements for aconstexprfunction.durationshall be a constant expression (7.7 [expr.const]) if the required initialization of the memberrep_would be a constant expression. An invocation of the copy constructor ofdurationshall be a constant initializer for thatdurationobject ( [basic.start.init]) if the required initialization of the memberrep_would be constant initializers for this subobject.
[2020-06-08 Nina Dinka Ranns comments]
The revised wording provided by LWG 2833(i) should resolve this issue as well.
Proposed resolution:
Section: 21 [meta] Status: Open Submitter: Daniel Krügler Opened: 2013-09-02 Last modified: 2016-01-28
Priority: 3
View other active issues in [meta].
View all other issues in [meta].
View all issues with Open status.
Discussion:
The current library specification uses at several places wording that is intended to refer to core language template deduction failure at the top-level of expressions (aka "SFINAE"), for example:
The expression
declval<T>() = declval<U>()is well-formed when treated as an unevaluated operand (Clause 5). Access checking is performed as if in a context unrelated toTandU. Only the validity of the immediate context of the assignment expression is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]
Similar wording can be found in the specification of result_of, is_constructible, and is_convertible,
being added to resolve an NB comment by LWG 1390(i) and 1391(i) through
N3142.
[2014-05-19, Daniel suggests a descriptive term]
constrictedly well-formed expression:
An expression e depending on a set of typesA1, ..., An which is well-formed when treated as
an unevaluated operand (Clause 5). Access checking is performed as if in a context unrelated to A1, ...,
An. Only the validity of the immediate context of e is considered. [Note: The compilation of
the expression can result in side effects such as the instantiation of class template specializations and function
template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the
"immediate context" and can result in the program being ill-formed. — end note]
[2014-05-20, Richard and Jonathan suggest better terms]
Richard suggested "locally well-formed"
Jonathan suggested "contextually well-formed" and then "The expression ... is valid in a contrived argument deduction context"[2014-06-07, Daniel comments and improves wording]
The 2014-05-19 suggestion did only apply to expressions, but there are two important examples that are not expressions, but instead
are involving an object definition (std::is_constructible) and a function definition
(std::is_convertible), respectively, instead. Therefore I suggest to rephrase the usage of "expression" into "program
construct" in the definition of Jonathan's suggestion of "valid in a contrived argument deduction context".
**Insertable,
EmplaceConstructible, and Erasable definitions in 23.2.2 [container.requirements.general], but given that
these are not fully described in terms of the aforementioned wording yet, I would recommend to fix them by a separate issue
once the committee has agreed on following the suggestion presented by this issue.
[2015-05-05 Lenexa: Move to Open]
...
MC: I think we like the direction but it isn't quite right: it needs some work
JW: I'm prepared to volunteer to move that further, hopefully with the help of Daniel
Roger Orr: should this be Core wording because it doesn't really have anything to do with libraries - the term could then just be used here
AM: Core has nothing to deal with that, though
HT: it seems there is nothing to imply that allows dropping out with an error - maybe that's a separate issue
MC: I'm not getting what you are getting at: could you write an issue? - any objection to move to Open?
...
Proposed resolution:
This wording is relative to N3936.
Add the following new definition to [definitions] as indicated:
valid in a contrived argument deduction context [defns.valid.contr.context]
A program construct c depending on a set of typesA1, ..., An, and treated as
an unevaluated operand (Clause 5) when c is an expression, which is well-formed.
Access checking is performed as if in a context unrelated to A1, ..., An.
Only the validity of the immediate context (13.10.3 [temp.deduct]) of c is considered.
[Note: The compilation of c can result in side effects such as the instantiation of class template
specializations and function template specializations, the generation of implicitly-defined functions, and so on.
Such side effects are not in the "immediate context" and can result in the program being ill-formed. —
end note].
Change Table 49 ("Type property predicates") as indicated:
Table 49 — Type property predicates Template Condition Preconditions …template <class T, class U>
struct is_assignable;The expression declval<T>() =is valid in a
declval<U>()
contrived argument deduction context
([defns.valid.contr.context]) for types
TandU.well-formed when treated
as an unevaluated operand
(Clause 5). Access
checking is performed as if
in a context unrelated toT
andU. Only the validity of
the immediate context of
the assignment expression
is considered. [Note: The
compilation of the
expression can result in
side effects such as the
instantiation of class
template specializations
and function template
specializations, the
generation of
implicitly-defined
functions, and so on. Such
side effects are not in the
"immediate context" and
can result in the program
being ill-formed. — end
note][…] …
Change 21.3.6.4 [meta.unary.prop] p7 as indicated:
-7- Given the following function prototype:
template <class T> add_rvalue_reference_t<T> create() noexcept;the predicate condition for a template specialization
is_constructible<T, Args...>shall be satisfied if and only if the following variable definitionwould be well-formedfor some invented variabletwould be valid in a contrived argument deduction context ([defns.valid.contr.context]) for typesTandArgs...:T t(create<Args>()...);[Note: These tokens are never interpreted as a function declaration. — end note]
Access checking is performed as if in a context unrelated toTand any of theArgs. Only the validity of the immediate context of the variable initialization is considered. [Note: The evaluation of the initialization can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]
Change Table 57 ("Other transformations") as indicated:
Table 57 — Other transformations Template Condition Comments …template <class Fn, class... ArgTypes>
struct result_of<Fn(ArgTypes...)>;[…] If the expression
INVOKE(declval<Fn>(),is
declval<ArgTypes>()...)
valid in a contrived argument deduction
context ([defns.valid.contr.context]) for types
FnandArgTypes...well, the
formed when treated as an
unevaluated operand (Clause 5)
member typedef type shall name the
type
decltype(INVOKE(declval<Fn>(),;
declval<ArgTypes>()...))
otherwise, there shall be no member
type.Access checking is performed as
if in a context unrelated toFnand
ArgTypes. Only the validity of the
immediate context of the expression is
considered. [Note: The compilation of
the expression can result in side
effects such as the instantiation of
class template specializations and
function template specializations, the
generation of implicitly-defined
functions, and so on. Such side effects
are not in the "immediate context"
and can result in the program being
ill-formed. — end note]…
Change 21.3.8 [meta.rel] p4 as indicated:
-4- Given the following function prototype:
template <class T> add_rvalue_reference_t<T> create() noexcept;the predicate condition for a template specialization
is_convertible<From, To>shall be satisfied if and only if the return expression in the following code would bewell-formedvalid in a contrived argument deduction context ([defns.valid.contr.context]) for typesToandFrom, including any implicit conversions to the return type of the function:To test() { return create<From>(); }[Note: This requirement gives well defined results for reference types,
voidtypes, array types, and function types. — end note]Access checking is performed as if in a context unrelated toToandFrom. Only the validity of the immediate context of the expression of the return-statement (including conversions to the return type) is considered. [Note: The evaluation of the conversion can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]
std::vector<UserType> broken?Section: 17.6.3.4 [new.delete.placement] Status: New Submitter: Daniel Krügler Opened: 2013-09-18 Last modified: 2016-01-28
Priority: 3
View all other issues in [new.delete.placement].
View all issues with New status.
Discussion:
The library gives explicit permission in 16.4.5.2.1 [namespace.std] p2 that user code may explicitly instantiate a library template provided that the instantiations depend on at least one user-defined type:
A program may explicitly instantiate a template defined in the standard library only if the declaration depends on the name of a user-defined type and the instantiation meets the standard library requirements for the original template.
But it seems that the C++11 library is not specified in a way that guarantees such an instantiation to be well-formed if the minimum requirements of the library is not satisfied.
For example, in general, the first template parameter ofstd::vector is not required to be
DefaultConstructible in general, but due to the split of the single C++03 member function
with default argument
void resize(size_type sz, T c = T());
into
void resize(size_type sz); void resize(size_type sz, const T& c);
the effect is now that for a type ND that is not DefaultConstructible, such as
struct NP {
NP(int);
};
the explicit instantiation of std::vector<ND> is no longer well-formed, because the attempt to
instantiate the single-argument overload of resize cannot not succeed, because this function imposes
the DefaultInsertable requirements and given the default allocator this effectively requires
DefaultConstructible.
But DefaultConstructible is not the only point, what about CopyConstructible versus
MoveConstructible alone? It turns out that currently the second resize overload
would fail during an explicit instantiation for a type like
struct MO {
MO() = default;
MO(MO&&) = default;
};
because it imposes CopyInsertable requirements that end up being equivalent to the CopyConstructible
requirements for the default allocator.
resize functions of std::vector could be prevented from instantiation by defining them like this
with an implementation:
template<class = void>
void resize(size_type sz) { […] }
template<class = void>
void resize(size_type sz, const T& c) { […] }
In this case, these functions could also be defined in a base class, but the latter approach won't work in all cases.
Basically such an implementation is required to constrain all member functions that are not covered by the general requirements imposed on the actual library template parameters. I tested three different C++11 library implementations and but none could instantiate for examplestd::list, std::vector, or std::deque with
value types that are not DefaultConstructible or only MoveConstructible.
This issue is raised to clarify the current situation in regard to the actual requirements imposed on user-provided
types that are used to explicitly instantiate Library-provided templates. For example, the current Container requirements
impose very little requirements on the actual value type and it is unclear to which extend library implementations have
to respect that.
The minimum solution of this issue should be to at least realize that there is no fundamental requirement on
DefaultConstructible for value types of library containers, because we have since C++03 the general
statement of 16.4.4.2 [utility.arg.requirements] ("In general, a default constructor is not required.").
It is unclear whether CopyConstructible should be required for an explicit instantiation request, but
given the careful introduction of move operations in the library it would seem astonishing that a
MoveConstructible type wouldn't suffice for value types of the container types.
In any case I can envision at least two approaches to solve this issue:
As indicated in LWG 2292(i), those function could get an explicit "Template Constraints:" element, albeit this promises more than needed to solve this issue.
The library could introduce a completely new element form, such as "Instantiation Constraints:" that
would handle this situation for explicit instantiation situations. This would allow for simpler techniques
to solve the issue when explicit instantiation is required compared to the first bullet, because it would not
(necessarily) guarantee SFINAE-friendly expression-wellformedness, such as inspecting the expression
std::declval<std::vector<ND>&>.resize(0) in an unevaluated context.
It should be noted that the 2013-08-27 comment to LWG 2193(i) could be resolved by a similar solution as indicated in this issue here.
Proposed resolution:
explicit only when necessary?Section: 23 [containers] Status: LEWG Submitter: Zhihao Yuan Opened: 2013-09-26 Last modified: 2018-11-12
Priority: 2
View other active issues in [containers].
View all other issues in [containers].
View all issues with LEWG status.
Discussion:
LWG 2193(i) yields explicit for default ctors to allow {}, but not for
all cases of uniform initialization. For example:
explicit vector(size_type count, const Allocator& alloc = Allocator());
This prevents {n, alloc()}. Although this use is relatively rare,
but the behavior is inconsistent with that of
vector(size_type count, const T& value, const Allocator& alloc = Allocator());
[Urbana 2014-11-07: Move to Open]
[2018-08 Batavia Monday issue discussion]
This really needs a paper; splitting a lot of constructors. Nevin to write paper.
[2018-11 San Diego Thursday night issue processing]
LEWG has rejected Nevin's paper, so they need to formulate a policy.
Proposed resolution:
Section: 23.2.2 [container.requirements.general] Status: Open Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2023-01-20
Priority: 3
View other active issues in [container.requirements.general].
View all other issues in [container.requirements.general].
View all issues with Open status.
Discussion:
23.2.2 [container.requirements.general]/10 says that unless otherwise specified, "no swap() function invalidates
any references, pointers, or iterators referring to the elements of the containers being swapped. [Note: The end()
iterator does not refer to any element, so it may be invalidated. — end note]". However, move constructors and move
assignment operators aren't given similar invalidation guarantees. The guarantees need several exceptions, so I do not believe
that blanket language like /11 "Unless otherwise specified (either explicitly or by defining a function in terms of other functions),
invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to,
or change the values of, objects within that container." is applicable.
[2014-02-13 Issaquah]
General agreeement on intent, several wording nits and additional paragraphs to hit.
STL to provide updated wording. Move to Open.
[2015-02 Cologne]
AM: in the proposed wording, I'd like to mention that the iterators now refer to elements of a different container.
I think we're saying something like this somewhere. JY: There's some wording like that for swap I think. TK: It's also in
list::splice(). DK to JY: 23.2.1p9.
[2015-06, Telecon]
Still waiting for updated wording
[2015-08 Chicago]
Still waiting for updated wording
[2018-08-23 Batavia Issues processing]
Priority to 3
[2023-01-20; std-proposals post]
Emile Cormier
observed
that the proposed resolution of this issue contradicts with changes made by
LWG 2839(i). Specifially, the current draft does not require
container elements to be preserved on self-move-assignment.
If this issue is accepted, it would either need to allow
iterator invalidation on self-move-assignment or remove the
"If a and rv do not refer to the same object"
changes added to the container requirements by LWG 2839(i).
Proposed resolution:
This wording is relative to N3691.
In 23.2.2 [container.requirements.general]/10 change as indicated:
-10- Unless otherwise specified (see 23.2.4.1, 23.2.5.1, 23.3.3.4, and 23.3.7.5) all container types defined in this Clause meet the following additional requirements:
[…]
no copy constructor or assignment operator of a returned iterator throws an exception.
no move constructor (or move assignment operator when
allocator_traits<allocator_type>::propagate_on_container_move_assignment::valueis true) of a container (except forarray) invalidates any references, pointers, or iterators referring to the elements of the source container. [Note: Theend()iterator does not refer to any element, so it may be invalidated. — end note]no
swap()function throws an exception.no
swap()function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. [Note: Theend()iterator does not refer to any element, so it may be invalidated. — end note]
regex_constants::collate's effects are inaccurately summarizedSection: 28.6.4.2 [re.synopt] Status: Open Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2016-01-28
Priority: 3
View all other issues in [re.synopt].
View all issues with Open status.
Discussion:
The table in 28.6.4.2 [re.synopt]/1 says that regex_constants::collate "Specifies that character ranges of the form
"[a-b]" shall be locale sensitive.", but 28.6.12 [re.grammar]/14 says that it affects individual character comparisons
too.
[2012-02-12 Issaquah : recategorize as P3]
Marshall Clow: 28.13/14 only applies to ECMAScript
All: we're unsure
Jonathan Wakely: we should ask John Maddock
Move to P3
[2014-5-14, John Maddock response]
The original intention was the original wording: namely that collate only made character ranges locale sensitive.
To be frank it's a feature that's probably hardly ever used (though I have no real hard data on that), and is a leftover
from early POSIX standards which required locale sensitive collation for character ranges, and then later changed
to implementation defined if I remember correctly (basically nobody implemented locale-dependent collation).
Z < a in that locale."
ECMAScript doesn't include collation at all.
IMO, +1 to changing 28.13 instead of 28.5.1. It seems like we'd be on
fairly solid ground if we wanted to remove regex_constants::collate
entirely, in favor of named character classes, but of course that's
not for this issue.
Proposed resolution:
This wording is relative to N3691.
In 28.6.4.2 [re.synopt]/1, Table 138 — "syntax_option_type effects", change as indicated:
Table 138 — syntax_option_typeeffectsElement Effect(s) if set …collateSpecifies that character ranges of the form "comparisons and character range comparisons shall be locale sensitive.[a-b]"…
Section: 28.6.6 [re.traits], 28.3.3.1.2.2 [locale.facet] Status: Open Submitter: Sergey Zubkov Opened: 2013-10-15 Last modified: 2025-03-06
Priority: 3
View all other issues in [re.traits].
View all issues with Open status.
Discussion:
28.6.6 [re.traits]/7, begins with "if typeid(use_facet<collate<charT> >) == typeid(collate_byname<charT>)",
which appears to be pseudocode with the intention to convey that the collate facet has not been replaced by the user. Cf. the wording in
N1429 "there is no portable way to implement
transform_primary in terms of std::locale, since even if the sort key format returned by
std::collate_byname<>::transform is known and can be converted into a primary sort key, the user can still
install their own custom std::collate implementation into the locale object used, and that can use any sort key
format they see fit.".
std::collate_byname<charT>, which is in fact true in some implementations (e.g libc++), but not others
(e.g. libstdc++). This does not follow from the description of _byname in 28.3.3.1.2.2 [locale.facet]/4, which is only
required to provide equivalent semantics, to the named locale's facet, not to actually be one.
[2015-05-06 Lenexa: Move to Open]
MC, RP: Consequence of failing to follow the rule is UB.
MC: Tightening of requirements.
RP: It should be this way, we just didn't impose it before.
MC: Second change is a bug fix, original code didn't work.
TK: Doesn't seem to make things worse.
Bring up in larger group tomorrow.
JW arrives.
JW: libstdc++ violates this due to two std::string ABIs.
JW: This prevents installing a type derived from Facet_byname, constrains the implementor from using a smarter derived class version.
JW: Can't look at facet id to detect replacement, because replacements have the same id.
RP: Can you give it multiple ids through multiple inheritance?
JW: No, the facet mechanism wouldn't like that.
JW: We should also ask Martin Sebor, he's implemented this stuff recently.
MC: Sounds like this resolution doesn't work, need a better solution.
JW: Write in words "if the facet has not been replaced by the user", the implementation knows how to detect that, but not like this.
RP: User RE traits need to detect this too.
JW: =(
Move to Open, JW will invite Martin Sebor to join LWG for discussion.
Later ...
JW: This is not needed for user specializations after all.
MC: Agree, [re.traits]/7 only applies to the stdlib traits.
NM: Effects: doesn't make sense.
JW, NM, Martin Sebor to come up with new wording.
[2025-03-06; Jonathan comments]
LWG 4186(i) fixed the missing (getloc()) part.
Maybe we can resolve this differently, by changing 28.6.6 [re.traits]
to:
LetCbeuse_facet<collate<charT> >(getloc()). Iftypeid(C) == typeid(collate_byname<charT>) || typeid(C) == typeid(collate<charT>)and the form of ...
Proposed resolution:
This wording is relative to N3691.
Modify 28.3.3.1.2.2 [locale.facet]/4 as indicated:
For some standard facets a standard "...
_byname" class, derived from it, implements the virtual function semanticsequivalent toprovided by that facet of the locale constructed bylocale(const char*)with the same name. Each such facet provides a constructor that takes aconst char*argument, which names the locale, and arefsargument, which is passed to the base class constructor. Each such facet also provides a constructor that takes a string argumentstrand arefsargument, which has the same effect as calling the first constructor with the two argumentsstr.c_str()andrefs. If there is no "..._byname" version of a facet, the base class implements named locale semantics itself by reference to other facets. For any localelocconstructed bylocale(const char*)and facetFacetthat has a corresponding standardFacet_bynameclass,typeid(use_facet<Facet>(loc)) == typeid(Facet_byname).
Modify 28.6.6 [re.traits]/7 as indicated:
template <class ForwardIterator> string_type transform_primary(ForwardIterator first, ForwardIterator last) const;-7- Effects: if
typeid(use_facet<collate<charT> >(getloc())) == typeid(collate_byname<charT>)and the form of the sort key returned bycollate_byname<charT>::transform(first, last)is known and can be converted into a primary sort key then returns that key, otherwise returns an empty string.
wchar_t const* or to wchar_t not invoked for operator<<Section: 31.7.6.2 [ostream] Status: New Submitter: Alf P. Steinbach Opened: 2013-10-29 Last modified: 2016-01-28
Priority: 4
View all other issues in [ostream].
View all issues with New status.
Discussion:
For wide streams argument types wchar_t const* and wchar_t are supported only as template parameters.
User defined conversions are not considered for template parameter matching. Hence inappropriate overloads of
operator<< are selected when an implicit conversion is required for the argument, which is inconsistent
with the behavior for char const* and char, is unexpected, and is a useless result.
#include <iostream>
struct Byte_string
{
operator char const*() const { return "Hurray, it works!"; }
};
struct Wide_string
{
operator wchar_t const*() const { return L"Hurray, it works!"; }
};
struct Byte_ch
{
operator char() const { return 'X'; }
};
struct Wide_ch
{
operator wchar_t() const { return L'X'; }
};
auto main() -> int
{
using namespace std;
wcout << "'X' as char value : " << Byte_ch() << endl;
wcout << "'X' as wchar_t value: " << Wide_ch() << endl;
wcout << "Byte string pointer : " << Byte_string() << endl;
wcout << "Wide string pointer : " << Wide_string() << endl;
}
Example output:
'X' as char value : X 'X' as wchar_t value: 88 Byte string pointer : Hurray, it works! Wide string pointer : 000803C8
Proposed resolution:
This wording is relative to N3797.
Modify 31.7.6.2 [ostream], class template basic_ostream synopsis, as indicated:
namespace std {
[…]
// 27.7.3.6.4 character inserters
template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
charT);
template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
char);
template<class traits>
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&,
char);
template<class traits>
basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>&,
wchar_t);
[…]
template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
const charT*);
template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&,
const char*);
template<class traits>
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&,
const char*);
template<class traits>
basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>&,
const wchar_t*);
[…]
}
Modify 31.7.6.3.4 [ostream.inserters.character] as indicated: [Drafting note:
The replacement of os by out in p1 and the insertion of "out." in p4
just fix two obvious typos — end drafting note]
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out, charT c); template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out, char c); // specialization template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, char c); template<class traits> basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>& out, wchar_t c); // signed and unsigned template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, signed char c); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, unsigned char c);-1- Effects: Behaves as a formatted output function (31.7.6.3.1 [ostream.formatted.reqmts]) of
-2- Returns:out. Constructs a character sequenceseq. Ifchas typecharand the character type of the stream is notchar, thenseqconsists ofout.widen(c); otherwiseseqconsists ofc. Determines padding forseqas described in 31.7.6.3.1 [ostream.formatted.reqmts]. Insertsseqintoout. Calls.osout.width(0)out.template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out, const charT* s); template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out, const char* s); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, const char* s); template<class traits> basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>& out, const wchar_t* s); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, const signed char* s); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, const unsigned char* s);-3- Requires:
-4- Effects: Behaves like a formatted inserter (as described in 31.7.6.3.1 [ostream.formatted.reqmts]) ofsshall not be a null pointer.out. Creates a character sequenceseqofncharacters starting ats, each widened usingout.widen()(27.5.5.3), wherenis the number that would be computed as if by:
traits::length(s)for the following overloads:
where the first argument is of type
basic_ostream<charT, traits>&and the second is of typeconst charT*,
and also for the overloadwhere the first argument is of typebasic_ostream<char, traits>&and the second is of typeconst char*,where the first argument is of type
basic_ostream<wchar_t, traits>&and the second is of typeconst wchar_t*,
std::char_traits<char>::length(s)for the overload where the first argument is of typebasic_ostream<charT, traits>&and the second is of typeconst char*,
traits::length(reinterpret_cast<const char*>(s))for the other two overloads.Determines padding for
-5- Returns:seqas described in 31.7.6.3.1 [ostream.formatted.reqmts]. Insertsseqintoout. Callsout.width(0).out.
charT('1') is not the wide equivalent of '1'Section: 22.9.2 [template.bitset], 31.7.9 [quoted.manip] Status: Open Submitter: Zhihao Yuan Opened: 2013-12-02 Last modified: 2016-01-28
Priority: 3
View other active issues in [template.bitset].
View all other issues in [template.bitset].
View all issues with Open status.
Discussion:
Example: char16_t('1') != u'1' is possible.
char16_t is defined to be Unicode
code point, which is same to the ASCII value and UTF-8 for
7-bit chars. However, char is not guaranteed to have an
encoding which is compatible with ASCII. For example, '1' in EBCDIC is 241.
I found three places in the standard casting narrow char
literals: bitset::bitset, bitset::to_string and quoted.
PJ confirmed this issue and says he has a solution used
in their <filesystem> implementation, and he may want to
propose it to the standard.
The solution in my mind, for now, is to make those default
arguments magical, where the "magic" can be implemented
with a C11 _Generic selection (works in clang):
#define _G(T, literal) _Generic(T{}, \
char: literal, \
wchar_t: L ## literal, \
char16_t: u ## literal, \
char32_t: U ## literal)
_G(char16_t, '1') == u'1'
[Lenexa 2015-05-05: Move to Open]
Ask for complete PR (need quoted, to string, et al.)
Will then take it up again
Expectation is that this is correct way to fix this
Proposed resolution:
This wording is relative to N3797.
[Drafting note: This is a sample wording fixing only one case; I'm just too lazy to copy-paste it before we discussed whether the solution is worth and sufficient (for example, should the othercharTs like unsigned char just don't compile without
supplying those arguments? I hope so). — end drafting note]
Modify 22.9.2 [template.bitset] p1, class template bitset synopsis, as indicated:
namespace std {
template <size_t N> class bitset {
public:
[…]
template<class charT, class traits, class Allocator>
explicit bitset(
const basic_string<charT,traits,Allocator>& str,
typename basic_string<charT,traits,Allocator>::size_type pos = 0,
typename basic_string<charT,traits,Allocator>::size_type n =
basic_string<charT,traits,Allocator>::npos,
charT zero = charT('0')see below, charT one = charT('1')see below);
[…]
};
[…]
}
Modify 22.9.2.2 [bitset.cons] as indicated:
template<class charT, class traits, class Allocator> explicit bitset(const basic_string<charT, traits, Allocator>& str, typename basic_string<charT, traits, Allocator>::size_type pos = 0, typename basic_string<charT, traits, Allocator>::size_type n = basic_string<charT, traits, Allocator>::npos, charT zero =charT('0')see below, charT one =charT('1')see below);-?- The default values of
-3- Requires::zeroandonecompare equal to the character literals0and1of typecharT, respectively.pos <= str.size(). […]
is_empty type traitSection: 21.3.6.4 [meta.unary.prop] Status: Open Submitter: Richard Smith Opened: 2014-02-01 Last modified: 2017-02-02
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with Open status.
Discussion:
The 'Condition' for std::is_empty is listed as:
"
Tis a class type, but not a union type, with no non-static data members other than bit-fields of length 0, no virtual member functions, no virtual base classes, and no base classBfor whichis_empty<B>::valueis false."
This is incorrect: there is no such thing as a non-static data member that is a bit-field of length 0, since bit-fields of length 0 must be unnamed, and unnamed bit-fields are not members (see 11.4.10 [class.bit] p2).
It also means that classes such as:
struct S {
int : 3;
};
are empty (because they have no non-static data members). There's implementation divergence on the value of
is_empty<S>::value.
is_empty is (or how it could be useful), but if it's desirable for the above type to
not be treated as empty, something like this could work:
"
Tis a class type, but not a union type, with no non-static data membersother than, no unnamed bit-fields of non-zero length0, no virtual member functions, no virtual base classes, and no base classBfor whichis_empty<B>::valueis false."
and if the above type should be treated as empty, then this might be appropriate:
"
Tis a class type, but not a union type, with no (named) non-static data membersother than bit-fields of length 0, no virtual member functions, no virtual base classes, and no base classBfor whichis_empty<B>::valueis false."
[2016-08 Chicago]
Walter says: We want is_empty_v<S> to produce false as a result. Therefore, we recommend adoption of the first of the issue's suggestions.
Tuesday AM: Moved to Tentatively Ready
Previous resolution [SUPERSEDED]:
[2016-10 by Marshall - this PR incorrectly highlighted changed portions]
Modify Table 38 — Type property predicates for
is_emptyas follows:
Tis a non-union class type with no non-static data membersother than, no unnamed bit-fields of non-zero length0, no virtual member functions, no virtual base classes, and no base classBfor whichis_empty_v<B>is false.
[2016-10 Telecon]
Should probably point at section 1.8 for some of this. Status back to 'Open'
Proposed resolution:
Modify Table 38 — Type property predicates for is_empty as follows:
Tis a class type, but not a union type,is a non-union class type with no non-static data membersother than, no unnamed bit-fields of non-zero length0, no virtual member functions, no virtual base classes, and no base classBfor whichis_empty_v<B>is false.
emplace() should not move/copy the mapped_type constructor
arguments when no insertion happensSection: 23.2.7 [associative.reqmts], 23.2.8 [unord.req] Status: New Submitter: Jeffrey Yasskin Opened: 2014-02-15 Last modified: 2015-09-23
Priority: 3
View other active issues in [associative.reqmts].
View all other issues in [associative.reqmts].
View all issues with New status.
Discussion:
a_uniq.emplace(args) is specified as:
Effects: Inserts a value_type object
tconstructed with
std::forward<Args>(args)...if and only if there is no element in the
container with key equivalent to the key oft. Theboolcomponent of
the returned pair is true if and only if the insertion takes place,
and the iterator component of the pair points to the element with key
equivalent to the key oft.
However, we occasionally find code of the form:
std::unique_ptr<Foo> p(new Foo);
auto res = m.emplace("foo", std::move(p));
where we'd like to avoid destroying the Foo if the insertion doesn't
take place (if the container already had an element with the specified key).
emplace_stable member function, but LEWG's
discussion strongly agreed that we'd rather have emplace() Just Work:
Should map::emplace() be guaranteed not to move/copy its arguments if the insertion doesn't happen?
SF: 8 F: 3 N: 0 A: 0 SA: 0
This poll was marred by the fact that we didn't notice or call out
that emplace() must construct the key before doing the lookup, and it
must not then move the key after it determines whether an insert is
going to happen, and the mapped_type instance must live next to the key.
The very similar issue 2006(i) was previously marked NAD, with
N3178 as
discussion. However, given LEWG's interest in the alternate behavior,
we should reopen the question in this issue.
We will need a paper that describes how to implement this before we can make more progress.
Proposed resolution:
istreambuf_iterator end-of-stream equalitySection: 24.6.4 [istreambuf.iterator] Status: New Submitter: Hyman Rosen Opened: 2014-02-19 Last modified: 2023-04-13
Priority: 3
View other active issues in [istreambuf.iterator].
View all other issues in [istreambuf.iterator].
View all issues with New status.
Discussion:
Given the following code,
#include <sstream> std::stringbuf buf; std::istreambuf_iterator<char> begin(&buf); std::istreambuf_iterator<char> end;
it is not clear from the wording of the Standard whether begin.equal(end)
must be true. In at least one implementation it is not (CC: Sun C++ 5.10 SunOS_sparc Patch 128228-25 2013/02/20) and in at least
one implementation it is (gcc version 4.3.2 x86_64-unknown-linux-gnu).
end is an end-of-stream iterator since it was default
constructed. It also says that an iterator becomes equal to an end-of-stream
iterator when end of stream is reached by sgetc() having returned eof().
[istreambuf.iterator::equal] says that equal() returns true iff both iterators are end of stream
or not end of stream. But there seems to be no requirement that equal check for end-of-stream by calling sgetc().
Jiahan Zi at BloombergLP discovered this issue through his code failing to
work correctly. Dietmar Kühl has opined in a private communication that
the iterators should compare equal.
[2023-03-31; Jonathan Wakely comments]
I agree that they should compare equal, but that's in conflict with the
resolution of LWG 2544(i), which says that begin
must not be at end-of-stream because &buf is not null.
[2023-04-12; Jonathan adds wording]
Proposed resolution:
This wording is relative to N4944.
Change 24.6.4.1 [istreambuf.iterator.general] as indicated:
constexpr istreambuf_iterator() noexcept; constexpr istreambuf_iterator(default_sentinel_t) noexcept; istreambuf_iterator(const istreambuf_iterator&) noexcept = default; ~istreambuf_iterator() = default; istreambuf_iterator(istream_type& s) noexcept;: istreambuf_iterator(s.rdbuf()) { } istreambuf_iterator(streambuf_type* s) noexcept; istreambuf_iterator(const proxy& p) noexcept; … private: streambuf_type* sbuf_; // exposition only int_type c_{}; // exposition only }; }
Change 24.6.4.3 [istreambuf.iterator.cons] as indicated:
For each
istreambuf_iteratorconstructor in this section, an end-of-stream iterator is constructed ifand only ifthe exposition-only membersbuf_is initialized with a null pointer value or ifsbuf_->sgetc()returnstraits_type::eof().constexpr istreambuf_iterator() noexcept; constexpr istreambuf_iterator(default_sentinel_t) noexcept;-1- Effects: Initializes
sbuf_withnullptr.istreambuf_iterator(istream_type& s) noexcept;
-2- Effects: Initializessbuf_withs.rdbuf().istreambuf_iterator(streambuf_type* s) noexcept;[Drafting note:
sgetc()can throw, but this function isnoexcept. Should it swallow exceptions and create an end-of-stream iterator, to avoid weakening the exception spec of an existing function?]-3- Effects: Initializes
sbuf_withs. Ifsis not null, initializesc_withs->sgetc(). Setssbuf_to null ifsgetcexits via an exception, or iftraits_type::eq_int_type(c_, traits_type::eof())istrue.istreambuf_iterator(const proxy& p) noexcept;-4- Effects: Initializes
sbuf_withp.sbuf_. Ifp.sbuf_is not null, initializesc_withp.keep_.
Change 24.6.4.4 [istreambuf.iterator.ops] as indicated:
charT operator*() const;-?- Preconditions:
sbuf_is not null.-1- Returns:
The character obtained via thestreambufmembersbuf_->sgetc().traits_type::to_char_type(c_).-?- Throws: Nothing.
istreambuf_iterator& operator++();-?- Preconditions:
sbuf_is not null.-2- Effects:
As if byPerformssbuf_->sbumpc().c_ = sbuf_->snextc(), then setssbuf_to null iftraits_type::eq_int_type(c_, traits_type::eof())istrue.-3- Returns:
*this.proxy operator++(int);-4-
Returns:proxy(sbuf_->sbumpc(), sbuf_).
Effects: Equivalent to:proxy p(**this, sbuf_); ++*this; return p;bool equal(const istreambuf_iterator& b) const;-5- Returns:
bool(sbuf_) == bool(b.sbuf_).[Note: This is
trueif and only if both iterators are at end-of-stream, or neither is at end-of-stream, regardless of what streambuf object they use. — end note]template<class charT, class traits> bool operator==(const istreambuf_iterator<charT, traits>& a, const istreambuf_iterator<charT, traits>& b);-6- Returns:
a.equal(b).bool equal(const istreambuf_iterator& i, default_sentinel_t s) const;-7- Returns:
.i.equal(s)i.sbuf_ == nullptr
Section: 30.5.9 [time.duration.literals] Status: Open Submitter: Jonathan Wakely Opened: 2014-05-16 Last modified: 2014-11-08
Priority: 3
View all issues with Open status.
Discussion:
30.5.9 [time.duration.literals] p3 says:
If any of these suffixes are applied to an integer literal and the resulting
chrono::durationvalue cannot be represented in the result type because of overflow, the program is ill-formed.
Ill-formed requires a diagnostic at compile-time, but there is no way
to detect the overflow from unsigned long long to the signed
duration<>::rep type.
[Urbana 2014-11-07: Move to Open]
Proposed resolution:
type_info's destructor shouldn't be required to be virtualSection: 17.7.3 [type.info] Status: Open Submitter: Stephan T. Lavavej Opened: 2014-06-14 Last modified: 2016-08-06
Priority: 3
View all other issues in [type.info].
View all issues with Open status.
Discussion:
type_info's destructor is depicted as being virtual, which is nearly unobservable to users (since they can't construct
or copy this class, they can't usefully derive from it). However, it's technically observable (via is_polymorphic and
has_virtual_destructor). It also imposes real costs on implementations, requiring them to store one vptr per
type_info object, when RTTI space consumption is a significant concern.
virtual here, but it would allow other implementations to drop virtual
and improve their RTTI space consumption.
Richard Smith:
It's observable in a few other ways.
std::map<void*, something> m; m[dynamic_cast<void*>(&typeid(blah))] = stuff;
... is broken by this change, because you can't dynamic_cast a non-polymorphic class type to void*.
type_info& f(); typeid(f());
... evaluates f() at runtime without this change, and might not do so with this change.
[Lenexa 2015-05-05: Move to Open]
Marshall to poll LEWG for their opinion
[2016-06]
On the reflector, STL wrote:
We'll prototype this change and report back with data in the future.
[2016-08 Chicago]
No update from STL. Set priority to P3
Proposed resolution:
This wording is relative to N3936.
Change 17.7.3 [type.info] as indicated:
namespace std { class type_info { public:virtualsee below ~type_info(); […] }; }-1- The class
type_infodescribes type information generated by the implementation. Objects of this class effectively store a pointer to a name for the type, and an encoded value suitable for comparing two types for equality or collating order. The names, encoding rule, and collating sequence for types are all unspecified and may differ between programs. Whether~type_info()isvirtualis implementation-defined.
Section: 16.4.6.9 [reentrancy] Status: Open Submitter: Stephan T. Lavavej Opened: 2014-07-01 Last modified: 2021-07-31
Priority: 3
View all other issues in [reentrancy].
View all issues with Open status.
Discussion:
N3936 16.4.6.9 [reentrancy]/1 talks about "functions", but that doesn't address the scenario of calling different member functions of a single object. Member functions often have to violate and then re-establish invariants. For example, vectors often have "holes" during insertion, and element constructors/destructors/etc. shouldn't be allowed to observe the vector while it's in this invariant-violating state. The [reentrancy] Standardese should be extended to cover member functions, so that implementers can either say that member function reentrancy is universally prohibited, or selectively allowed for very specific scenarios.
(For clarity, this issue has been split off from LWG 2382(i).)[2014-11-03 Urbana]
AJM confirmed with SG1 that they had no special concerns with this issue, and LWG should retain ownership.
AM: this is too overly broad as it also covers calling the exact same member function on a different objectMove to Open
[2015-07 Telecon Urbana]
Marshall to ping STL for updated wording.
[2016-05 email from STL]
I don't have any better suggestions than my original PR at the moment.
Previous resolution [SUPERSEDED]:
This wording is relative to N3936.
Change 16.4.6.9 [reentrancy] p1 as indicated:
-1- Except where explicitly specified in this standard, it is implementation-defined which functions (including different member functions called on a single object) in the Standard C++ library may be recursively reentered.
[2021-07-29 Tim suggests new wording]
The "this pointer" restriction is modeled on 11.9.5 [class.cdtor] p2.
It allows us to continue to specify a member function f as calling some other
member function g, since any such call would use something obtained
from the first member function's this pointer.
const (or are treated as such for the
purposes of data race avoidance). Using "access" means that we also cover direct
access to the object representation, such as the following pathological example
from Arthur O'Dwyer,
which is now undefined:
std::string s = "hello world"; char *first = (char*)&s; char *last = (char*)(&s + 1); s.append(first, last);
Proposed resolution:
This wording is relative to N4892.
Add the following paragraph to 16.4.6.9 [reentrancy]:
-?- During the execution of a standard library non-static member function F on an object, if that object is accessed through a glvalue that is not obtained, directly or indirectly, from the
thispointer of F, in a manner that can conflict (6.10.2.2 [intro.races]) with any access that F is permitted to perform (16.4.6.10 [res.on.data.races]), the behavior is undefined unless otherwise specified.
std::align [ptr.align]Section: 20.2.5 [ptr.align] Status: New Submitter: Melissa Mears Opened: 2014-08-06 Last modified: 2014-11-03
Priority: 3
View other active issues in [ptr.align].
View all other issues in [ptr.align].
View all issues with New status.
Discussion:
The specification of std::align does not appear to specify what happens when the value of the size
parameter is 0. (The question of what happens when alignment is 0 is mentioned in another Defect Report, 2377(i);
it would change the behavior to be undefined rather than potentially implementation-defined.)
size being 0 is interesting because the result is ambiguous. Consider the following code's output:
#include <cstdio>
#include <memory>
int main()
{
alignas(8) char buffer[8];
void *ptr = &buffer[1];
std::size_t space = sizeof(buffer) - sizeof(char[1]);
void *result = std::align(8, 0, ptr, space);
std::printf("%d %td\n", !!result, result ? (static_cast<char*>(result) - buffer) : std::ptrdiff_t(-1));
}
There are four straightforward answers as to what the behavior of std::align with size 0 should be:
The behavior is undefined because the size is invalid.
The behavior is implementation-defined. This seems to be the status quo, with current implementations using #3.
Act the same as size == 1, except that if size == 1 would fail but would be defined and succeed
if space were exactly 1 larger, the result is a pointer to the byte past the end of the ptr buffer. That is, the
"aligned" version of a 0-byte object can be one past the end of an allocation. Such pointers are, of course, valid when not
dereferenced (and a "0-byte object" shouldn't be), but whether that is desired is not specified in the Standard's definition
of std::align, it appears. The output of the code sample is "1 8" in this case.
Act the same as size == 1; this means that returning "one past the end" is not a possible result. In this case,
the code sample's output is "0 -1".
The two compilers I could get working with std::align, Visual Studio 2013 and Clang 3.4, implement #3. (Change %td to
%Id on Visual Studio 2013 and earlier. 2014 and later will have %td.)
Proposed resolution:
slice_array, gslice_array, mask_array, indirect_array copy constructorSection: 29.6.5 [template.slice.array], 29.6.7 [template.gslice.array], 29.6.8 [template.mask.array], 29.6.9 [template.indirect.array] Status: New Submitter: Akira Takahashi Opened: 2014-08-12 Last modified: 2014-11-03
Priority: 4
View all other issues in [template.slice.array].
View all issues with New status.
Discussion:
I found a missing specification of the copy constructor of the following class templates:
slice_array (29.6.5 [template.slice.array])
gslice_array (29.6.7 [template.gslice.array])
mask_array (29.6.8 [template.mask.array])
indirect_array (29.6.9 [template.indirect.array])
Proposed resolution:
Before 29.6.5.2 [slice.arr.assign] insert a new sub-clause as indicated:
-?- slice_array constructors [slice.arr.cons]
slice_array(const slice_array&);-?- Effects: The constructed slice refers to the same
valarray<T>object to which the argument slice refers.
Before 29.6.7.2 [gslice.array.assign] insert a new sub-clause as indicated:
-?- gslice_array constructors [gslice.array.cons]
gslice_array(const gslice_array&);-?- Effects: The constructed slice refers to the same
valarray<T>object to which the argument slice refers.
Before 29.6.8.2 [mask.array.assign] insert a new sub-clause as indicated:
-?- mask_array constructors [mask.array.cons]
mask_array(const mask_array&);-?- Effects: The constructed slice refers to the same
valarray<T>object to which the argument slice refers.
Before 29.6.9.2 [indirect.array.assign] insert a new sub-clause as indicated:
-?- indirect_array constructors [indirect.array.cons]
indirect_array(const indirect_array&);-?- Effects: The constructed slice refers to the same
valarray<T>object to which the argument slice refers.
Section: 28.6.2 [re.req] Status: New Submitter: Jonathan Wakely Opened: 2014-09-30 Last modified: 2020-04-16
Priority: 3
View other active issues in [re.req].
View all other issues in [re.req].
View all issues with New status.
Discussion:
The requirements on the traits class in 28.6.2 [re.req] do not say whether a
regular expression traits class is required to be DefaultConstructible,
CopyConstructible, CopyAssignable etc.
std::regex_traits class appears to be all of the above, but can
basic_regex assume that for user-defined traits classes?
Should the following statements all leave u in equivalent states?
X u{v};
X u; u = v;
X u; u.imbue(v.getloc();
Whether they are equivalent has implications for basic_regex copy construction and
assignment.
[2020-04-16, Jonathan adds that 28.6.7.5 [re.regex.locale] requires the traits type to be default-initialized, despite no guarantee that the traits type is default constructible. ]
Proposed resolution:
is_constructible, etc. and default argumentsSection: 21 [meta] Status: Core Submitter: Hubert Tong Opened: 2014-11-04 Last modified: 2015-10-21
Priority: 3
View other active issues in [meta].
View all other issues in [meta].
View all issues with Core status.
Discussion:
The BaseCharacteristic for is_constructible is defined in terms of the well-formedness
of a declaration for an invented variable. The well-formedness of the described declaration itself may
change for the same set of arguments because of the introduction of default arguments.
std::is_constructible; however, it seems that this situation is caused without a user violation
of the library requirements or the ODR. There is a similar issue with is_convertible, result_of
and others.
a.cc:
#include <type_traits>
struct A { A(int, int); };
const std::false_type& x1 = std::is_constructible<A, int>();
int main() { }
b.cc:
#include <type_traits>
struct A { A(int, int); };
inline A::A(int, int = 0) { }
const std::true_type& x2 = std::is_constructible<A, int>();
Presumably this program should invoke undefined behaviour, but the Library specification doesn't say that.
[2015-02 Cologne]
Core wording should say "this kind of thing is ill-formed, no diagnostic required"
Proposed resolution:
<initializer_list>Section: 17.11 [support.initlist], 24.7 [iterator.range] Status: New Submitter: Richard Smith Opened: 2014-11-11 Last modified: 2021-06-06
Priority: 3
View other active issues in [support.initlist].
View all other issues in [support.initlist].
View all issues with New status.
Discussion:
These sections define helper functions, some of which apply to initializer_list<T>. And they're
available if you include one of a long list of header files, many of which include <initializer_list>.
But they are not available if you include <initializer_list>. This seems very odd.
#include <initializer_list>
auto x = {1, 2, 3};
const int *p = data(x); // error, undeclared
#include <vector>
const int *q = data(x); // ok
Proposed resolution:
Section: 16.4.4.6 [allocator.requirements], 23.3.13.3 [vector.capacity], 23.3.13.5 [vector.modifiers] Status: New Submitter: dyp Opened: 2014-12-06 Last modified: 2015-06-10
Priority: 3
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with New status.
Discussion:
When resizing a vector, the accessibility and exception specification of the value type's
constructors determines whether the elements are copied or moved to the new buffer.
However, the copy/move is performed via the allocator's construct member function, which is
assumed, but not required, to call the copy/move constructor and propagate only exceptions
from the value type's copy/move constructor. The issue might also affect other classes.
Table 28 — Allocator requirements Expression Return type Assertion/note
pre-/post-conditionDefault …a.construct(c, args)(not used) Effect: Constructs an object of type Catc::new ((void*)c) C(forward<Args>(args)...)…
and from 16.4.4.6 [allocator.requirements] p9:
An allocator may constrain the types on which it can be instantiated and the arguments for which its
constructmember may be called. If a type cannot be used with a particular allocator, the allocator class or the call toconstructmay fail to instantiate.
I conclude the following from the wording:
The allocator is not required to call the copy constructor if the arguments (args) is a single (potentially const) lvalue of the value type. Similarly for a non-const rvalue + move constructor. See also 23.2.2 [container.requirements.general] p15 which seems to try to require this, but is not sufficient: That paragraph specifies the semantics of the allocator's operations, but not which constructors of the value type are used, if any.
The allocator may throw exceptions in addition to the exceptions propagated by the constructors of the value type; it can also propagate exceptions from constructors other than a copy/move constructor.
This leads to an issue with the wording of the exception safety guarantees for vector modifiers in 23.3.13.5 [vector.modifiers] p1:
[…]
void push_back(const T& x); void push_back(T&& x);Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of
Tor by any InputIterator operation there are no effects. If an exception is thrown while inserting a single element at the end andTisCopyInsertableoris_nothrow_move_constructible<T>::valueis true, there are no effects. Otherwise, if an exception is thrown by the move constructor of a non-CopyInsertableT, the effects are unspecified.
The wording leads to the following problem:
Copy and move assignment are invoked directly from vector.
For intermediary objects (see 2164(i)),
vector also directly invokes the copy and move constructor of the value type.
However, construction of the actual element within the buffer is invoked via the allocator abstraction.
As discussed above, the allocator currently is not required to call a copy/move constructor.
If is_nothrow_move_constructible<T>::value is true for some value type T,
but the allocator uses modifying operations for MoveInsertion that do throw,
the implementation is required to ensure that "there are no effects",
even if the source buffer has been modified.
vector capacity functions specify exception safety guarantees
referring to the move constructor of the value type. For example, vector::resize in 23.3.13.3 [vector.capacity] p14:
Remarks: If an exception is thrown other than by the move constructor of a non-CopyInsertableTthere are no effects.
The wording leads to the same issue as described above.
Code example:
template<class T>
class allocator;
class pot_reg_type // a type which creates
// potentially registered instances
{
private:
friend class allocator<pot_reg_type>;
struct register_t {};
static std::set<pot_reg_type*>& get_registry()
{
static std::set<pot_reg_type*> registry;
return registry;
}
void enregister() noexcept(false)
{
get_registry().insert(this);
}
void deregister()
{
get_registry().erase(this);
}
public:
pot_reg_type(void ) noexcept(true) {}
pot_reg_type(pot_reg_type const&) noexcept(true) {}
pot_reg_type(pot_reg_type&& ) noexcept(true) {}
private:
pot_reg_type(register_t ) noexcept(false)
{ enregister(); }
pot_reg_type(register_t, pot_reg_type const&) noexcept(false)
{ enregister(); }
pot_reg_type(register_t, pot_reg_type&& ) noexcept(false)
{ enregister(); }
};
template<class T>
class allocator
{
public:
using value_type = T;
value_type* allocate(std::size_t p)
{ return (value_type*) ::operator new(p); }
void deallocate(value_type* p, std::size_t)
{ ::operator delete(p); }
void construct(pot_reg_type* pos)
{
new((void*)pos) pot_reg_type((pot_reg_type::register_t()));
}
void construct(pot_reg_type* pos, pot_reg_type const& source)
{
new((void*)pos) pot_reg_type(pot_reg_type::register_t(), source);
}
template<class... Args>
void construct(T* p, Args&&... args)
{
new((void*)p) T(std::forward<Args>(args)...);
}
};
The construct member function template is only required for rebinding,
which can be required e.g. to store additional debug information in
the allocated memory (e.g. VS2013).
noexcept(true) move
constructor, this allocator won't call that constructor for rvalue arguments.
In any case, it does not call a constructor for which vector has formulated its
requirements. An exception thrown by a constructor called by this allocator is not
covered by the specification in 23.3.13.5 [vector.modifiers] and therefore is
guaranteed not to have any effect on the vector object when resizing.
For an example how this might invalidate the exception safety
guarantees, see this post on the std-discussion mailing list.
Another problem arises for value types whose constructors are private,
but may be called by the allocator e.g. via friendship.
Those value types are not MoveConstructible
(is_move_constructible is false), yet they can be MoveInsertable.
It is not possible for vector to create intermediary objects (see 2164(i)) of such a type
by directly using the move constructor.
Current implementations of the single-element forms of vector::insert and vector::emplace
do create intermediary objects by directly calling one of the value type's constructors,
probably to allow inserting objects from references that alias other elements of the container.
As far as I can see, Table 100 — "Sequence container requirements" in 23.2.4 [sequence.reqmts]
does not require that the creation of such intermediare objects can be performed
by containers using the value type's constructor directly.
It is unclear to me if the allocator's construct function could be used to create those
intermediary objects, given that they have not been allocated by the allocator.
Two possible solutions:
Add the following requirement to the allocator_traits::construct function:
If the parameter pack args consists of a single parameter of the type
value_type&&,
the function may only propagate exceptions if is_nothrow_move_constructible<value_type>::value
is false.
alloctor_traits::construct to call a true copy/move constructor
of the value type breaks std::scoped_allocator_adapter,
as pointed out by Casey Carter in a post on the std-discussion mailing list.
Change vector's criterion whether to move or copy when resizing:
Instead of testing the value type's constructors viais_move_constructible, check the value of
noexcept( allocator_traits<Allocator>::construct(alloc, ptr, rval) )
where
alloc is an lvalue of type Allocator,
ptr is an expression of type allocator_traits<Allocator>::pointer
and
rval is a non-const rvalue of type value_type.
A short discussion of the two solutions:
Solution 1 allows keepingis_nothrow_move_constructible<value_type>
as the criterion for vector to decide between copying and moving when resizing.
It restricts what can be done inside the construct member function of allocators,
and requires implementers of allocators to pay attention to the value types used.
One could conceive allocators checking the following with a static_assert:
If the value type is_nothrow_move_constructible,
then the constructor actually called for MoveInsertion within the construct
member function is also declared as noexcept.
Solution 2 requires changing both the implementation of the default
allocator (add a conditional noexcept) and vector (replace
is_move_constructible with an allocator-targeted check).
It does not impose additional restrictions on the allocator (other than
23.2.2 [container.requirements.general] p15),
and works nicely even if the move constructor of a MoveInsertable type is private or deleted
(the allocator might be a friend of the value type).
In both cases, an addition might be required to provide the basic exception safety guarantee.
A short discussion on this topic can be found
in the std-discussion mailing list.
Essentially, if allocator_traits<Allocator>::construct throws an exception,
the object may or may not have been constructed.
Two solutions are mentioned in that discussion:
allocator_traits<Allocator>::construct needs to tell its caller
whether or not the construction was successful, in case of an exception.
If allocator_traits<Allocator>::construct propagates an exception,
it shall either not have constructed an object at the specified location,
or that object shall have been destroyed
(or it shall ensure otherwise that no resources are leaked).
[2015-05-23, Tomasz Kamiński comments]
Solution 1 discussed in this issue also breaks support for the polymorphic_allocator proposed in the part
of the Library Fundamentals TS v1, in addition to already mentioned std::scoped_allocator_adapter. Furthermore
there is unknown impact on the other user-defined state-full allocators code written in the C++11.
std::allocator_trait::construct method and
copy/move constructor even for the standard std::allocator. As example please consider following class:
struct NonCopyable
{
NonCopyable() = default;
NonCopyable(NonCopyable const&) = delete;
NonCopyable(NonCopyable&&) = delete;
};
struct InitListConstructor : NonCopyable
{
InitListConstructor() = default;
InitListConstructor(std::initializer_list<int>);
operator int() const;
};
For the above declarations following expression are ill-formed:
InitListConstructor copy(std::declval<InitListConstructor const&>()); InitListConstructor move(std::declval<InitListConstructor&&>());
So the class is not CopyConstructible nor MoveConstructible. However the following are well formed:
InitListConstructor copy{std::declval<InitListConstructor const&>()};
InitListConstructor move{std::declval<InitListConstructor&&>()};
And will be used by std::allocator<InitListConstructor>::construct in case of move-insertion
and copy-insertion, after appliance of the resolution proposed in mentioned papers:
The gist of the proposed library fix is simple:
if
is_constructible_v<TargetType, Args...>, use direct-nonlist-initializationotherwise, use brace-initialization.
As consequence the requirement proposed in the Solution 1:
If the parameter pack
argsconsists of a single parameter of the typevalue_type&&, the function may only propagate exceptions ifis_nothrow_move_constructible<value_type>::valueis false.
Will no longer hold for the std::allocator.
Proposed resolution:
copy_n's number of InputIterator increments unspecifiedSection: 26.7.1 [alg.copy] Status: Open Submitter: Jonathan Wakely Opened: 2015-01-28 Last modified: 2018-06-22
Priority: 3
View other active issues in [alg.copy].
View all other issues in [alg.copy].
View all issues with Open status.
Discussion:
It's unspecified how many times copy_n increments the InputIterator.
uninitialized_copy_n is specified to increment it exactly n times,
which means if an istream_iterator is used then the next character
after those copied is read from the stream and then discarded, losing data.
copy_n with n - 1 increments of the InputIterator, which avoids reading
and discarding a character when used with istream_iterator, but is
inconsistent with uninitialized_copy_n and causes surprising behaviour
with istreambuf_iterator instead, because copy_n(in, 2, copy_n(in, 2,
out)) is not equivalent to copy_n(in, 4, out)
[2016-08 Chicago]
Tues PM: refer to LEWG
[LEWG Kona 2017]
This is a mess. Append to Effects: If the InputIterator is not a forward iterator, increments n-1 times. Otherwise the number of increments is not more than n. (ncm) The preceding proposition is unsatisfactory, because it is wrong for istreambuf_iterator, which is much more useful than istream_iterator. Proposing instead: Append to Effects: If InputIterator is istream_iterator for some T, increments n-1 times. Otherwise, increments n times. Want a paper exploring what the implementations actually do, and what non-uniformity is "right".
Status to Open
Proposed resolution:
Section: 22.4.9 [tuple.rel], 20.2.10.3 [allocator.globals], 20.3.1.6 [unique.ptr.special], 20.3.2.2.8 [util.smartptr.shared.cmp], 30.5.7 [time.duration.comparisons], 30.6.7 [time.point.comparisons], 20.6.5 [scoped.adaptor.operators], 24.5.1.8 [reverse.iter.cmp], 24.5.4.8 [move.iter.op.comp] Status: New Submitter: Richard Smith Opened: 2015-02-07 Last modified: 2021-06-06
Priority: 3
View other active issues in [tuple.rel].
View all other issues in [tuple.rel].
View all issues with New status.
Discussion:
The standard library specifies a lot of heterogeneous comparison operators. For instance:
template<class... TTypes, class... UTypes> constexpr bool operator!=(const tuple<TTypes...>&, const tuple<UTypes...>&);
This has an unfortunate consequence:
#include <tuple> #include <utility> using namespace std::rel_ops; std::tuple<int> a(0); bool b = a != a;
The last line here is ill-formed due to ambiguity: it might be rel_ops::operator!=, and it might be the
heterogeneous tuple operator!=. These are not partially ordered, because they have different constraints:
rel_ops requires the types to match, whereas the tuple comparison requires both types to be tuples (but not
to match). The same thing happens for user code that defines its own unconstrained
'template<typename T> operator!=(const T&, const T&)' rather than using rel_ops.
template<class... TTypes> constexpr bool operator!=(const tuple<TTypes...>&, const tuple<TTypes...>&);This is then unambiguously chosen over the other options in the preceding case. FWIW, libstdc++ already does this in some cases.
Proposed resolution:
wstring_convert uses cvtstateSection: 99 [depr.conversions.string] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2017-04-22
Priority: 4
View other active issues in [depr.conversions.string].
View all other issues in [depr.conversions.string].
View all issues with New status.
Discussion:
How do wstring_convert::from_bytes and wstring_convert::to_bytes use
the cvtstate member?
codecvt member functions? Is a copy of it passed
to the member functions? "Otherwise it shall be left unchanged"
implies a copy is used, but if that's really what's intended there are
simpler ways to say so.
Proposed resolution:
wbuffer_convert uses cvtstateSection: 99 [depr.conversions.buffer] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2017-04-22
Priority: 4
View other active issues in [depr.conversions.buffer].
View all other issues in [depr.conversions.buffer].
View all issues with New status.
Discussion:
How does wbuffer_convert use the cvtstate member?
Proposed resolution:
wbuffer_convert unclearSection: 99 [depr.conversions.buffer] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2017-04-22
Priority: 4
View other active issues in [depr.conversions.buffer].
View all other issues in [depr.conversions.buffer].
View all issues with New status.
Discussion:
If a codecvt conversion returns codecvt_base::error should that be
treated as EOF? An exception? Should all the successfully converted
characters before a conversion error be available to the users of the
wbuffer_convert and/or the internal streambuf, or does a conversion
error lose information?
Proposed resolution:
wstring_convert should be more precise regarding "byte-error string" etc.Section: 99 [depr.conversions.string] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2017-04-22
Priority: 4
View other active issues in [depr.conversions.string].
View all other issues in [depr.conversions.string].
View all issues with New status.
Discussion:
Paragraph 4 of 99 [depr.conversions.string] introduces byte_err_string
as "a byte string to display on errors". What does display mean? The string is returned
on error, it's not displayed anywhere.
byte_err_string".
What default value? Is "Hello, world!" allowed? If it means
default-construction it should say so. If paragraph 14 says it won't
be used what does it matter how it's initialized? The end of the
paragraph refers to storing "byte_err in byte_err_string". This should
be more clearly related to the wording in paragraph 14.
It might help if the constructor (and destructor) was specified before
the other member functions, so it can more formally define the
difference between being "constructed with a byte-error string" and
not.
All the same issues apply to the wide_err_string member.
Proposed resolution:
<regex> needs lots of noexceptSection: 28.6 [re] Status: New Submitter: Stephan T. Lavavej Opened: 2015-03-27 Last modified: 2015-05-05
Priority: 3
View other active issues in [re].
View all other issues in [re].
View all issues with New status.
Discussion:
Only 4 functions are marked noexcept in all of Clause 28. Many more need to be marked — for example,
regex_error::code(), basic_regex::swap(), and sub_match::length().
Proposed resolution:
std::less<T*> in constant expressionSection: 22.10.8 [comparisons] Status: New Submitter: Agustín K-ballo Bergé Opened: 2015-04-01 Last modified: 2021-04-10
Priority: 3
View other active issues in [comparisons].
View all other issues in [comparisons].
View all issues with New status.
Discussion:
It is not entirely clear if and when the specializations of std::less (and friends) for pointer types
can be used in a constant expression. Consider the following code:
#include <functional>
struct foo {};
foo x, y;
constexpr bool b = std::less<foo*>{}(&x, &y); // [1]
foo z[] = {{}, {}};
constexpr bool ba = std::less<foo*>{}(&z[0], &z[1]); // [2]
Comparing the address of unrelated objects is not a constant expression since the result is unspecified, so
it could be expected for [1] to fail and [2] to succeed. However, std::less specialization for pointer
types is well-defined and yields a total order, so it could just as well be expected for [1] to succeed. Finally,
since the implementation of such specializations is not mandated, [2] could fail as well (This could happen, if
an implementation would provide such a specialization and if that would use built-in functions that would not be
allowed in constant expressions, for example). In any case, the standard should be clear so as to avoid
implementation-defined constexpr-ness.
[2017-01-22, Jens provides rationale and proposed wording]
std::less<T*> is required to deliver a total order on pointers.
However, the layout of global objects is typically determined
by the linker, not the compiler, so requiring std::less<T*> to
provide an ordering at compile-time that is consistent with
run-time would need results from linking to feed back to
the compiler, something that C++ has traditionally not required.
This wording is relative to N4618.
Add at the end of 22.10.8 [comparisons]:
-2- For templates
less,greater,less_equal, andgreater_equal, […], if the call operator calls a built-in operator comparing pointers, the call operator yields a strict total order that is consistent among those specializations and is also consistent with the partial order imposed by those built-in operators. Relational comparisons of pointer values are not required to be usable as constant expressions.
[2021-04-05; Jiang An comments and provides alternative wording]
The libc++ and MSVC STL implementations only support flat address spaces, and always use comparison operators.
The libstdc++ implementation casts pointer values to uintptr_t if the direct comparison result is unusable
in constant evaluation.
std::is_constant_evaluated.
Proposed resolution:
This wording is relative to N4885.
Add at the end of 22.10.8 [comparisons] p2:
-2- For templates
less,greater,less_equal, andgreater_equal, the specializations for any pointer type yield a result consistent with the implementation-defined strict total order over pointers (3.28 [defns.order.ptr]). [Note 1: Ifa < bis well-defined for pointersaandbof typeP, then(a < b) == less<P>()(a, b),(a > b) == greater<P>()(a, b), and so forth. — end note] For template specializationsless<void>,greater<void>,less_equal<void>, andgreater_equal<void>, if the call operator calls a built-in operator comparing pointers, the call operator yields a result consistent with the implementation-defined strict total order over pointers. A comparison result of pointer values is a core constant expression if and only if the corresponding built-in comparison expression is a core constant expression.
Add at the end of 22.10.9 [range.cmp] (3.1):
-3- Effects:
(3.1) — If the expression
std::forward<T>(t) == std::forward<U>(u)results in a call to a built-in operator==comparing pointers: returnsfalseif either (the converted value of)tprecedesuoruprecedestin the implementation-defined strict total order over pointers (3.28 [defns.order.ptr]) and otherwisetrue. The result is a core constant expression if and only ifstd::forward<T>(t) == std::forward<U>(u)is a core constant expression.(3.2) — Otherwise, equivalent to:
return std::forward<T>(t) == std::forward<U>(u);
Add at the end of 22.10.9 [range.cmp] (7.1):
-7- Effects:
(7.1) — If the expression
std::forward<T>(t) < std::forward<U>(u)results in a call to a built-in operator<comparing pointers: returnstrueif (the converted value of)tprecedesuin the implementation-defined strict total order over pointers (3.28 [defns.order.ptr]) and otherwisefalse. The result is a core constant expression if and only ifstd::forward<T>(t) < std::forward<U>(u)is a core constant expression.(7.2) — Otherwise, equivalent to:
return std::forward<T>(t) < std::forward<U>(u);
Add at the end of 22.10.8.8 [comparisons.three.way] (3.1):
-3- Effects:
(3.1) — If the expression
std::forward<T>(t) <=> std::forward<U>(u)results in a call to a built-in operator<=>comparing pointers: returnsstrong_ordering::lessif (the converted value of)tprecedesuin the implementation-defined strict total order over pointers (3.28 [defns.order.ptr]),strong_ordering::greaterifuprecedest, and otherwisestrong_ordering::equal. The result is a core constant expression if and only ifstd::forward<T>(t) <=> std::forward<U>(u)is a core constant expression.(3.2) — Otherwise, equivalent to:
return std::forward<T>(t) <=> std::forward<U>(u);
initializer_list supports incomplete classesSection: 17.11 [support.initlist] Status: New Submitter: David Krauss Opened: 2015-04-27 Last modified: 2015-05-05
Priority: 4
View other active issues in [support.initlist].
View all other issues in [support.initlist].
View all issues with New status.
Discussion:
The typical use-case of std::initializer_list<T> is for a pass-by-value parameter of T's constructor.
However, this contravenes 16.4.5.8 [res.on.functions]/2.5 because initializer_list doesn't specifically allow
incomplete types (as do for example std::unique_ptr (20.3.1 [unique.ptr]/5) and
std::enable_shared_from_this (20.3.2.7 [util.smartptr.enab]/2)).
Proposed resolution:
Section: 21.3.6.4 [meta.unary.prop] Status: New Submitter: Hubert Tong Opened: 2015-05-07 Last modified: 2015-08-03
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with New status.
Discussion:
I do not believe that the wording in 21.3.6.4 [meta.unary.prop] paragraph 3 allows for the following program to be ill-formed:
#include <type_traits>
template <typename T> struct B : T { };
template <typename T> struct A { A& operator=(const B<T>&); };
std::is_assignable<A<int>, int> q;
In particular, I do not see where the wording allows for the "compilation of the expression"
declval<T>() = declval<U>() to occur as a consequence of instantiating std::is_assignable<T, U>
(where T and U are, respectively, A<int> and int in the example code).
A<int> as a result of requiring it to be a complete type does not trigger the instantiation of
B<int>; however, the "compilation of the expression" in question does.
Proposed resolution:
uncaught_exception()Section: 31.7.6.2.4 [ostream.sentry] Status: New Submitter: Roger Orr Opened: 2015-05-08 Last modified: 2020-09-06
Priority: 3
View all other issues in [ostream.sentry].
View all issues with New status.
Discussion:
In the current 31.7.6.2.4 [ostream.sentry], p4 refers to the now deprecated std::uncaught_exception():
D.9 [depr.uncaught].
If
((os.flags() & ios_base::unitbuf) && !uncaught_exception() && os.good())is true, callsos.rdbuf()->pubsync().
This needs to be changed, for example to use std::uncaught_exceptions() and to capture the value on entry and
compare with the saved value on exit.
[2015-06, Telecon]
JW: I already added an 's' here to make it use the new function, but that doesn't resolve Roger's suggestion to capture the value on entry and check it.
[2019-03-21; Daniel comments and provides wording]
The wording below implements Roger's suggestion.
Proposed resolution:
This wording is relative to N4810.
Modify 31.7.6.2.4 [ostream.sentry], class basic_ostream::sentry synopsis, as indicated:
namespace std { template<class charT, class traits = char_traits<charT>> class basic_ostream<charT, traits>::sentry { bool ok_; // exposition only int uncaught_ = uncaught_exceptions(); // exposition only public: explicit sentry(basic_ostream<charT, traits>& os); ~sentry(); explicit operator bool() const { return ok_; } sentry(const sentry&) = delete; sentry& operator=(const sentry&) = delete; }; }[…]
~sentry();-4- If
(os.flags() & ios_base::unitbuf) &&is!uncaught_exceptions() <= uncaught_ && os.good()true, callsos.rdbuf()->pubsync(). If that function returns-1, setsbadbitinos.rdstate()without propagating an exception.
basic_streambuf is not an abstract classSection: 31.6.3 [streambuf] Status: New Submitter: Jonathan Wakely Opened: 2015-05-28 Last modified: 2015-08-03
Priority: 3
View all other issues in [streambuf].
View all issues with New status.
Discussion:
31.6.3 [streambuf] p1 says:
The class template
basic_streambuf<charT, traits>serves as an abstract base class for deriving various stream buffers whose objects each control two character sequences: […]
The term "abstract base class" is not defined in the standard, but "abstract class" is (11.7.4 [class.abstract]).
According to the synopsisbasic_streambuf has no pure virtual
functions so is not an abstract class and none of libstdc++, libc++, or
dinkumware implement it as an abstract class. I don't believe the wording was
ever intended to require it to be an abstract class, but it could be
read that way.
I suggest the wording be changed to "polymorphic base class" or
something else that can't be seen to imply a normative requirement to
make it an abstract class.
Proposed resolution:
Section: 6.10.2 [intro.multithread], 32.5.8 [atomics.types.generic], 17.14 [support.runtime] Status: SG1 Submitter: Geoffrey Romer Opened: 2015-05-29 Last modified: 2018-03-15
Priority: 3
View all other issues in [intro.multithread].
View all issues with SG1 status.
Discussion:
The concurrency libraries specified in clauses 29 and 30 do not adequately specify how they relate to the concurrency model specified in 6.10.2 [intro.multithread]. In particular:
6.10.2 [intro.multithread] specifies "atomic objects" as having certain properties. I can only assume that instances of the classes defined in Clause 29 are intended to be "atomic objects" in this sense, but I can't find any wording to specify that, and it's genuinely unclear whether Clause 30 objects are atomic objects. In fact, on a literal reading the C++ Standard doesn't appear to provide any portable way to create an atomic object, or even determine whether an object is an atomic object. (It's not clear if the term "atomic object" is actually needed, given that atomic objects can have non-atomic operations, and non-atomic objects can have atomic operations. But even if the term itself goes away, there still needs to be some indication that Clause 29 objects have the properties currently attributed to atomic objects). Similarly, 6.10.2 [intro.multithread] uses "atomic operation" as a term of art, but the standard never unambiguously identifies any operation as an "atomic operation" (although in one case it unambiguously identifies an operation that is not atomic). It does come close in a few cases, but not close enough:6.10.2 [intro.multithread]/p7 could be read to imply that "synchronization operations" in Clauses 29 and 30 are also atomic operations. However, that's vague and indirect, and somewhat belied by 32.6.4.2 [thread.mutex.requirements.mutex]/p5, which specifies that mutex lock and unlock operations "behave as atomic operations", but only "for purposes of determining the existence of a data race". Furthermore, not a single operation in Clause 29 explicitly identifies itself as a "synchronization operation".
32.5.8 [atomics.types.generic]/p4 states in part that "There shall be a specialization atomic<bool>
which provides the general atomic operations as specified in 29.6.1", but read in context, "general atomic operations"
appears to be a loose synonym for "general operations on atomic types" as defined in [atomics.types.operations.general],
rather than a use of "atomic object" as Words of Power. Incidentally, "atomic type" is never satisfactorily defined either
(although the <atomic> synopsis comes close).
17.14 [support.runtime]/p10 specifies exactly which operations are "plain lock-free atomic operations", but in a standard where an "integral constant expression" isn't necessarily a "constant expression", I do not feel safe assuming that a "plain lock-free atomic operation" is an "atomic operation".
Hans Boehm tells me the operations with "atomically" in the Effects element are intended to be atomic operations, but since "atomic operation" is a term of art (e.g. in 6.10.2 [intro.multithread]/p27.4), I think this needs to be spelled out rather than assumed. Furthermore, this does not help with 32.5.11 [atomics.fences], or anything in Clause 30.
[2018-03 JAX; Geoffrey comments in behalf of SG1]
SG1 consensus is that operations outside clause 32 are not "atomic operations", and objects of types defined outside clause 32 are not "atomic objects". "Synchronization operations" are operations which act as endpoints of primitive edges of partial orders other than sequenced-before, but it may make more sense to just drop that term and inline the definition, so to speak.
We would welcome a paper to make those definitions more explicit, and revise the wording as needed to be consistent with those definitions.Proposed resolution:
codecvt_mode should be a bitmask typeSection: 99 [depr.locale.stdcvt] Status: New Submitter: Jonathan Wakely Opened: 2015-06-08 Last modified: 2017-04-22
Priority: 3
View all other issues in [depr.locale.stdcvt].
View all issues with New status.
Discussion:
The enumeration type codecvt_mode is effectively a bitmask type
(16.3.3.3.3 [bitmask.types]) with three elements, but isn't defined as
such.
codecvt_mode doesn't have overloaded
operators, making it very inconvenient to combine values:
std::codecvt_utf16<char32_t, 0x10FFFF, static_cast<std::codecvt_mode>(std::little_endian|std::generate_header)> cvt;
The static_cast harms readability and should not be necessary.
codecvt_mode is specified to be a bitmask type,
or as a minimal fix we provide an overloaded operator| that returns
the right type.
Proposed resolution:
Section: 17.6.3.5 [new.delete.dataraces] Status: New Submitter: Hans Boehm Opened: 2015-06-09 Last modified: 2016-02-01
Priority: 3
View all other issues in [new.delete.dataraces].
View all issues with New status.
Discussion:
17.6.3.5 [new.delete.dataraces] uses obsolete wording.
It should introduce a "synchronizes with" relationship. "Happens before" is too weak, since that may not composes with sequenced before. The "shall not introduce a data race" wording is probably not technically correct either. These may race with other (non-allocation/deallocation) concurrent accesses to the object being allocated or deallocated.Proposed resolution:
Section: 28.3.4.6.2.3 [locale.time.get.virtuals] Status: Open Submitter: Hubert Tong Opened: 2015-06-19 Last modified: 2017-09-07
Priority: 4
View other active issues in [locale.time.get.virtuals].
View all other issues in [locale.time.get.virtuals].
View all issues with Open status.
Discussion:
I recently encountered a failure related to questionable use of do_get_year. The platform where the code happened
to work had an implementation which handled certain three-digit "year identifiers" as the number of years since
1900 (this article describes such an implementation).
[2016-08 Chicago]
Wed PM: This has been this way since C++98. Don't think it's a P2.
Change to P4, and move to Open.
Proposed resolution:
basic_string::value_typeSection: 27.1 [strings.general] Status: New Submitter: Jonathan Wakely Opened: 2015-06-26 Last modified: 2020-09-06
Priority: 4
View all other issues in [strings.general].
View all issues with New status.
Discussion:
The allocator-aware container requirements in Table 98 impose no
MoveAssignable requirements on the value_type when
propagate_on_container_move_assignment is true, because typically the
container's storage would be moved by just exchanging some pointers.
basic_string using the small string optimization move
assignment may need to assign individual characters into the small
string buffer, even when the allocator propagates.
The only requirement on the char-like objects stored in a basic_string
are that they are non-array POD types and Destructible, which means
that a POD type with a deleted move assignment operator should be
usable in a basic_string, despite it being impossible to move assign:
#include <string>
struct odd_pod
{
odd_pod() = default;
odd_pod& operator=(odd_pod&&) = delete;
};
static_assert(std::is_pod<odd_pod>::value, "POD");
int main()
{
using S = std::basic_string<odd_pod>;
S s;
s = S{}; // fails
}
Using libstdc++ basic_string<odd_pod> cannot even be
default-constructed because the constructor attempts to assign the
null terminator to the first element of the small string buffer.
basic_string should require its value_type to be at
least DefaultConstructible and MoveAssignable.
[2016-06, Oulu]
This should be resolved by P0178
Note: P0178 was sent back to LEWG in Oulu.
Proposed resolution:
std::tuple construction unspecifiedSection: 22.4.4.2 [tuple.cnstr] Status: New Submitter: Brian Rodriguez Opened: 2015-08-25 Last modified: 2017-02-19
Priority: 3
View other active issues in [tuple.cnstr].
View all other issues in [tuple.cnstr].
View all issues with New status.
Discussion:
The std::tuple order of element construction is unspecified. It is either in the same order of the type list or in reverse.
#include <iostream>
#include <tuple>
struct X
{
X(int) { std::cout << "X constructor\n"; }
};
struct Y
{
Y(int) { std::cout << "Y constructor\n"; }
};
int main()
{
std::tuple<X, Y> t(1, 2);
}
Here is a link to two sample compilations. The first uses libstdc++ and constructs in reverse order, and the second uses libc++ and constructs in in-order.
Astd::tuple mimics both a struct and type-generic container and should thus follow their standards. Construction is
fundamentally different from a function call, and it has been historically important for a specific order to be guaranteed;
namely: whichever the developer may decide. Mandating construction order will allow developers to reference younger elements
later on in the chain as well, much like a struct allows you to do with its members.
There are implementation issues as well. Reversed lists will require unnecessary overhead for braced-initializer-list initialization.
Since lists are evaluated from left to right, the initializers must be placed onto the stack to respect the construction order.
This issue could be significant for large tuples, deeply nested tuples, or tuples with elements that require
many constructor arguments.
I propose that the std::tuple<A, B, ..., Y, Z>'s constructor implementation be standardized, and made to construct
in the same order as its type list e.g. A{}, B{}, ..., Y{}, Z{}.
Daniel:
When N3140 became accepted, wording had been added that gives at least an indication of requiring element initialization in the order of the declaration of the template parameters. This argumentation can be based on 22.4.4.2 [tuple.cnstr] p3 (emphasize mine):-3- In the constructor descriptions that follow, let
ibe in the range[0,sizeof...(Types))in order,Tibe theithtype inTypes, andUibe theithtype in a template parameter pack namedUTypes, where indexing is zero-based.
But the current wording needs to be improved to make that intention clearer and an issue like this one is necessary to be sure that
the committee is agreeing (or disagreeing) with that intention, especially because N3140 didn't really point out the relevance of the element
construction order in the discussion, and because not all constructors explicitly refer to the ordered sequence of numbers generated
by the variable i (The move constructor does it right, but most other don't do that).
[2017-02-12, Alisdair comments]
Note that this issue should not be extended to cover the assignment operators,
as implementations may want the freedom to re-order member-wise assignment
so that, for example, all potentially-throwing assignments are performed before
non-throwing assignments (as indicated by the noexcept operator).
Proposed resolution:
Section: 32.10.5 [futures.state] Status: Open Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2016-08-06
Priority: 3
View all other issues in [futures.state].
View all issues with Open status.
Discussion:
When a shared-state is released, it may be necessary to execute user defined code for the destructor of a stored value or exception. It is unclear whether the execution of said destructor constitutes an observable side effect.
While discussing N4445 in Lenexa, Nat Goodspeed pointed out that 32.10.5 [futures.state]/5.1 does not explicitly mention the destruction of the result, so implementations should be allowed to release (or reuse) a shared state ahead of time under the "as-if" rule.
The standard should clarify whether the execution of destructors is a visible side effect of releasing a shared state.[2016-08-03 Chicago]
Fri AM: Moved to Open
Proposed resolution:
promise at thread exitSection: 32.10.6 [futures.promise] Status: Open Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2016-08-06
Priority: 3
View all other issues in [futures.promise].
View all issues with Open status.
Discussion:
promise::set_value_at_thread_exit and promise::set_exception_at_thread_exit operate on a shared state
at thread exit, without making the thread participate in the ownership of such shared state.
std::promise<int>{}.set_value_at_thread_exit(42);
Arguably, since the promise abandons its shared state without actually making it ready, a broken_promise
error condition should be stored in the shared state. Implementations diverge, they either crash at thread exit by
dereferencing an invalid pointer, or keep the shared state around until thread exit.
[2016-08-03 Chicago]
[2016-08-03, Billy O'Neal suggests concrete wording]
Fri AM: Moved to Open
Proposed resolution:
This wording is relative to N4606.
Change 32.10.5 [futures.state] p7 as indicated:
-7- When an asynchronous provider is said to abandon its shared state, it means:
(7.1) — first, if that state is not ready or scheduled to be made ready at thread exit, the provider
(7.1.1) — stores an exception object of type
future_errorwith an error condition ofbroken_promisewithin its shared state; and then(7.1.2) — makes its shared state ready;
Change 32.10.5 [futures.state] p10 as indicated:
-10- Some functions (e.g.,
promise::set_value_at_thread_exit)delay making the shared state ready untilschedule the shared state to be made ready when the calling thread exits. This associates a reference to the shared state with the calling thread. The destruction of each of that thread's objects with thread storage duration (6.8.6.3 [basic.stc.thread]) is sequenced before making that shared state ready. When the calling thread makes the shared state ready, if the thread holds the last reference to the shared state, the shared state is destroyed. [Note: This means that the shared state may not become ready until after the asynchronous provider has been destroyed. — end note]
future::then can run a continuationSection: 99 [concurr.ts::futures.unique.future] Status: Open Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2025-10-20
Priority: 4
View all issues with Open status.
Discussion:
Addresses: concurr.ts
In N4538, the continuation given to
future::then can be run "on an unspecified thread of execution". This is too broad, as it allows the
continuation to be run on the main thread, a UI thread, or any other thread. In comparison, functions given to
async run "as if in a new thread of execution", while the Parallelism TS gives less guarantees by running
"in either the invoking thread or in a thread implicitly created by the library to support parallel algorithm execution".
The threads on which the continuation given to future::then can run should be similarly constrained.
[2017-03-01, Kona, SG1]
Agreement that this is a problem. Suggested addition to the issue is below. We have no immediate delivery vehicle for a fix at the moment, but we would like to make the intended direction clear.
There is SG1 consensus that.then continuations should, by default, and in the absence of executors, be run
only in the following ways:
If the future is not ready when .then() is called, the .then argument may be run on the execution
agent that fulfills the promise.
In all cases, the .then argument may be run on an implementation-provided thread, i.e. a thread that is
neither the main thread nor explicitly created by the user.
In the absence of an executor argument (which currently cannot be supplied), running of the .then() continuation
will not block the thread calling .then(), even if the future is ready at the time.
.then caller"
0 | 0 | 5 | 5 | 3
"3. Leave as implementation defined"
1 | 2 | 4 | 3 | 3
"4. Always new execution agent"
2 | 3 | 6 | 2 | 0
The actual conclusion was to allow either (1) or (4) for now, since they are quite close, but present a very different
programming mode from (2).
[2025-10-20; Reflector poll.]
Set priority to 4 based on age of issue and apparent lack of interest.
Proposed resolution:
Section: 28.6.12 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2015-10-08 Last modified: 2024-10-03
Priority: 4
View other active issues in [re.grammar].
View all other issues in [re.grammar].
View all issues with New status.
Discussion:
In 28.6.12 [re.grammar] paragraph 2:
basic_regexmember functions shall not call any locale dependent C or C++ API, including the formatted string input functions. Instead they shall call the appropriate traits member function to achieve the required effect.
Yet, the required interface for a regular expression traits class (28.6.2 [re.req]) does not appear to have
any reliable method for determining whether a character as encoded for the locale associated with the traits
instance is the same as a character represented by a UnicodeEscapeSequence, e.g., assuming a sane
ru_RU.koi8r locale:
#include <stdio.h>
#include <stdlib.h>
#include <regex>
const char data[] = "\xB3";
const char matchCyrillicCaptialLetterYo[] = R"(\u0401)";
int main(void)
{
try {
std::regex myRegex;
myRegex.imbue(std::locale("ru_RU.koi8r"));
myRegex.assign(matchCyrillicCaptialLetterYo, std::regex_constants::ECMAScript);
printf("(%s)\n", std::regex_replace(std::string(data), myRegex, std::string("E")).c_str());
myRegex.assign("[[:alpha:]]", std::regex_constants::ECMAScript);
printf("(%s)\n", std::regex_replace(std::string(data), myRegex, std::string("E")).c_str());
} catch (std::regex_error& e) {
abort();
}
return 0;
}
The implementation I tried prints:
(Ё) (E)
Which means that the character class matching worked, but not the matching to the UnicodeEscapeSequence.
[2024-10-03; Jonathan comments]
std::basic_regex<charT> only properly supports
matching single code units that fit in charT.
There's nothing in the spec that supports matching code points that
require multiple code units, let alone checking whether a character
in an arbitrary encoding corresponds to any given Unicode code point.
28.6.12 [re.grammar] paragraph 12 appears to be an attempt to
allow implementations to fail to match here, but is insufficient.
When is_unsigned_v<char> is true, the CV of the
UnicodeEscapeSequence "\u0080" is not greater than CHAR_MAX,
but that doesn't help because U+0080 is encoded as two bytes in UTF-8.
Being able to represent 0x80 as char does not mean the CV can be
matched as a single char.
The API is unsuitable for Unicode-aware strings.
Proposed resolution:
Section: 22.10.8 [comparisons], 23.2.2 [container.requirements.general], 32.4.3.2 [thread.thread.id] Status: New Submitter: Matt Austern Opened: 2015-10-08 Last modified: 2015-10-21
Priority: 3
View other active issues in [comparisons].
View all other issues in [comparisons].
View all issues with New status.
Discussion:
A number of places in the library, including 22.10.8 [comparisons]/14, the Optional container requirements in
23.2.2 [container.requirements.general], and 32.4.3.2 [thread.thread.id]/8, use the phrase "total order".
Unfortunately, that phrase is ambiguous. In mathematics, the most common definition is that a relation ≤ is
a total order if it's total, transitive, and antisymmetric in the sense that x≤y ∧ y≤x ⇒ x=y.
What we really want is a strict total order: a relation < is a strict total order if it's total, transitive, and
antisymmetric in the sense that exactly one of x<y, y<x, and x=y holds.
The non-normative note in 26.8 [alg.sorting]/4 correctly uses the phrase "strict total ordering" rather than simply "total ordering".
We could address this issue by replacing "total order" with "strict total order" everywhere it appears, since I think there are no cases where we actually want a non-strict total order, or we could add something in Clause 17 saying that we always mean strict total order whenever we say total order.
Proposed resolution:
chrono::duration_casts from smaller durations to larger durations do not overflowSection: 30.2 [time.syn] Status: New Submitter: Andy Giese Opened: 2016-02-05 Last modified: 2016-05-08
Priority: 4
View all other issues in [time.syn].
View all issues with New status.
Discussion:
Currently 30.2 [time.syn] states
// convenience typedefs typedef duration<signed integer type of at least 64 bits, nano> nanoseconds; typedef duration<signed integer type of at least 55 bits, micro> microseconds; typedef duration<signed integer type of at least 45 bits, milli> milliseconds; typedef duration<signed integer type of at least 35 bits > seconds; typedef duration<signed integer type of at least 29 bits, ratio< 60>> minutes; typedef duration<signed integer type of at least 23 bits, ratio<3600>> hours;
However, a duration_cast<minutes>(seconds::max()) would cause overflow if the underlying signed integers
only met the minimums specified.
duration_cast from any smaller duration in
these "convenience typedefs" will not overflow any larger duration. That is, hours should be able to hold
the maximum of minutes, which should be able to hold the maximum of seconds and so on.
More formally, if the ratio typedef A and typedef B is 1:Y where Y > 1 (e.g.,
1 : 60 in case of minutes : seconds), then #bitsA-1 must be at least
ceil(log2(2#bitsB-1)/Y)).
In the case of minutes : seconds, X = 1, Y = 60. Let
#bitsseconds = 32. Therefore:
2(#bitsseconds - 1) = 231 = 2147483648
ceil(log2(231 / 60) = 26
#bitsminutes - 1 = 26
#bitsminutes = 27
Therefore, a minimum of 27 bits would be needed to store minutes if 32 were used to store seconds.
// convenience typedefs typedef duration<signed integer type of at least 64 bits, nano> nanoseconds; typedef duration<signed integer type of at least 55 bits, micro> microseconds; typedef duration<signed integer type of at least 46 bits, milli> milliseconds; typedef duration<signed integer type of at least 37 bits > seconds; typedef duration<signed integer type of at least 32 bits, ratio< 60>> minutes; typedef duration<signed integer type of at least 27 bits, ratio<3600>> hours;
These bits were chosen to satisfy the above formula. Note that
minimums only increased, so larger ranges could be held. A nice
outcome of this choice is that minutes does not go above 32 bits.
[2016-04-23, Tim Song comments]
The P/R of LWG 2592 doesn't fix the issue it wants to solve, because the actual underlying type will likely have more bits than the specified minimum.
Considerseconds, which the P/R requires to have at least 37 bits. On a typical system this implies
using a 64-bit integer. To ensure that casting from seconds::max() to minutes doesn't overflow
in such a system, it is necessary for the latter to have at least 59 bits (which means, in practice, 64 bits too),
not just 32 bits. Thus, just changing the minimum number of bits will not be able to provide the desired guarantee
that casting from a smaller unit to a larger one never overflow.
If such a guarantee is to be provided, it needs to be spelled out directly. Note that the difference here is 9 bits
(for the 1000-fold case) and 5 bits (for the 60-fold case), which is less than the size difference between integer
types on common systems, so such a requirement would effectively require those convenience typedefs to use the
same underlying integer type.
Proposed resolution:
This wording is relative to N4567.
Change 30.2 [time.syn], header <chrono> synopsis, as indicated
[…] // convenience typedefs typedef duration<signed integer type of at least 64 bits, nano> nanoseconds; typedef duration<signed integer type of at least 55 bits, micro> microseconds; typedef duration<signed integer type of at least 4645bits, milli> milliseconds; typedef duration<signed integer type of at least 3735bits > seconds; typedef duration<signed integer type of at least 3229bits, ratio< 60>> minutes; typedef duration<signed integer type of at least 2723bits, ratio<3600>> hours; […]
shared_ptr on shared_ptr(nullptr, d)Section: 20.3.2.2 [util.smartptr.shared] Status: New Submitter: Kazutoshi Satoda Opened: 2016-02-20 Last modified: 2016-06-20
Priority: 3
View all other issues in [util.smartptr.shared].
View all issues with New status.
Discussion:
Latest draft (N4567) 20.3.2.2 [util.smartptr.shared] p1 says:
A
shared_ptrobject is empty if it does not own a pointer.
Please note that it says "own a pointer". This definition was added as the resolution for LWG defect 813(i).
20.3.2.2.2 [util.smartptr.shared.const] p8 says about the effect ofshared_ptr(nullptr_t p, D d):
Effects: Constructs a
shared_ptrobject that owns the objectpand the deleterd.
Please note that it says "owns the object". This was intentionally
changed from "the pointer" as a part of resolution for LWG defect 758(i),
to cover nullptr_t case.
shared_ptr(nullptr, d) owns an object of type nullptr_t, but does
not own a pointer, it is said as "empty" by a strict reading of the
above mentioned definition in 20.3.2.2 [util.smartptr.shared] p1.
These cause a contradiction:
20.3.2.2.2 [util.smartptr.shared.const] p9 sets a postcondition
use_count() == 1 on shared_ptr(nullptr, d). But
20.3.2.2.6 [util.smartptr.shared.obs] p7 says that the return value of use_count()
is "0 when *this is empty".
Proposed wording changes:
Replace the last 2 words in 20.3.2.2 [util.smartptr.shared] p1 from[…] empty if it does not own a pointer.
to
[…] empty if it does not own an object.
Note that shared_ptr(nullptr_t) is defined to be empty in synopsis in
20.3.2.2 [util.smartptr.shared].
constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }
It could be less confusing if shared_ptr(nullptr, d) could be defined to
be empty. But it seems too late to change that (which means changing
whether the deleter is called or not, see
this Stackoverflow article).
Then I'm proposing just fix the contradiction.
Proposed resolution:
This wording is relative to N4594.
Change 20.3.2.2 [util.smartptr.shared] p1 as indicated:
-1- The
shared_ptrclass template stores a pointer, usually obtained vianew.shared_ptrimplements semantics of shared ownership; the last remaining owner of the pointer is responsible for destroying the object, or otherwise releasing the resources associated with the stored pointer. Ashared_ptrobject is empty if it does not own an objecta pointer.
reverse_iterator::operator[]'s return type revisitedSection: 24.5.1.2 [reverse.iterator], 24.5.1.6 [reverse.iter.elem] Status: New Submitter: Robert Haberlach Opened: 2016-02-28 Last modified: 2021-06-06
Priority: 3
View all other issues in [reverse.iterator].
View all issues with New status.
Discussion:
Issue 386(i) changed the return type of reverse_iterator::operator[] to unspecified. However,
as of N3066, the return type of a random access iterator's operator[] shall be convertible to reference;
thus the return type of reverse_iterator::operator[] should be reference (and it is in all common
implementations).
reference instead of unspecified.
[2021-06-06 Tim syncs wording to current working draft]
Proposed resolution:
This wording is relative to N4885.
Edit 24.5.1.2 [reverse.iterator], class template synopsis, as indicated:
namespace std {
template <class Iterator>
class reverse_iterator {
public:
[…]
using reference = iter_reference_t<Iterator>;
[…]
constexpr referenceunspecified operator[](difference_type n) const;
[…]
};
}
Change 24.5.1.6 [reverse.iter.elem] before p3 as indicated:
constexpr referenceunspecifiedoperator[](difference_type n) const;
Section: 22.2.6 [declval], 20.3.1 [unique.ptr], 20.3.1.2.1 [unique.ptr.dltr.general], 20.3.2.2 [util.smartptr.shared], 20.3.2.3 [util.smartptr.weak], 20.3.2.7 [util.smartptr.enab] Status: New Submitter: Zhihao Yuan Opened: 2016-03-08 Last modified: 2016-04-16
Priority: 3
View other active issues in [declval].
View all other issues in [declval].
View all issues with New status.
Discussion:
Currently the phrase to grant this permission is:
The template parameter
TofLibraryTemplatemay be an incomplete type.
Two problems:
The timing is unclear. We always allow specializations like LibraryTemplate<Incomp>* p;
To the users of a template, the correct terminology should be "argument" rather than "parameter".
Suggested resolution:
In an instantiation of
LibraryTemplate, an incomplete type may be used as the template argument for the template parameterT.
as shown here.
Or, to copy N4510's wording:An incomplete type
Tmay be used when instantiatingLibraryTemplate.
Proposed resolution:
register_callback can failSection: 31.5.2.7 [ios.base.callback] Status: New Submitter: David Krauss Opened: 2016-03-14 Last modified: 2019-06-15
Priority: 3
View all issues with New status.
Discussion:
register_callback allocates memory and so it can fail, but the case is unspecified. libc++ sets
badbit, which is consistent with iword and pword. libstdc++ throws std::bad_alloc.
[2019-06-13; Billy comments]
Just as an additional data point: MSVC++ agrees with libstdc++ and also throws std::bad_alloc.
Proposed resolution:
money_base::space and do_put: U+0020 versus fillSection: 28.3.4.7.4 [locale.moneypunct] Status: New Submitter: Hubert Tong Opened: 2016-04-12 Last modified: 2016-05-22
Priority: 3
View all other issues in [locale.moneypunct].
View all issues with New status.
Discussion:
The description of money_base::space is that "at least one space is required at that position."
(N4582 subclause 22.4.6.3 [locale.moneypunct] paragraph 2)
"the number of characters generated for the specified format" (excluding fill padding) includes exactly
one character for money_base::space (if present), and
all characters corresponding to money_base::space (excluding fill padding) are copies of fill.
In particular, there is implementation divergence over point (b) as to whether U+0020 or fill should be used.
Further, should a character other than fill be used, it is unclear when "the fill characters are
placed where none or space appears in the formatting pattern", whether the fill characters are placed
at the beginning or the end of the "space field".
fill is more likely
to be the pragmatic choice.
Proposed resolution:
This wording is relative to N4582.
Change 28.3.4.7.4 [locale.moneypunct] paragraph 2 as indicated:
-2- Where
noneorspaceappears, white space is permitted in the format, except wherenoneappears at the end, in which case no white space is permitted. For input, the valuespaceindicates that at least one space is required at that position. For output, the valuespaceindicates one instance of the fill character (28.3.4.7.3.3 [locale.money.put.virtuals]).The value. Wherespaceindicates that at least one space is required at that positionsymbolappears, the sequence of characters returned bycurr_symbol()is permitted, and can be required. Wheresignappears, the first (if any) of the sequence of characters returned bypositive_sign()ornegative_sign()(respectively as the monetary value is non-negative or negative) is required. Any remaining characters of the sign sequence are required after all other format components. Wherevalueappears, the absolute numeric monetary value is required.
Section: 16.4.6.5 [member.functions] Status: New Submitter: Hubert Tong Opened: 2016-04-15 Last modified: 2017-02-02
Priority: 3
View other active issues in [member.functions].
View all other issues in [member.functions].
View all issues with New status.
Discussion:
In N4582 subclause 17.6.5.5 [member.functions], the requirement that:
any call to the member function that would select an overload from the set of declarations described in this standard behaves as if that overload were selected
is unclear in the extent of the "as if". For example, in providing:
basic_string(const charT* s);
for a one-argument call to:
basic_string(const charT* s, const Allocator& a = Allocator());
it can be read that an implementation may be required to call the copy constructor for the allocator since the core language rules for copy elision would not allow the "a" argument to be constructed directly into the member used to store the allocator.
Clarification (even if just a note) would be appreciated.[2016-05 Issues Telecon]
This is related to issue 2563(i).
Proposed resolution:
num_put::do_put(..., bool) performs ill-formed do_put callSection: 28.3.4.3.3.3 [facet.num.put.virtuals] Status: New Submitter: Hubert Tong Opened: 2016-05-07 Last modified: 2016-05-22
Priority: 3
View other active issues in [facet.num.put.virtuals].
View all other issues in [facet.num.put.virtuals].
View all issues with New status.
Discussion:
The call to do_put(out, str, fill, (int)val) in N4582 subclause 28.3.4.3.3.3 [facet.num.put.virtuals]
paragraph 6 cannot select a best viable function in overload resolution given the overloads listed for
do_put in 28.3.4.3.3 [locale.nm.put].
Some implementations call the long overload (as overriden);
some implementations call the unsigned long overload (as overriden);
some implementations call something else.
It appears that the resolution to DR 359(i) attempted a fix; however, the relevant portion of the change was not applied to the WP.
Proposed resolution:
boolalpha is setSection: 28.3.4.3.3.3 [facet.num.put.virtuals] Status: New Submitter: Hubert Tong Opened: 2016-05-07 Last modified: 2018-02-11
Priority: 3
View other active issues in [facet.num.put.virtuals].
View all other issues in [facet.num.put.virtuals].
View all issues with New status.
Discussion:
N4582 subclause 28.3.4.3.3.3 [facet.num.put.virtuals] paragraph 6 makes no provision for fill-padding in its
specification of the behaviour when (str.flags() & ios_base::boolalpha) != 0.
[2017-07-06, Marshall comments]
All the other cases from num_put — int, long, etc all are covered in
28.3.4.3.3.3 [facet.num.put.virtuals] p1 .. p5, which describe how to align and pad the output. (Specifically,
stage 3) p6 does not.
cout << std::setw(15) << false;
outputs:
0 ﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎﹎// Column counter
but
cout << std::setw(15) << boolalpha << false;
outputs:
false
libc++ implements this exactly.
Dinkumware, libstdc++ and MSVC apply padding and alignment.
Proposed resolution:
a.assign(n, t)Section: 23.2.4 [sequence.reqmts] Status: New Submitter: Kazutoshi Satoda Opened: 2016-05-08 Last modified: 2020-09-06
Priority: 3
View other active issues in [sequence.reqmts].
View all other issues in [sequence.reqmts].
View all issues with New status.
Discussion:
Please look through the following modifications on a value v of type vector<T>:
assert(v.size() > 0); v.push_back(v[0]); v.insert(v.begin(), v[0]); v.resize(v.size() * 2, v[0]); v.assign(v.size() * 2, v[0]);
All of these use an element of itself which may be moved or destroyed by the modification.
From what I see so far, the first three are required to work. Please see library issue 526(i) for validity of them. But only the last one is undefined because it violates a precondition of a sequence container operation. I think this is too subtle. Should it be like that, really? The precondition is in Table 107 "Sequence container requirements" at the next of 23.2.4 [sequence.reqmts] p3.In Tables 107 and 108,
Xdenotes a sequence container class,adenotes a value ofXcontaining elements of typeT, […]ndenotes a value ofX::size_type, […]tdenotes an lvalue or aconstrvalue ofX::value_type, […][…]
Table 107 — Sequence container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-condition[…]a.assign(n, t)voidRequires: Tshall beCopyInsertableintoXandCopyAssignable.
pre:tis not a reference intoa.
Replaces elements inawithncopies oft.
I looked into the following implementations:
libc++ relies on the precondition.
It deallocates first onn > capacity() case,
see here.
libstdc++ doesn't rely on the precondition.
It creates temporaryvector(n, t) and swap()
on n > capacity() case, see
here.
MSVC relies on the precondition.
It unconditionally doesclear() and then insert(begin(), n, t).
I looked into my local "%PROGRAMFILES(X86)%/Microsoft Visual Studio 14.0/VC/include/vector".
One drawback of libstdc++ implementation, I could find so far, is
possibly increased peek memory usage (both old and new buffer exist at
the same time). But, because the same can happen on the most other
modifications, it seems a reasonable trade-off to remove the
precondition to fill the subtle gap. Users who really needs less memory
usage can do clear() and insert() by themselves.
basic_string::assign(n, c) is safe on this point.
At 27.4.3.7.3 [string.assign] p17:
basic_string& assign(size_type n, charT c);Effects: Equivalent to
Returns:assign(basic_string(n, c)).*this.
This can be seen as another gap.
Looking back on the history, I found that the definition ofassign(n, t)
was changed at C++14 for library issue 2209(i). There were more restricting
definitions like this:
void assign(size_type n, const T& t);Effects:
erase(begin(), end()); insert(begin(), n, t);
I think the precondition was probably set to accept this old definition and is not required inherently. And if the less memory usage was really intended, the standard is now underspecifying about that.
[2016-05 Issues Telecon]
Howard believes this should be NAD, but we tabled the discussion.
Previous resolution [SUPERSEDED]:This wording is relative to N4582.
In 23.2.4 [sequence.reqmts], edit Table 107 (Sequence container requirements) as indicated:
Table 107 — Sequence container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-condition[…]a.assign(n, t)voidRequires: Tshall beCopyInsertableintoXandCopyAssignable.
pre:tis not a reference intoa.
Replaces elements inawithncopies oft.
[2020-04-25, Daniel syncs current wording with recent working draft]
Proposed resolution:
This wording is relative to N4861.
In 23.2.4 [sequence.reqmts], edit Table [tab:container.seq.req] (Sequence container requirements) as indicated:
Table 77 — Sequence container requirements (in addition to container) [tab:container.seq.req] Expression Return type Assertion/note
pre-/post-condition[…]a.assign(n, t)voidPreconditions: Tis Cpp17CopyInsertable intoX
and Cpp17CopyAssignable.tis not a reference intoa.
Effects: Replaces elements inawithncopies oft.
Invalidates all references, pointers and iterators referring to the elements ofa.
Forvectoranddeque, also invalidates the past-the-end iterator.
recursive_directory_iterator::recursion_pending() is incorrectly specifiedSection: 31.12.12.2 [fs.rec.dir.itr.members] Status: Open Submitter: Eric Fiselier Opened: 2016-05-09 Last modified: 2022-12-18
Priority: 2
View all other issues in [fs.rec.dir.itr.members].
View all issues with Open status.
Discussion:
The current specification of recursion_pending() says (31.12.12.2 [fs.rec.dir.itr.members]/24):
Returns:
trueifdisable_recursion_pending()has not been called subsequent to the prior construction or increment operation, otherwisefalse.
This language does not take into account cases where the prior construction was a copy construction from a iterator,
it, where it.recursion_pending() == false.
[2016-08 Chicago]
Wed AM: Move to Open
[2018-1-26 issues processing telecon]
Status to 'Tentatively Ready'; Casey will explore whether making recursion_pending an exposition-only member makes this clearer.
This wording is relative to N4582.
Change 31.12.12.2 [fs.rec.dir.itr.members] as indicated:
explicit recursive_directory_iterator(const path& p); recursive_directory_iterator(const path& p, directory_options options); recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept; recursive_directory_iterator(const path& p, error_code& ec) noexcept;[…]
-3- Postcondition:options() == optionsfor the signatures with adirectory_optionsargument, otherwiseoptions() == directory_options::none.
options() == optionsfor the signatures with adirectory_optionsargument, otherwiseoptions() == directory_options::none.
recursion_pending() == true.[…]
[Drafting note: The following changes the specification of
recursion_pending()seemingly recursive. Perhaps it would be easier to specifyrecursion_pending()in terms of a exposition only member inrecursive_directory_iterator.]bool recursion_pending() const;[…]
-24- Returns:trueifdisable_recursion_pending()has not been called subsequent to the prior construction or increment operation, otherwisefalsefalseifdisable_recursion_pending()has been called subsequent to the prior construction or increment operation, otherwise the value ofrecursion_pending()set by that operation. […]recursive_directory_iterator& operator++(); recursive_directory_iterator& increment(error_code& ec) noexcept;[…]
-27- Effects: As specified by Input iterators (24.2.3), except that: […] -?- Postcondition:recursion_pending() == true.
[2018-01-29: Casey provides a PR with an exposition-only member]
Status to 'Review'.
Previous resolution from Casey [SUPERSEDED]:This wording is relative to N4713.
Change [fs.rec.dir.itr] as indicated:
[…] // other members as required by 24.3.5.3 [input.iterators], input iterators private: bool recurse_; // exposition-only }; }Change 31.12.12.2 [fs.rec.dir.itr.members] as indicated:
explicit recursive_directory_iterator(const path& p); recursive_directory_iterator(const path& p, directory_options options); recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept; recursive_directory_iterator(const path& p, error_code& ec) noexcept;[…]
-3- Postconditions:
options() == optionsfor the signatures with adirectory_optionsargument, otherwiseoptions() == directory_options::none.
options() == optionsfor the signatures with adirectory_optionsargument, otherwiseoptions() == directory_options::none.
recurse_ == true.[…]
recursive_directory_iterator(const recursive_directory_iterator& rhs);[…]
-8- Postconditions:
[…]
(8.3) —
recursion_pending() == rhs.recursion_pending()recurse_ == rhs.recurse_recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;[…]
-10- Postconditions:
options(),depth(), andrecursion_pending()recurse_have the values thatrhs.options(),rhs.depth(), andrhs.recursion_pending()rhs.recurse_, respectively, had before the function call.recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);[…]
-12- Postconditions:
[…]
(12.3) —
recursion_pending() == rhs.recursion_pending()recurse_ == rhs.recurse_[…]
recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;[…]
-15- Postconditions:
options(),depth(), andrecursion_pending()recurse_have the values thatrhs.options(),rhs.depth(), andrhs.recursion_pending()rhs.recurse_, respectively, had before the function call.[…]
bool recursion_pending() const;-21- Returns:
recurse_.trueifdisable_recursion_pending()has not been called subsequent to the prior construction or increment operation, otherwisefalse[…]
recursive_directory_iterator& operator++(); recursive_directory_iterator& increment(error_code& ec) noexcept;-23- Effects: As specified for the prefix increment operation of Input iterators ( [iterators.input]), except that:
[…]
-?- Postcondition:
recurse_ == true.void disable_recursion_pending();-28- Postcondition
s:.recursion_pending()recurse_ == false[…]
[2018-05-23: Casey restores the intended design with an expansion of the original PR]
The intended design is that all copies of a single recursive_directory_iterator share a
common block of state which includes the values returned by options, depth, and
recursion_pending - hence the mandate that those functions not be called on a
non-dereferenceable iterator in 31.12.12 [fs.class.rec.dir.itr] para 2. To allow an
implementation with such shared state, it's necessary to make changes to the value returned by
recursion_pending() visible to all copies of the same dereferenceable iterator.
Also:
pop notionally calls increment repeatedly until the current directory is
exhausted, pop should affect the value of recursion_pending similarly to
increment.
options is not valid for all constructor signatures described by
31.12.12.2 [fs.rec.dir.itr.members] para 2.
the copies and moves don't specify what they actually do
it's not quite kosher for the copies and moves to have postconditions on the value of expressions that have UB if the iterator copied/moved from is not dereferenceable.
[2018-06, Rapperswil, Wednesday evening]
JW: p21 currently can just say "unspecified"
BO: if we are OK with only remote implementations we can remove the unspecifiedness
BO: the problematic business is the "recursion pending" bit
JW: I want time to work on this
[2018-08-23 Batavia Issues processing]
General agreement that flag should be shared; Casey to reword.
[2022-12-18; Daniel comments]
Note that this proposed wording has some overlap with LWG 3668(i) for
recursive_directory_iterator's constructors without options argument.
If we would like a different wording form for this textual location in one issue we
should resync the other issue to reduce the chance of a merge conflict.
Proposed resolution:
This wording is relative to N4750.
Change 31.12.12.2 [fs.rec.dir.itr.members] as indicated:
explicit recursive_directory_iterator(const path& p); recursive_directory_iterator(const path& p, directory_options options); recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept; recursive_directory_iterator(const path& p, error_code& ec) noexcept;-?- For the signatures with no parameter
options, letoptionsbedirectory_options::none.-2- Effects: […]
-3- Postconditions:
options() == optionsfor the signatures with adirectory_optionsargument, otherwiseoptions() == directory_options::none.
this->options() == options
recursion_pending() == true[…]
recursive_directory_iterator(const recursive_directory_iterator& rhs);-7- Effects: Constructs an
object of classiterator that denotes the same directory entry asrecursive_directory_iteratorrhs, if any..-8- Postconditions: If
rhsis dereferenceable,[…]
recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;-9- Effects: Constructs an
object of classiterator that denotes the directory entry denoted byrecursive_directory_iteratorrhsbefore the function call, if any..-10- Postconditions: If
rhsis dereferenceable, […]recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);-11- Effects:
IfCauses*thisandrhsare the same object, the member has no effect.*thisto denote the same directory entry denoted byrhs, if any.-12- Postconditions: If
rhsis dereferenceable,[…]
recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;-14- Effects:
IfCauses*thisandrhsare the same object, the member has no effect.*thisto denote the directory entry denoted byrhsbefore the function call, if any.-15- Postconditions: If
rhswas dereferenceable before the function call, […]-16- Returns:
*this.-x- Remarks: If
*thisandrhsdo not refer to the same object, the resulting state ofrhsis unspecified (16.4.6.17 [lib.types.movedfrom]).directory_options options() const;-17- Returns: The value
of the argument passed to the constructor for theestablished by the most recently called member that has a postcondition foroptionsparameter, if present, otherwisedirectory_options::noneoptions().[…]
bool recursion_pending() const;-21- Returns:
Iftrueifdisable_recursion_pending()has not been called subsequent to the prior construction or increment operation, otherwisefalse.disable_recursion_pending()has been called on a copy of*this, an unspecified value. Otherwise, the value established forrecursion_pending()by the postcondition of the most recent construction, assignment, increment, ordisable_recursion_pendingoperation.[…]
recursive_directory_iterator& operator++(); recursive_directory_iterator& increment(error_code& ec);-23- Effects: As specified for the prefix increment operation of Input iterators (24.3.5.3 [input.iterators]), except that: […]
-?- Postconditions: If
*thisis dereferenceable,recursion_pending() == true.[…]
void pop(); void pop(error_code& ec);-26- Effects: If
depth() == 0, set*thistorecursive_directory_iterator(). […]-?- Postconditions: If
*thisis dereferenceable,recursion_pending() == true.[…]
Section: 23.5 [unord] Status: New Submitter: Billy Robert O'Neal III Opened: 2016-05-20 Last modified: 2022-07-16
Priority: 3
View all other issues in [unord].
View all issues with New status.
Discussion:
The resolution of LWG 2210(i) missed constructors accepting a range or initializer list and allocator.
Previous resolution [SUPERSEDED]:
This wording is relative to N4582.
Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_map { public: […] unordered_map(size_type n, const hasher& hf, const allocator_type& a) : unordered_map(n, hf, key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a) : unordered_map(f, l, see below, hasher(), key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_map(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_map(f, l, n, hf, key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a) : unordered_map(il, see below, hasher(), key_equal(), a) { } unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_map(il, n, hasher(), key_equal(), a) { } […] }; }Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_multimap { public: […] unordered_multimap(size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multimap(f, l, see below, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multimap(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(f, l, n, hf, key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a) : unordered_multimap(il, see below, hasher(), key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multimap(il, n, hasher(), key_equal(), a) { } […] }; }Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_set { public: […] unordered_set(size_type n, const hasher& hf, const allocator_type& a) : unordered_set(n, hf, key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a) : unordered_set(f, l, see below, hasher(), key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_set(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_set(f, l, n, hf, key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a) : unordered_set(il, see below, hasher(), key_equal(), a) { } unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_set(il, n, hasher(), key_equal(), a) { } […] }; }Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_multiset { public: […] unordered_multiset(size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multiset(f, l, see below, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multiset(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(f, l, n, hf, key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a) : unordered_multiset(il, see below, hasher(), key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multiset(il, n, hasher(), key_equal(), a) { } […] }; }
[2016-06, Oulu — Daniel comments and provides new wording]
During the LWG discussion of this issue it has been observed, that the interpretation of the embedded see below
is not really clear and that we should split declaration and definition of the new overloads, so that we have a place
that allows us to specify what "see below" stands for. In addition, the new wording wraps the "see below"
as "size_type(see below)" to clarify the provided expression type, similar as we did for the default
constructor of unordered_map.
[Oulu, 2016-06]
Alisdair to review wording.
Previous resolution [SUPERSEDED]:
This wording is relative to N4594.
Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_map { public: […] unordered_map(size_type n, const hasher& hf, const allocator_type& a) : unordered_map(n, hf, key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_map(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_map(f, l, n, hf, key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a); unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_map(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.3.2 [unord.map.cnstr] p2
template <class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a) : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a) : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The number of buckets is implementation-defined.
Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_multimap { public: […] unordered_multimap(size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multimap(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(f, l, n, hf, key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a); unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multimap(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.4.2 [unord.multimap.cnstr] p2
template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a) : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The number of buckets is implementation-defined.
Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_set { public: […] unordered_set(size_type n, const hasher& hf, const allocator_type& a) : unordered_set(n, hf, key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_set(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_set(f, l, n, hf, key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a); unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_set(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2
template <class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a) : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a) : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The number of buckets is implementation-defined.
Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_multiset { public: […] unordered_multiset(size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multiset(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(f, l, n, hf, key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a); unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multiset(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2
template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a) : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The number of buckets is implementation-defined.
[2017-08-04, Daniel and Alisdair finetune wording]
We decided to improve the added Remarks: elements by changing from the previous form:
Remarks: The number of buckets is implementation-defined.
to the more elaborate form:
Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.
[2020-11-29; Reflector discussions]
It has been pointed out that this issue is related to LWG 1199(i), LWG 2210(i), and LWG 3506(i).
Previous resolution [SUPERSEDED]:
This resolution is relative to N4687.
Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_map { public: […] unordered_map(size_type n, const hasher& hf, const allocator_type& a) : unordered_map(n, hf, key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_map(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_map(f, l, n, hf, key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a); unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_map(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.3.2 [unord.map.cnstr] p2
template <class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a) : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a) : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_multimap { public: […] unordered_multimap(size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multimap(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(f, l, n, hf, key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a); unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multimap(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.4.2 [unord.multimap.cnstr] p2
template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a) : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_set { public: […] unordered_set(size_type n, const hasher& hf, const allocator_type& a) : unordered_set(n, hf, key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_set(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_set(f, l, n, hf, key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a); unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_set(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2
template <class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a) : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a) : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_multiset { public: […] unordered_multiset(size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multiset(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(f, l, n, hf, key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a); unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multiset(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2
template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a) : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.
[2022-07-10; Daniel comments]
It is has been noticed by Daniel Eiband on [std-discussion]
that the following deduction guides for the following constructors of the set types
std::unordered_set and std::unordered_multiset are missing:
unordered_set(InputIterator, InputIterator, Allocator); unordered_set(initializer_list<T>, Allocator); unordered_multiset(InputIterator, InputIterator, Allocator); unordered_multiset(initializer_list<T>, Allocator);
Since this issue is adding these missing constructors it should also add the associated deduction guides. The proposed wording has been updated to this effect and also rebased to N4910.
Previous resolution [SUPERSEDED]:
This resolution is relative to N4910.
Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:
namespace std { template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>> { class unordered_map { public: […] unordered_map(size_type n, const hasher& hf, const allocator_type& a) : unordered_map(n, hf, key_equal(), a) { } template<class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a); template<class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_map(f, l, n, hasher(), key_equal(), a) { } template<class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_map(f, l, n, hf, key_equal(), a) { } […] unordered_map(initializer_list<value_type> il, const allocator_type& a); unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_map(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.3.2 [unord.map.cnstr] p2
template<class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a) : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a) : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:
namespace std { template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>> { class unordered_multimap { public: […] unordered_multimap(size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(n, hf, key_equal(), a) { } template<class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a); template<class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multimap(f, l, n, hasher(), key_equal(), a) { } template<class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(f, l, n, hf, key_equal(), a) { } […] unordered_multimap(initializer_list<value_type> il, const allocator_type& a); unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multimap(il, n, hasher(), key_equal(), a) { } […] }; }Insert the following new prototype specification just after 23.5.4.2 [unord.multimap.cnstr] p2
template<class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a) : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:
namespace std { template<class Key, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<Key>> { class unordered_set { public: […] unordered_set(size_type n, const hasher& hf, const allocator_type& a) : unordered_set(n, hf, key_equal(), a) { } template<class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a); template<class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_set(f, l, n, hasher(), key_equal(), a) { } template<class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_set(f, l, n, hf, key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a); unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_set(il, n, hasher(), key_equal(), a) { } […] }; […] template<class T, class Allocator> unordered_set(initializer_list<T>, typename see below::size_type, Allocator) -> unordered_set<T, hash<T>, equal_to<T>, Allocator>; template<class T, class Hash, class Allocator> unordered_set(initializer_list<T>, typename see below::size_type, Hash, Allocator) -> unordered_set<T, Hash, equal_to<T>, Allocator>; template<class InputIterator, class Allocator> unordered_set(InputIterator, InputIterator, Allocator) -> unordered_set<iter-value-type<InputIterator>, hash<iter-value-type<InputIterator>>, equal_to<iter-value-type<InputIterator>>, Allocator>; template<class T, class Allocator> unordered_set(initializer_list<T>, Allocator) -> unordered_set<T, hash<T>, equal_to<T>, Allocator>; }Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2
template<class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a) : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a) : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:
namespace std { template<class Key, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<Key>> { class unordered_multiset { public: […] unordered_multiset(size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(n, hf, key_equal(), a) { } template<class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a); template<class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multiset(f, l, n, hasher(), key_equal(), a) { } template<class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(f, l, n, hf, key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a); unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multiset(il, n, hasher(), key_equal(), a) { } […] }; […] template<class T, class Allocator> unordered_multiset(initializer_list<T>, typename see below ::size_type, Allocator) -> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>; template<class T, class Hash, class Allocator> unordered_multiset(initializer_list<T>, typename see below ::size_type, Hash, Allocator) -> unordered_multiset<T, Hash, equal_to<T>, Allocator>; template<class InputIterator, class Allocator> unordered_multiset(InputIterator, InputIterator, Allocator) -> unordered_multiset<iter-value-type<InputIterator>, hash<iter-value-type<InputIterator>>, equal_to<iter-value-type<InputIterator>>, Allocator>; template<class T, class Allocator> unordered_multiset(initializer_list<T>, Allocator) -> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>; }Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2
template<class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a) : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.
[2022-07-15; Casey comments]
P1206R7 added from_range_t constructors corresponding to existing iterator pair
constructors for the standard containers. For consistency, this issue should add from_range_t
constructors corresponding to each new iterator pair constructor.
[2022-07-16; Daniel comments and updates wording]
The new from_range_t constructors have been added for each added new iterator pair constructor.
Note that the corresponding deduction guides already exist.
Proposed resolution:
This resolution is relative to N4910.
Add to the synopsis in 23.5.3.1 [unord.map.overview] p3:
namespace std {
template<class Key,
class T,
class Hash = hash<Key>,
class Pred = equal_to<Key>,
class Allocator = allocator<pair<const Key, T>>> {
class unordered_map {
public:
[…]
unordered_map(size_type n, const hasher& hf, const allocator_type& a)
: unordered_map(n, hf, key_equal(), a) { }
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l, const allocator_type& a);
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
: unordered_map(f, l, n, hasher(), key_equal(), a) { }
template<class InputIterator>
unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a)
: unordered_map(f, l, n, hf, key_equal(), a) { }
template<container-compatible-range<value_type> R>
unordered_map(from_range_t, R&& rg, const allocator_type& a);
template<container-compatible-range<value_type> R>
unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a)
: unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { }
template<container-compatible-range<value_type> R>
unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a)
: unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { }
unordered_map(initializer_list<value_type> il, const allocator_type& a);
unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a)
: unordered_map(il, n, hasher(), key_equal(), a) { }
[…]
};
}
Insert the following new prototype specification just after 23.5.3.2 [unord.map.cnstr] p2
template<class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a) : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { } template<container-compatible-range<value_type> R> unordered_map(from_range_t, R&& rg, const allocator_type& a) : unordered_map(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a) : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.
Add to the synopsis in 23.5.4.1 [unord.multimap.overview] p3:
namespace std {
template<class Key,
class T,
class Hash = hash<Key>,
class Pred = equal_to<Key>,
class Allocator = allocator<pair<const Key, T>>> {
class unordered_multimap {
public:
[…]
unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
: unordered_multimap(n, hf, key_equal(), a) { }
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a);
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
: unordered_multimap(f, l, n, hasher(), key_equal(), a) { }
template<class InputIterator>
unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a)
: unordered_multimap(f, l, n, hf, key_equal(), a) { }
template<container-compatible-range<value_type> R>
unordered_multimap(from_range_t, R&& rg, const allocator_type& a);
template<container-compatible-range<value_type> R>
unordered_multimap(from_range_t, R&& rg, size_type n, const allocator_type& a)
: unordered_multimap(from_range, std::forward<R>(rg),
n, hasher(), key_equal(), a) { }
[…]
unordered_multimap(initializer_list<value_type> il, const allocator_type& a);
unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a)
: unordered_multimap(il, n, hasher(), key_equal(), a) { }
[…]
};
}
Insert the following new prototype specification just after 23.5.4.2 [unord.multimap.cnstr] p2
template<class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { } template<container-compatible-range<value_type> R> unordered_multimap(from_range_t, R&& rg, const allocator_type& a) : unordered_multimap(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a) : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.
Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:
namespace std {
template<class Key,
class Hash = hash<Key>,
class Pred = equal_to<Key>,
class Allocator = allocator<Key>> {
class unordered_set {
public:
[…]
unordered_set(size_type n, const hasher& hf, const allocator_type& a)
: unordered_set(n, hf, key_equal(), a) { }
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l, const allocator_type& a);
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
: unordered_set(f, l, n, hasher(), key_equal(), a) { }
template<class InputIterator>
unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a)
: unordered_set(f, l, n, hf, key_equal(), a) { }
unordered_set(initializer_list<value_type> il, const allocator_type& a);
unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a)
: unordered_set(il, n, hasher(), key_equal(), a) { }
template<container-compatible-range<value_type> R>
unordered_set(from_range_t, R&& rg, const allocator_type& a);
template<container-compatible-range<value_type> R>
unordered_set(from_range_t, R&& rg, size_type n, const allocator_type& a)
: unordered_set(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { }
[…]
};
[…]
template<class T, class Allocator>
unordered_set(initializer_list<T>, typename see below::size_type, Allocator)
-> unordered_set<T, hash<T>, equal_to<T>, Allocator>;
template<class T, class Hash, class Allocator>
unordered_set(initializer_list<T>, typename see below::size_type, Hash, Allocator)
-> unordered_set<T, Hash, equal_to<T>, Allocator>;
template<class InputIterator, class Allocator>
unordered_set(InputIterator, InputIterator, Allocator)
-> unordered_set<iter-value-type<InputIterator>,
hash<iter-value-type<InputIterator>>,
equal_to<iter-value-type<InputIterator>>,
Allocator>;
template<class T, class Allocator>
unordered_set(initializer_list<T>, Allocator)
-> unordered_set<T, hash<T>, equal_to<T>, Allocator>;
}
Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2
template<class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a) : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { } template<container-compatible-range<value_type> R> unordered_set(from_range_t, R&& rg, const allocator_type& a) : unordered_set(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a) : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.
Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:
namespace std {
template<class Key,
class Hash = hash<Key>,
class Pred = equal_to<Key>,
class Allocator = allocator<Key>> {
class unordered_multiset {
public:
[…]
unordered_multiset(size_type n, const hasher& hf, const allocator_type& a)
: unordered_multiset(n, hf, key_equal(), a) { }
template<class InputIterator>
unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a);
template<class InputIterator>
unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
: unordered_multiset(f, l, n, hasher(), key_equal(), a) { }
template<class InputIterator>
unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf,
const allocator_type& a)
: unordered_multiset(f, l, n, hf, key_equal(), a) { }
template<container-compatible-range<value_type> R>
unordered_multiset(from_range_t, R&& rg, const allocator_type& a);
template<container-compatible-range<value_type> R>
unordered_multiset(from_range_t, R&& rg, size_type n, const allocator_type& a)
: unordered_multiset(from_range, std::forward<R>(rg),
n, hasher(), key_equal(), a) { }
[…]
unordered_multiset(initializer_list<value_type> il, const allocator_type& a);
unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a)
: unordered_multiset(il, n, hasher(), key_equal(), a) { }
[…]
};
[…]
template<class T, class Allocator>
unordered_multiset(initializer_list<T>, typename see below ::size_type, Allocator)
-> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>;
template<class T, class Hash, class Allocator>
unordered_multiset(initializer_list<T>, typename see below ::size_type, Hash, Allocator)
-> unordered_multiset<T, Hash, equal_to<T>, Allocator>;
template<class InputIterator, class Allocator>
unordered_multiset(InputIterator, InputIterator, Allocator)
-> unordered_multiset<iter-value-type<InputIterator>,
hash<iter-value-type<InputIterator>>,
equal_to<iter-value-type<InputIterator>>,
Allocator>;
template<class T, class Allocator>
unordered_multiset(initializer_list<T>, Allocator)
-> unordered_multiset<T, hash<T>, equal_to<T>, Allocator>;
}
Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2
template<class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { } template<container-compatible-range<value_type> R> unordered_multiset(from_range_t, R&& rg, const allocator_type& a)) : unordered_multiset(from_range, std::forward<R>(rg), size_type(see below), hasher(), key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a) : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The initial number of buckets supplied by the
size_typeargument is implementation-defined.
complex stream extraction underspecifiedSection: 29.4.6 [complex.ops] Status: New Submitter: Tim Song Opened: 2016-05-23 Last modified: 2018-10-16
Priority: 3
View all other issues in [complex.ops].
View all issues with New status.
Discussion:
The specification of operator>>(istream&, complex<T>&) is extremely short on details.
It currently reads, in its entirety (29.4.6 [complex.ops]/12-15):
template<class T, class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, complex<T>& x);Effects: Extracts a complex number
Requires: The input values shall be convertible toxof the form:u,(u), or(u,v), whereuis the real part andvis the imaginary part (31.7.5.3 [istream.formatted]).T. If bad input is encountered, callsis.setstate(ios_base::failbit)(which may throwios::failure(31.5.4.4 [iostate.flags])). Returns:is. Remarks: This extraction is performed as a series of simpler extractions. Therefore, the skipping of whitespace is specified to be the same for each of the simpler extractions.
It is completely unclear:
'(', ')' and ','
(by ==, or by traits::eq),"(0, 0]", libstdc++ extracts the ]
while libc++ leaves it in the stream.)Previous resolution [SUPERSEDED]:
Drafting note: the following wording is based on:
- Characters are extracted using
operator>>and compared usingtraits::eq.- Mismatched characters are returned to the stream.
This wording is relative to N4582.
Replace 29.4.6 [complex.ops]/12-15 with the following paragraphs:
template<class T, class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, complex<T>& x);-?- Effects: First, extracts a character from
is.In the description above, characters are extracted from
- If the character extracted is equal to
is.widen('('), extracts an objectuof typeTfromis, then extracts a character fromis.
- If this character is equal to
is.widen(')'), then assignscomplex<T>(u)tox.- Otherwise, if this character is equal to
is.widen(','), extracts an objectvof typeTfromis, then extracts a character fromis. If this character is equal tois.widen(')'), then assignscomplex<T>(u, v)tox; otherwise returns the character toisand the extraction fails.- Otherwise, returns the character to
isand the extraction fails.- Otherwise, returns the character to
is, extracts an objectuof typeTfromis, and assignscomplex<T>(u)tox.isas if byoperator>>(31.7.5.3.3 [istream.extractors]), and returned to the stream as if bybasic_istream::putback(31.7.5.4 [istream.unformatted]). Character equality is determined usingtraits::eq. An objecttof typeTis extracted fromisas if byis >> t. If any extraction operation fails, no further operation is performed and the whole extraction fails. On failure, callsis.setstate(ios_base::failbit)(which may throwios::failure(31.5.4.4 [iostate.flags])). -?- Returns:is. -?- [Note: This extraction is performed as a series of simpler extractions. Therefore, the skipping of whitespace is specified to be the same for each of the simpler extractions. — end note]
[2017-12-13 Tim Song adjusts the P/R to avoid relying on putback.]
Proposed resolution:
Drafting note: the following wording assumes that:
This wording is relative to N4778.
Replace 29.4.6 [complex.ops]/12-16 with the following paragraphs:
template<class T, class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, complex<T>& x);-?- Effects: Let
PEEK(is)be a formatted input function (31.7.5.3.1 [istream.formatted.reqmts]) ofisthat returns the next character that would be extracted fromisbyoperator>>. [Note: Thesentryobject is constructed and destroyed, but the returned character is not extracted from the stream. — end note]In the description above, characters are extracted from
- If
PEEK(is)is not equal tois.widen('('), extracts an objectuof typeTfromis, and assignscomplex<T>(u)tox.- Otherwise, extracts that character from
is, then extracts an objectuof typeTfromis, then:
- If
PEEK(is)is equal tois.widen(')'), then extracts that character fromisand assignscomplex<T>(u)tox.- Otherwise, if it is equal to
is.widen(','), then extracts that character fromisand then extracts an objectvof typeTfromis, then:
- If
PEEK(is)is equal tois.widen(')'), then extracts that character fromisand assignscomplex<T>(u, v)tox.- Otherwise, the extraction fails.
- Otherwise, the extraction fails.
isas if byoperator>>(31.7.5.3.3 [istream.extractors]), character equality is determined usingtraits::eq, and an objecttof typeTis extracted fromisas if byis >> t. If any extraction operation fails, no further operation is performed and the whole extraction fails. On failure, assignscomplex<T>()toxand callsis.setstate(ios_base::failbit)(which may throwios::failure(31.5.4.4 [iostate.flags])). -?- Returns:is. -?- [Note: This extraction is performed as a series of simpler extractions. Therefore, the skipping of whitespace is specified to be the same for each of the simpler extractions. — end note]
numeric_limits primary template definitionSection: 17.3.5 [numeric.limits] Status: Open Submitter: Richard Smith Opened: 2016-06-09 Last modified: 2023-04-18
Priority: 3
View other active issues in [numeric.limits].
View all other issues in [numeric.limits].
View all issues with Open status.
Discussion:
I've received this report at the project editor mail alias, and it seems like it may be worthy of a LWG issue:
I recently had this problem:
- I was storing data in a vector of
__uint128_ts- I used a sorting library which used
numeric_limits<T>::max()as a sentinel value- GCC's libstdc++ provides a
numeric_limitsspecialisation for that type, but- Clang's libc++ does not.
This broke the sorting for me on different platforms, and it was quite difficult to determine why. If the default
I know thatnumeric_limitsdidn't default to0s andfalsevalues (18.3.2.4 of N4582), and insteadstatic_asserted, causing my code to not compile, I would have found the solution immediately.__uint128_tis non-standard, so neither GCC nor Clang is doing the wrong thing nor the right thing here. I could just submit a patch to libc++ providing the specialisations, but it doesn't fix the problem at its core. I am wondering, what is the rationale behind the defaults being0andfalse? It seems like it is inviting a problem for any future numeric types, whether part of a library, compiler extension, and possibly even future updates to C++'s numeric types. I think it would be much better to prevent code that tries to use unspecifiednumeric_limitsfrom compiling.
An alternative to this suggestion would be to still define the primary template, but not provide any of the members
except is_specialized. Either way, this would make numeric_limits members SFINAEable.
[2016-11-12, Issaquah]
Sat PM: This looks like a good idea. Jonathan and Marshall will do post C++17 implementations and report back.
[2018-11 San Diego Thursday night issue processing]
See Walter's paper P0437 for ideas and/or future directions.
[2023-04]
See Walter's paper P1841 for the preferred direction.
Proposed resolution:
Section: 17.6.3.2 [new.delete.single] Status: New Submitter: Clark Nelson Opened: 2016-06-21 Last modified: 2016-08-01
Priority: 3
View other active issues in [new.delete.single].
View all other issues in [new.delete.single].
View all issues with New status.
Discussion:
It should be considered whether the description of the single-object allocation functions should say "or smaller", like the array allocation functions. For example, according to 17.6.3.2 [new.delete.single] p1 (emphasis mine):
The allocation function (3.7.4.1) called by a new-expression (5.3.4) to allocate
sizebytes of storage suitably aligned to represent any object of that size.
In contrast to this, 17.6.3.3 [new.delete.array] p1 says (emphasis mine):
The allocation function (3.7.4.1) called by the array form of a new-expression (5.3.4) to allocate
sizebytes of storage suitably aligned to represent any array object of that size or smaller. (footnote: It is not the direct responsibility ofoperator new[](std::size_t)oroperator delete[](void*)to note the repetition count or element size of the array. Those operations are performed elsewhere in the arraynewanddeleteexpressions. The arraynewexpression, may, however, increase the size argument to operatornew[](std::size_t)to obtain space to store supplemental information.)
Proposed resolution:
emplace between optional and variantSection: 22.5.3.4 [optional.assign], 22.6.3.5 [variant.mod], 22.7.4.4 [any.modifiers] Status: New Submitter: Richard Smith Opened: 2016-07-13 Last modified: 2020-05-10
Priority: 3
View all issues with New status.
Discussion:
Referring to N4604:
In [optional.object.assign]: emplace (normal form) has a Requires that the construction works.
Requires:
is_constructible_v<T, Args&&...>istrue.
emplace (initializer_list form) has a SFINAE condition:
Remarks: […] unless
is_constructible_v<T, initializer_list<U>&, Args&&...>istrue.
In 22.7.4.4 [any.modifiers]: emplace (normal form) has a Requires that the construction works:
Requires:
is_constructible_v<T, Args...>istrue.
emplace (initializer_list form) has a SFINAE condition:
Remarks: […] unless
is_constructible_v<T, initializer_list<U>&, Args...>istrue.
In 22.6.3.5 [variant.mod]: emplace (T, normal form) has a SFINAE condition:
Remarks: […] unless
is_constructible_v<T, Args...>istrue, andToccurs exactly once inTypes....
emplace (Idx, normal form) has a both a Requires and a SFINAE condition:
Requires:
Remarks: […] unlessI < sizeof...(Types)is_constructible_v<T, Args...>istrue, andToccurs exactly once inTypes....
emplace (T, initializer_list form) has a SFINAE condition:
Remarks: […] unless
is_constructible_v<T, initializer_list<U>&, Args...>istrue, andToccurs exactly once inTypes....
emplace (Idx, initializer_list form) has a both a Requires and a SFINAE condition:
Requires:
Remarks: […] unlessI < sizeof...(Types)is_constructible_v<T, Args...>is true, andToccurs exactly once inTypes....
Why the inconsistency? Should all the cases have a SFINAE requirement?
I see that variant has an additional requirement (T occurs exactly once in Types...), but that
only agues that it must be a SFINAE condition — doesn't say that the other cases (any/variant) should not.
map/multimap/unordered_map/unordered_multimap have SFINAE'd versions of
emplace that don't take initializer_lists, but they don't have any emplace versions
that take ILs.
Suggested resolution:
Add SFINAE requirements to optional::emplace(Args&&... args) and
any::emplace(Args&&... args);
[2016-08 Chicago]
During issue prioritization, people suggested that this might apply to any as well.
Ville notes that 2746(i), 2754(i) and 2756(i) all go together.
[2020-05-10; Daniel comments and provides wording]
The inconsistency between the two any::emplace overloads have been removed by resolving LWG
2754(i) to use Constraints: elements. The last Mandating paper
(P1460R1), adopted in Prague, changed the Requires: elements
for variant::emplace, "I < sizeof...(Types)" to Mandates:, but that paper
was focused on fixing inappropriate preconditions, not searching for consistency here. Given that the
in_place_index_t constructors of variant uses SFINAE-conditions for this form of static
precondition violation, I recommend that its emplace functions use the same style, which would bring
them also in consistency with their corresponding type-based emplace forms that are
Mandates:-free but delegate to the index-based forms.
Proposed resolution:
This wording is relative to N4861.
Modify 22.5.3.4 [optional.assign], as indicated:
template<class... Args> T& emplace(Args&&... args);-29-
[…]MandatesConstraints:is_constructible_v<T, Args...>istrue.template<class U, class... Args> T& emplace(initializer_list<U> il, Args&&... args);-35- Constraints:
[…]is_constructible_v<T, initializer_list<U>&, Args...>istrue.
Modify 22.6.3.5 [variant.mod], as indicated:
template<class T, class... Args> T& emplace(Args&&... args);-1- Constraints:
[…]is_constructible_v<T, Args...>istrue, andToccurs exactly once inTypes.template<class T, class U, class... Args> T& emplace(initializer_list<U> il, Args&&... args);-3- Constraints:
[…]is_constructible_v<T, initializer_list<U>&, Args...>istrue, andToccurs exactly once inTypes.template<size_t I, class... Args> variant_alternative_t<I, variant<Types...>>& emplace(Args&&... args);-6- Constraints:
-5- Mandates:I < sizeof...(Types).is_constructible_v<TI, Args...>istrueandI < sizeof...(Types)istrue. […]template<size_t I, class U, class... Args> variant_alternative_t<I, variant<Types...>>& emplace(initializer_list<U> il, Args&&... args);-13- Constraints:
-12- Mandates:I < sizeof...(Types).is_constructible_v<TI, initializer_list<U>&, Args...>istrueandI < sizeof...(Types)istrue. […]
shared_ptr deleter not specified to observe expired weak_ptr instancesSection: 20.3.2.2.3 [util.smartptr.shared.dest] Status: New Submitter: Aaron Jacobs Opened: 2016-07-21 Last modified: 2025-03-18
Priority: 4
View all other issues in [util.smartptr.shared.dest].
View all issues with New status.
Discussion:
The C++14 standard contains no language that guarantees the deleter run by a
shared_ptr will see all associated weak_ptr instances as expired. For example,
the standard doesn't appear to guarantee that the assertion in the following
snippet won't fire:
std::weak_ptr<Foo> weak;
std::shared_ptr<Foo> strong{
new Foo,
[&weak] (Foo* f) {
assert(weak.expired());
delete f;
},
};
weak = strong;
strong.reset();
It seems clear that the intent is that associated weak_ptrs are expired,
because otherwise shared_ptr deleters could resurrect a reference to an object
that is being deleted.
use_count() caused by the destructor is sequenced before the call to the
deleter or the call to delete p.
[2016-11-08, Jonathan and STL suggest NAD]
STL and Jonathan feel that the example has unspecified behaviour, and the assertion is allowed to fire, and that's OK (the program's expectation is not reasonable). Otherwise it's necessary to move-construct a copy of the deleter and use that copy to destroy the owned pointer. We do not want to be required to do that.
[2017-09-20, Jonathan comments]
I'd like to withdraw my NAD suggestion. The value of use_count() is already observable during the destructor via
shared_ptr and weak_ptr objects that share ownership, so specifying when it changes ensures correct
behaviour.
[2025-03-18, Jonathan comments]
Proposed resolution:
Section: 22.3.3 [pairs.spec], 22.4.12 [tuple.special], 22.5.10 [optional.specalg], 22.6.10 [variant.specalg], 20.3.1.6 [unique.ptr.special], 23.3.3.4 [array.special], 23.6.3.6 [queue.special], 23.6.4.5 [priqueue.special], 23.6.6.7 [stack.special] Status: New Submitter: Agustín K-ballo Bergé Opened: 2016-08-15 Last modified: 2020-09-06
Priority: 3
View all other issues in [pairs.spec].
View all issues with New status.
Discussion:
Related: 2748(i) swappable traits for optionals, 2749(i) swappable traits for variants.
The adoption of P0185R1 "Adding [nothrow-]swappable traits" makes certain non-swappable types indirectly swappable. Consider a type defined as follows:
struct non_swappable {
friend void swap(non_swappable&, non_swappable&) = delete;
};
non_swappable ns1, ns2;
using std::swap;
swap(ns1, ns2); // ill-formed
static_assert(std::is_swappable_v<non_swappable> == false); // holds
Lvalues of type non_swappable are not swappable, as defined by 16.4.4.3 [swappable.requirements],
overload resolution selects the deleted function. Consistently, is_swappable_v<non_swappable> yields
false. It should be noted that since non_swappable is move constructible and move assignable, a qualified
call to std::swap would be well-formed, even under P0185. Now consider the following snippet:
std::tuple<non_swappable> tns1, tns2; using std::swap; swap(tns1, tns2); // previously ill-formed, now well-formed static_assert(std::is_swappable_v<std::tuple<non_swappable>> == false); // fires
Before P0185, this snippet would violate the implicit requirement of specialized swap for tuples that each tuple element be swappable. After P0185, this specialized swap overload for tuples would be SFINAEd away, resulting in overload resolution selecting the base swap overload, and performing the exchange via move construction and move assignment of tuples.
This issue affects all ofpair, tuple, unique_ptr, array, queue,
priority_queue, stack, and should eventually also apply to optional and variant.
Previous resolution [SUPERSEDED]:
This wording is relative to N4606, except when otherwise noted.
Modify 22.3.3 [pairs.spec] as indicated:
template<class T1, class T2> void swap(pair<T1, T2>& x, pair<T1, T2>& y) noexcept(noexcept(x.swap(y)));-7- Effects: As if by
-8- Remarks: This function shallx.swap(y).not participate in overload resolutionbe defined as deleted unlessis_swappable_v<T1>istrueandis_swappable_v<T2>istrue.Modify 22.4.12 [tuple.special] as indicated:
template <class... Types> void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);-1- Remarks: This function shall
not participate in overload resolutionbe defined as deleted unlessis_swappable_v<isTi>truefor alli, where0 <= iandi < sizeof...(Types). The expression insidenoexceptis equivalent to:noexcept(x.swap(y))-2- Effects: As if by
x.swap(y).Modify 20.3.1.6 [unique.ptr.special] as indicated:
template <class T, class D> void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;-1- Remarks: This function shall
-2- Effects: Callsnot participate in overload resolutionbe defined as deleted unlessis_swappable_v<D>istrue.x.swap(y).Modify 23.3.3.4 [array.special] as indicated:
template <class T, size_t N> void swap(array<T, N>& x, array<T, N>& y) noexcept(noexcept(x.swap(y)));-1- Remarks: This function shall
-2- Effects: As if bynot participate in overload resolutionbe defined as deleted unlessN == 0oris_swappable_v<T>istrue.x.swap(y). […]Modify 23.6.3.6 [queue.special] as indicated:
template <class T, class Container> void swap(queue<T, Container>& x, queue<T, Container>& y) noexcept(noexcept(x.swap(y)));-1- Remarks: This function shall
-2- Effects: As if bynot participate in overload resolutionbe defined as deleted unlessis_swappable_v<Container>istrue.x.swap(y).Modify 23.6.4.5 [priqueue.special] as indicated:
template <class T, class Container, class Compare> void swap(priority_queue<T, Container, Compare>& x, priority_queue<T, Container, Compare>& y) noexcept(noexcept(x.swap(y)));-1-
-2- Effects: As if byRemarks:This function shallnot participate in overload resolutionbe defined as deleted unlessis_swappable_v<Container>istrueandis_swappable_v<Compare>istrue.x.swap(y).Modify 23.6.6.7 [stack.special] as indicated:
template <class T, class Container> void swap(stack<T, Container>& x, stack<T, Container>& y) noexcept(noexcept(x.swap(y)));-1- Remarks: This function shall
-2- Effects: As if bynot participate in overload resolutionbe defined as deleted unlessis_swappable_v<Container>istrue.x.swap(y).Modify 22.5.10 [optional.specalg] as indicated:
This change should be performed if and only if LWG 2748(i) is accepted and is against the wording of 2748(i):
template <class T> void swap(optional<T>& x, optional<T>& y) noexcept(noexcept(x.swap(y)));-1- Effects: Calls
-2- Remarks: This function shallx.swap(y).not participate in overload resolutionbe defined as deleted unlessis_move_constructible_v<T>istrueandis_swappable_v<T>istrue.Modify 22.6.10 [variant.specalg] as indicated:
This change should be performed if and only if LWG 2749(i) is accepted and is against the wording of 2749(i):
template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);-1- Effects: Equivalent to
-2- Remarks: This function shallv.swap(w).not participate in overload resolutionbe defined as deleted unlessis_move_constructible_v<Ti> && is_swappable_v<Ti>istruefor alli. The expression insidenoexceptis equivalent tonoexcept(v.swap(w)).
[2019-04-17 Jonathan updates proposed resolution based on Ville's 2016-11-17 observation that the container adaptors always require swappable sequences anyway. The new proposed resolution is based on the latest WP, "de-shalled", and Remarks elements are repositioned after the Effects.]
Proposed resolution:
This wording is relative to N4810.
Modify 22.3.3 [pairs.spec] as indicated:
template<class T1, class T2> constexpr void swap(pair<T1, T2>& x, pair<T1, T2>& y) noexcept(noexcept(x.swap(y)));-7- Effects: As if by
-8- Remarks: This functionx.swap(y).shall not participate in overload resolutionis defined as deleted unlessis_swappable_v<T1>istrueandis_swappable_v<T2>istrue.
Modify 22.4.12 [tuple.special] as indicated:
template <class... Types> constexpr void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);-?- Effects: As if by
x.swap(y).-1- Remarks: This function
shall not participate in overload resolutionis defined as deleted unlessis_swappable_v<isTi>truefor alli, where0 <= iandi < sizeof...(Types). The expression insidenoexceptis equivalent to:noexcept(x.swap(y))-2-
Effects: As if byx.swap(y).
Modify 22.5.10 [optional.specalg] as indicated:
template <class T> void swap(optional<T>& x, optional<T>& y) noexcept(noexcept(x.swap(y)));-1- Effects: Calls
-2- Remarks: This functionx.swap(y).shall not participate in overload resolutionis defined as deleted unlessis_move_constructible_v<T>istrueandis_swappable_v<T>istrue.
Modify 22.6.10 [variant.specalg] as indicated:
template <class... Types> void swap(variant<Types...>& v, variant<Types...>& w) noexcept(see below);-1- Effects: Equivalent to
-2- Remarks: This functionv.swap(w).shall not participate in overload resolutionis defined as deleted unlessis_move_constructible_v<Ti> && is_swappable_v<Ti>istruefor alli. The expression insidenoexceptis equivalent tonoexcept(v.swap(w)).
Modify 20.3.1.6 [unique.ptr.special] as indicated:
template <class T, class D> void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;-?- Effects: Calls
x.swap(y).-1- Remarks: This function
shall not participate in overload resolutionis defined as deleted unlessis_swappable_v<D>istrue.-2-
Effects: Callsx.swap(y).
Modify 23.3.3.4 [array.special] as indicated:
template <class T, size_t N> void swap(array<T, N>& x, array<T, N>& y) noexcept(noexcept(x.swap(y)));-1-
-2- Effects: As if byConstraints:N == 0oris_swappable_v<T>istrue.x.swap(y). -3- Complexity: Linear inN. -?- Remarks: This function is defined as deleted unlessN == 0oris_swappable_v<T>istrue.
optional/variant/anySection: 22.5.3.2 [optional.ctor], 22.5.3.4 [optional.assign], 22.6.3.2 [variant.ctor], 22.7.4.2 [any.cons], 22.7.4.4 [any.modifiers] Status: New Submitter: Tim Song Opened: 2016-10-29 Last modified: 2020-06-13
Priority: 3
View all other issues in [optional.ctor].
View all issues with New status.
Discussion:
Throughout optional/variant/any's specification references are made to "the selected constructor
of T". For example, 22.5.3.2 [optional.ctor]/16 says of the constructor from const T&:
-16- Remarks: If
T's selected constructor is aconstexprconstructor, this constructor shall be aconstexprconstructor.
Similarly, the in-place constructor has this wording (22.5.3.2 [optional.ctor]/25-26):
-25- Throws: Any exception thrown by the selected constructor of
-26- Remarks: IfT.T's constructor selected for the initialization is aconstexprconstructor, this constructor shall be aconstexprconstructor.
If T is a scalar type, it has no constructor at all. Moreover, even for
class types, the in-place constructor wording ignores any implicit conversion done on the argument before the selected
constructor is called, which 1) may not be valid in constant expressions and 2) may throw an exception; such exceptions
aren't thrown "by the selected constructor of T" but outside it.
[Issues Telecon 16-Dec-2016]
Priority 3; Jonathan to provide wording.
[2020-06-11; Nina Dinka Ranns comments and provides initial wording]
This wording depends on the current resolution for LWG 2833(i), which covers the constexpr
portion of this issue.
Proposed resolution:
This wording is relative to N4861.
Modify 22.5.3.2 [optional.ctor] as indicated:
constexpr optional(const optional& rhs);-3- […]
-4- […] -5- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.Tconstexpr optional(optional&& rhs) noexcept(see below);-7- […]
[…] -10- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.Ttemplate<class... Args> constexpr explicit optional(in_place_t, Args&&... args);-12- […]
[…] -15- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.Ttemplate<class U, class... Args> constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);-17- […]
[…] -20- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.Ttemplate<class U = T> constexpr explicit(see below) optional(U&& v);-22- […]
[…] -25- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.Ttemplate<class U> explicit(see below) optional(const optional<U>& rhs);-27- […]
[…] -30- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.Ttemplate<class U> explicit(see below) optional(optional<U>&& rhs);-32- […]
[…] -35- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.T
Modify 22.6.3.2 [variant.ctor] as indicated:
template<class T, class... Args> constexpr explicit variant(in_place_type_t<T>, Args&&... args);-20- […]
[…] -23- Throws: Any exception thrown bycalling the selected constructor ofthe initialization of the contained value.Ttemplate<class T, class U, class... Args> constexpr explicit variant(in_place_type_t<T>, initializer_list<U> il, Args&&... args);-25- […]
[…] -28- Throws: Any exception thrown bycalling the selected constructor ofthe initialization of the contained value.Ttemplate<size_t I, class... Args> constexpr explicit variant(in_place_index_t<I>, Args&&... args);-30 […]
[…] -33- Throws: Any exception thrown bycalling the selected constructor ofthe initialization of the contained value.TI
Modify 22.7.4.2 [any.cons] as indicated:
any(const any& other);[…]-2- Effects: […]
-3- Throws: Any exceptionsarising from calling the selected constructor forthrown by the initialization of the contained value.template<class T> any(T&& value);-5- […]
[…] -9- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.VTtemplate<class T, class... Args> explicit any(in_place_type_t<T>, Args&&... args);-10- […]
[…] -15- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.VTtemplate<class T, class U, class... Args> explicit any(in_place_type_t<T>, initializer_list<U> il, Args&&... args);-16- […]
[…] -21- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.VT
Modify 22.7.4.4 [any.modifiers] as indicated:
template<class T, class... Args> decay_t<T>& emplace(Args&&... args);-1- […]
[…] -7- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value. -8- […]VTtemplate<class T, class U, class... Args> decay_t<T>& emplace(initializer_list<U> il, Args&&... args);-9- […]
[…] -15- Throws: Any exception thrown by theselected constructor ofinitialization of the contained value.VT
quick_exit can deadlockSection: 17.5 [support.start.term] Status: New Submitter: Jean-François Bastien Opened: 2016-11-07 Last modified: 2020-09-06
Priority: 3
View other active issues in [support.start.term].
View all other issues in [support.start.term].
View all issues with New status.
Discussion:
While SG1 was processing NB comments CA1 and LATE2 regarding P0270R1,
we decided to remove the proposed guarantee that quick_exit be made signal safe.
at_quick_exit aren't forbidden from calling
quick_exit, but quick_exit implementations likely acquire some form of a lock before
processing all registered functions (because a note forbids the implementation from introducing data races).
The following code can therefore deadlock:
#include <cstdlib>
int main()
{
std::at_quick_exit([] () { std::quick_exit(0); });
std::quick_exit(1);
return 0;
}
The same applies if a function registered in at_quick_exit handles a signal, and that signal calls
quick_exit. SG1 believes that both issues (same thread deadlock, and signal deadlock) can be resolved
in the same manner. Either:
quick_exit while servicing quick_exit is undefined; orquick_exit while servicing quick_exit is defined to not deadlock,
and instead calls _Exit without calling further registered functions.Option 2. seems preferable, and can be implemented along the lines of:
#include <array>
#include <atomic>
#include <cstddef>
namespace {
typedef void (*func)();
std::array<func, 32> quick_exit_functions;
const auto* quick_exit_functions_ptr = &quick_exit_functions;
std::atomic_flag lock = ATOMIC_FLAG_INIT;
struct scope
{
scope() { while (lock.test_and_set(std::memory_order_acquire)) ; }
~scope() { lock.clear(std::memory_order_release); }
};
}
namespace std {
extern "C" void quick_exit(int status) noexcept
{
decltype(quick_exit_functions_ptr) f;
{
scope s;
f = quick_exit_functions_ptr;
quick_exit_functions_ptr = nullptr;
}
if (f) {
size_t pos = f->size();
while (pos > 0)
(*f)[--pos]();
}
_Exit(status);
}
extern "C++" int at_quick_exit(func f) noexcept
{
scope s;
if (!quick_exit_functions_ptr || quick_exit_functions.size() == quick_exit_functions.max_size())
return -1;
quick_exit_functions[quick_exit_functions.size()] = f;
return 0;
}
}
Ideally, the resolution would also add back the wording which SG1 dropped from P0270R1:
Add at new element to the end of 17.5 [support.start.term] p13 (
quick_exit()):Remarks: The function
quick_exit()is signal-safe (17.14.4 [csignal.syn]). [Note: It might still be unsafe to callquick_exit()from a handler, because the functions registered withat_quick_exit()might not be signal-safe. — end note]
[Issues Telecon 16-Dec-2016]
Priority 3
Proposed resolution:
This wording is relative to N4606.
Add at new element to the end of 17.5 [support.start.term] p13 (quick_exit()):
[[noreturn]] void quick_exit(int status) noexcept;-13- Effects: Functions registered by calls to
-?- Remarks: The functionat_quick_exitare called in the reverse order of their registration, except that a function shall be called after any previously registered functions that had already been called at the time it was registered. Objects shall not be destroyed as a result of callingquick_exit. If control leaves a registered function called byquick_exitbecause the function does not provide a handler for a thrown exception,std::terminate()shall be called. [Note:at_quick_exitmay call a registered function from a different thread than the one that registered it, so registered functions should not rely on the identity of objects with thread storage duration. — end note] After calling registered functions,quick_exitshall call_Exit(status). [Note: The standard file buffers are not flushed. See: ISO C 7.22.4.5. — end note]quick_exit()is signal-safe (17.14.4 [csignal.syn]). [Note: It might still be unsafe to callquick_exit()from a handler, because the functions registered withat_quick_exit()might not be signal-safe. — end note]
Section: 32.2.5 [thread.req.lockable], 32.6.4 [thread.mutex.requirements] Status: New Submitter: Agustín K-ballo Bergé Opened: 2016-11-12 Last modified: 2022-11-06
Priority: 3
View all issues with New status.
Discussion:
The current draft contains 14 occurrences of a Return type: clause. That clause is not covered by 16.3.2.4 [structure.specifications] p3. This was reported as editorial request #266.
[Issues Telecon 16-Dec-2016]
Priority 3; Jonathan to provide wording.
[12-May-2020, Jonathan provides wording to correct the 13 occurrences.]
Previous resolution [SUPERSEDED]:
This wording is relative to N4681.
Modify 32.2.5.3 [thread.req.lockable.req] as indicated:
-1- A type
Lmeets the Cpp17Lockable requirements if it meets the Cpp17BasicLockable requirements and the following expressions are well-formed, have typebool, and have the specified semantics (mdenotes a value of typeL).
m.try_lock()-2- Effects: [...]
-3- Return type:bool.-4- Returns:
trueif the lock was acquired,falseotherwise.Modify 32.2.5.4 [thread.req.lockable.timed] as indicated:
-1- A type
Lmeets the Cpp17TimedLockable requirements if it meets the Cpp17BasicLockable requirements and the following expressions are well-formed, have typebool, and have the specified semantics (mdenotes a value of typeL,rel_timedenotes a value of an instantiation ofduration(30.5 [time.duration]), andabs_timedenotes a value of an instantiation oftime_point(30.6 [time.point])).
m.try_lock_for(rel_time)-2- Effects: [...]
-3- Return type:bool.-4- Returns:
trueif the lock was acquired,falseotherwise.
m.try_lock_until(abs_time)-2- Effects: [...]
-3- Return type:bool.-4- Returns:
trueif the lock was acquired,falseotherwise.Modify 32.6.4.2 [thread.mutex.requirements.mutex] as indicated:
-6- The expression
m.lock()is well-formed, has typevoid, and has the following semantics:-7- Preconditions: [...]
-8- Effects: [...]
-9- Postconditions: [...]
-10- Return type:void.-11- Synchronization: [...]
-12- Throws: [...]
-13- Error conditions: [...]
-14- The expression
m.try_lock()is well-formed, has typebool, and has the following semantics:-15- Preconditions: [...]
-16- Effects: [...]
-17- Return type:bool.-18- Returns:
trueif ownership of the mutex was obtained for the calling thread, otherwisefalse.-19- Synchronization: [...]
-20- Throws: Nothing.
-21- The expression
m.unlock()is well-formed, has typevoid, and has the following semantics:-22- Preconditions: [...]
-23- Effects: [...]
-24- Return type:void.-25- Synchronization: [...]
-26- Throws: Nothing.
Modify 32.6.4.3 [thread.timedmutex.requirements] as indicated:
-1- The timed mutex types are the standard library types [...]
-2- The timed mutex types meet the Cpp17TimedLockable requirements (32.2.5.4 [thread.req.lockable.timed]).
-3- The expression
m.try_lock_for(rel_time)is well-formed, has typebool, and has the following semantics:-4- Preconditions: [...]
-5- Effects: [...]
-6- Return type:bool.-7- Returns:
trueif the shared lock was acquired,falseotherwise.-8- Synchronization: [...]
-9- Throws: [...]
-10- The expression
m.try_lock_until(abs_time)is well-formed, has typebool, and has the following semantics:-11- Preconditions: [...]
-12- Effects: [...]
-13- Return type:bool.-14- Returns:
trueif ownership was obtained, otherwisefalse.-15- Synchronization: [...]
-16- Throws: [...]
Modify 32.6.4.4 [thread.sharedmutex.requirements] as indicated:
-1- The standard library types
shared_mutexandshared_timed_mutexare shared mutex types. [...]-2- In addition to the exclusive lock ownership mode [...]
-3- The expression
m.lock_shared()is well-formed, has typevoid, and has the following semantics:-4- Preconditions: [...]
-5- Effects: [...]
-6- Postconditions: [...]
-7- Return type:void.-8- Synchronization: [...]
-9- Throws: [...]
-10- Error conditions: [...]
-11- The expression
m.unlock_shared()is well-formed, has typevoid, and has the following semantics:-12- Preconditions: [...]
-13- Effects: [...]
-14- Return type:void.-15- Synchronization: [...]
-16- Throws: [...]
-17- The expression
m.try_lock_shared()is well-formed, has typebool, and has the following semantics:-18- Preconditions: [...]
-19- Effects: [...]
-20- Return type:bool.-21- Returns:
trueif the shared ownership lock was acquired,falseotherwise.-22- Synchronization: [...]
-23- Throws: [...]
Modify 32.6.4.5 [thread.sharedtimedmutex.requirements] as indicated:
-1- The standard library type
shared_timed_mutexis a shared timed mutex type. [...]-2- The expression
m.try_lock_shared_for(rel_time)is well-formed, has typebool, and has the following semantics:-3- Preconditions: [...]
-4- Effects: [...]
-5- Return type:bool.-6- Returns:
trueif the shared lock was acquired,falseotherwise.-7- Synchronization: [...]
-8- Throws: [...]
-9- The expression
m.try_lock_shared_until(abs_time)is well-formed, has typebool, and has the following semantics:-10- Preconditions: [...]
-11- Effects: [...]
-12- Return type:bool.-13- Returns:
trueif the shared lock was acquired,falseotherwise.-14- Synchronization: [...]
-15- Throws: [...]
[2022-11-06; Daniel comments and provides alternative wording]
Now that we have the new element Result: specified in 16.3.2.4 [structure.specifications], we can simply replace all occurrences of the Return type: by this element.
Proposed resolution:
This wording is relative to N4917.
Modify 32.2.5.3 [thread.req.lockable.req] as indicated:
-1- A type
Lmeets the Cpp17Lockable requirements if it meets the Cpp17BasicLockable requirements and the following expressions are well-formed and have the specified semantics (mdenotes a value of typeL).
m.try_lock()-2- Effects: [...]
-3-
Return typeResult:bool.-4- Returns:
trueif the lock was acquired, otherwisefalse.
Modify 32.2.5.4 [thread.req.lockable.timed] as indicated:
-1- A type
Lmeets the Cpp17TimedLockable requirements if it meets the Cpp17Lockable requirements and the following expressions are well-formed and have the specified semantics (mdenotes a value of typeL,rel_timedenotes a value of an instantiation ofduration(30.5 [time.duration]), andabs_timedenotes a value of an instantiation oftime_point(30.6 [time.point])).
m.try_lock_for(rel_time)-2- Effects: [...]
-3-
Return typeResult:bool.-4- Returns:
trueif the lock was acquired, otherwisefalse.
m.try_lock_until(abs_time)-5- Effects: [...]
-6-
Return typeResult:bool.-7- Returns:
trueif the lock was acquired, otherwisefalse.
Modify 32.6.4.2.1 [thread.mutex.requirements.mutex.general] as indicated:
-5- The expression
m.lock()is well-formed and has the following semantics:-6- Preconditions: [...]
-7- Effects: [...]
-8- Synchronization: [...]
-9- Postconditions: [...]
-10-
Return typeResult:void.-11- Throws: [...]
-12- Error conditions: [...]
-13- The expression
m.try_lock()is well-formed and has the following semantics:-14- Preconditions: [...]
-15- Effects: [...]
-16- Synchronization: [...]
-17-
Return typeResult:bool.-18- Returns:
trueif ownership was obtained, otherwisefalse.-19- Throws: Nothing.
-20- The expression
m.unlock()is well-formed and has the following semantics:-21- Preconditions: [...]
-22- Effects: [...]
-23-
Return typeResult:void.-24- Synchronization: [...]
-25- Throws: Nothing.
Modify 32.6.4.3.1 [thread.timedmutex.requirements.general] as indicated:
-1- The timed mutex types are the standard library types […]
-2- The expression
m.try_lock_for(rel_time)is well-formed and has the following semantics:-3- Preconditions: [...]
-4- Effects: [...]
-5- Synchronization: [...]
-6-
Return typeResult:bool.-7- Returns:
trueif ownership was obtained, otherwisefalse.-8- Throws: [...]
-9- The expression
m.try_lock_until(abs_time)is well-formed and has the following semantics:-10- Preconditions: [...]
-11- Effects: [...]
-12- Synchronization: [...]
-13-
Return typeResult:bool.-14- Returns:
trueif ownership was obtained, otherwisefalse.-15- Throws: [...]
Modify 32.6.4.4.1 [thread.sharedmutex.requirements.general] as indicated:
-1- The standard library types
shared_mutexandshared_timed_mutexare shared mutex types. [...]-2- In addition to the exclusive lock ownership mode [...]
-3- The expression
m.lock_shared()is well-formed and has the following semantics:-4- Preconditions: [...]
-5- Effects: [...]
-6- Synchronization: [...]
-7- Postconditions: [...]
-8-
Return typeResult:void.-9- Throws: [...]
-10- Error conditions: [...]
-11- The expression
m.unlock_shared()is well-formed and has the following semantics:-12- Preconditions: [...]
-13- Effects: [...]
-14-
Return typeResult:void.-15- Synchronization: [...]
-16- Throws: [...]
-17- The expression
m.try_lock_shared()is well-formed and has the following semantics:-18- Preconditions: [...]
-19- Effects: [...]
-20- Synchronization: [...]
-21-
Return typeResult:bool.-22- Returns:
trueif the shared lock was acquired, otherwisefalse.-23- Throws: [...]
Modify 32.6.4.5.1 [thread.sharedtimedmutex.requirements.general] as indicated:
-1- The standard library type
shared_timed_mutexis a shared timed mutex type. [...]-2- The expression
m.try_lock_shared_for(rel_time)is well-formed and has the following semantics:-3- Preconditions: [...]
-4- Effects: [...]
-5- Synchronization: [...]
-6-
Return typeResult:bool.-7- Returns:
trueif the shared lock was acquired, otherwisefalse.-8- Throws: [...]
-9- The expression
m.try_lock_shared_until(abs_time)is well-formed and has the following semantics:-10- Preconditions: [...]
-11- Effects: [...]
-12- Synchronization: [...]
-13-
Return typeResult:bool.-14- Returns:
trueif the shared lock was acquired, otherwisefalse.-15- Throws: [...]
std::array initialization is still not permissive enoughSection: 23.3.3.1 [array.overview] Status: Open Submitter: Robert Haberlach Opened: 2016-11-16 Last modified: 2018-03-19
Priority: 3
View other active issues in [array.overview].
View all other issues in [array.overview].
View all issues with Open status.
Discussion:
LWG 2590(i)'s resolution is incomplete:
std::array<int, 1> arr{{0}};
should be fine, but isn't guaranteed, since {0} has no type. We should rather go for implicit conversion:
An array is an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up to
Nelementswhose types are convertible tothat can be implicitly converted toTT.
[2016-11-26, Tim Song comments]
This is not possible as written, because due to the brace elision rules for aggregate initialization,
std::array<int, 2> arr{{0}, {1}}; will never work: the {0}
is taken as initializing the inner array, and the {1} causes an error.
[2017-01-27 Telecon]
Priority 2; consensus is that the P/R is not quite right.
[2018-3-14 Wednesday evening issues processing; priority to 3; move to Open]
Jens: There's nothing you can do about the double braces in std::array. That's a core thing.
STL to write paper to resolve this.
Proposed resolution:
This wording is relative to N4606.
Change 23.3.3.1 [array.overview] p2 as indicated:
-2- An
arrayis an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up toNelementswhose types are convertiblethat can be implicitly converted toT.
is_trivially_constructible and non-trivial destructorsSection: 21.3.6.4 [meta.unary.prop] Status: New Submitter: Richard Smith Opened: 2016-11-17 Last modified: 2023-05-25
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with New status.
Discussion:
struct S
{
~S(); // non-trivial
};
static_assert(std::is_trivially_constructible<S>::value, "");
Should the assert pass? Implementations disagree.
Per 21.3.6.4 [meta.unary.prop]'s Table 38, this trait looks at whether the following variable definition is known to call no operation that is not trivial:S t(create<Args>()...);
... where Args is an empty pack in this case. That variable definition results in a call to the S destructor.
Should that call be considered by the trait?
[2017-01-27 Telecon]
Priority 3
This issue interacts with 2116(i)
[2020-01-24; Peter Dimov comments]
std::is_trivially_copy_constructible_v<D>, where D is
struct D
{
~D() {}
};
reports false. This is because the definition of
is_trivially_copy_constructible requires the invented variable
definition T t(declval<Args>()...);, which in our case is
D t(declval<D>());, to not call any nontrivial operations.
is_nothrow_copy_constructible.
But that's wrong; the copy constructor is trivial.
As a consequence, variant<D> also doesn't have a trivial
copy constructor, which causes (completely unnecessary) inefficiencies
when said variant is copied.
[2023-05-25; May 2023 mailing]
Alisdair provided P2842R0.
Proposed resolution:
Section: 22.5.3.7 [optional.observe] Status: Open Submitter: Richard Smith Opened: 2016-11-24 Last modified: 2020-06-13
Priority: 2
View other active issues in [optional.observe].
View all other issues in [optional.observe].
View all issues with Open status.
Discussion:
After applying LWG 2740(i), we have:
constexpr const T* operator->() const; constexpr T* operator->();-1- Requires:
-2- Returns:*thiscontains a value.val. -3- Throws: Nothing. -4- Remarks: These functions shall beconstexprfunctions.
Paragraph 4 is completely superfluous. We already said these functions were constexpr in the synopsis. Can it be removed?
[Issues Telecon 16-Dec-2016]
Priority 2
Jonathan notes: Although Richard is correct, I suggest we don't strike the paragraph, so that we remember to fix it as part of 2833(i), when we know how to say this properly.
[2018-06 Rapperswil Thursday issues processing]
Status to Open; also see 7.7 [expr.const]/6 and 2289(i).
[2020-06-08 Nina Dinka Ranns comments]
The revised wording provided by LWG 2833(i) should resolve this issue as well.
Proposed resolution:
constexprSection: 22.6.3.2 [variant.ctor] Status: Open Submitter: Richard Smith Opened: 2016-11-28 Last modified: 2025-08-22
Priority: 2
View other active issues in [variant.ctor].
View all other issues in [variant.ctor].
View all issues with Open status.
Discussion:
The library has lots of functions declared constexpr, but it's not clear what that means. The constexpr
keyword implies that there needs to be some invocation of the function, for some set of template
arguments and function arguments, that is valid in a constant expression (otherwise the program would be ill-formed,
with no diagnostic required), along with a few side conditions. I suspect the library intends to require something a
lot stronger than that from implementations (something along the lines of "all calls that could reasonably be constant
subexpressions are in fact constant subexpressions, unless otherwise stated").
"This function shall be
constexprif and only if the value-initialization of the alternative typeT0would satisfy the requirements for aconstexprfunction."
This is the wrong constraint: instead of constraining whether the function is constexpr, we should constrain
whether a call to it is a constant subexpression.
Daniel:
This is has some considerable overlap with LWG 2289(i) but is phrased in a more general way.[2016-12-16, Issues Telecon]
Priority 2; this is also the general case of 2829(i).
[2017-02-20, Alisdair comments and suggests concrete wording]
Below is is draft wording I was working on at Issaquah to try to address both issues.
[2017-11 Albuquerque Wednesday issue processing]
Status to Open; really needs a paper.
STL says "What about plus<T>?" plus<int> needs to be usable in a constexpr context, but plus<string> can't be.
[2017-11 Albuquerque Saturday issues processing]
Geoffrey to write a paper resolving this.
[2018-06 Rapperswil Thursday issues processing]
Geoffrey has been unable to write this paper due to time constraints. He wrote up his progress here. Daniel has offered to help someone to write this paper; he's willing to be a co-author.
[2018-08-23 Batavia Issues processing]
Michael Wong to investigate.
Previous resolution from Daniel [SUPERSEDED]:This wording is relative to N4640.
Modify 16.4.6.7 [constexpr.functions] as indicated:
17.6.5.6
constexprfunctions and constructors [constexpr.functions]-1- This International Standard explicitly requires that certain standard library functions are
constexpr(9.2.6 [dcl.constexpr]). If the specification for a templated entity requires that it shall be aconstexprtemplated entity, then that templated entity shall be usable in a constant expression.. An implementationshall notmay declareanyadditional standard library function signature asconstexprexcept for those where it is explicitly required. Within any header that provides any non-defining declarations ofconstexprfunctions or constructors an implementation shall provide corresponding definitions.
[2020-06-08 Nina Dinka Ranns comments and provides alternative wording]
The revised wording draft also resolves LWG 2289(i), LWG 2829(i), and LWG 3215(i).
Previous resolution [SUPERSEDED]:This wording is relative to N4861.
1. Modify 16.4.6.7 [constexpr.functions] as indicated:
-1- This document explicitly requires that certain standard library functions are
-?- Letconstexpr(9.2.6 [dcl.constexpr]). An implementation shall not declare any standard library function signature asconstexprexcept for those where it is explicitly required. Within any header that provides any non-defining declarations of constexpr functions or constructors an implementation shall provide corresponding definitions.Fdenote a standard library function template or member function of a class template. If the specification ofFdeclares it to beconstexpr, unless otherwise specified, thenFcan be used in a constant expression if and only if all the expressions that are evaluated as specified in the description ofF's semantics can be used in a constant expression.2. - 10. […] // Remainder of Nina's update
[2020-10-02 Jens Maurer improves wording]
Specifically the wording for 16.4.6.7 [constexpr.functions] needs improvement and is updated below.
[2020-10-02 Tim Song comments]
The new wording doesn't cover the following example:
// global scope int x; int y; constexpr int j = (std::swap(x, y), 0); // error
swap is a "standard library function template...declared constexpr"
x and y are (lvalue) constant expressions
std::swap(x, y) is plainly not a constant expression
[2020-10-04 Jens Maurer comments]
Yes, we're still lacking text for that (and maybe Nina's old text helps for that).
[2020-12-14; Jiang An comments]
The item "constexpr functions" is also used in 23.2.2 [container.requirements.general]/14 and 24.3.1 [iterator.requirements.general]/16, and such usage should also be modified by this issue here.
[St. Louis 2024-06-24;
Re-confirmed Tim's previous observation, new P/R needed.
Jens says there are two ways that swap could work, and the library doesn't
actually say how it does what it does, so it's not possible for a reader to
know whether they can expect it to be usable in a constant expression.
]
[2025-08-22; Discussion of editorial issue #4954]
During the discussion of editorial issue #4954 it has been suggested that this issue here should also attempt to find a replacement for the definition of the term "constant initializer" which got lost by CWG 2366 but is still referenced by 24.6.2.2 [istream.iterator.cons] p3.
Proposed resolution:
This wording is relative to N4861.
Modify 16.4.6.7 [constexpr.functions] as indicated:
-1- This document explicitly requires that certain standard library functions are
-?- Letconstexpr(9.2.6 [dcl.constexpr]). An implementation shall not declare any standard library function signature asconstexprexcept for those where it is explicitly required. Within any header that provides any non-defining declarations of constexpr functions or constructors an implementation shall provide corresponding definitions.Fdenote a standard library function template or member function of a class template declaredconstexpr. Unless otherwise specified, a function call expression (7.6.1.3 [expr.call]) whose postfix-expression namesFis a constant expression if all of the argument subexpressions are constant expressions.
Modify 22.3.2 [pairs.pair] as indicated:
-2- The defaulted
move and copyconstructors, respectively,ofpairis a constexpr functioncan be used in a constant expression if and only if all required element-wise initializationsfor move and copy, respectively, would satisfy the requirements for a constexpr functioncan be used in a constant expression.
Modify 22.4.4.2 [tuple.cnstr] as indicated:
-3- The defaulted
move and copyconstructors, respectively,oftupleis a constexpr functioncan be used in a constant expression if and only if all required element-wise initializationsfor move and copy, respectively, would satisfy the requirements for a constexpr functioncan be used in a constant expression. The defaultedmove and copyconstructors oftuple<>are constexpr functionscan be used in a constant expression.
Modify 22.5.3.2 [optional.ctor] as indicated:
constexpr optional() noexcept; constexpr optional(nullopt_t) noexcept;[…]-1- […]
-2- Remarks: No contained value is initialized.For every object typeTthese constructors are constexpr constructors (9.2.6 [dcl.constexpr]).template<class... Args> constexpr explicit optional(in_place_t, Args&&... args);-12- […]
-13- […] -14- […] -15- […]-16- Remarks: IfT's constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor.template<class U, class... Args> constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);-17- […]
-18- […] -19- […] -20- […]-21- Remarks: IfT's constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor.template<class U = T> constexpr explicit(see below) optional(U&& v);-22- […]
-23- […] -24- […] -25- […] -26- Remarks:IfThe expression insideT's constructor selected for the initialization is a constexpr constructor, this constructor is a constexpr constructor.explicitis equivalent to:!is_convertible_v<U, T>
Modify 22.5.3.7 [optional.observe] as indicated:
constexpr const T* operator->() const; constexpr T* operator->();-1- […]
-2- […] -3- […]-4- Remarks: These functions are constexpr functions.constexpr const T& operator*() const&; constexpr T& operator*() &;[…]-5- […]
-6- […] -7- […]-8- Remarks: These functions are constexpr functions.constexpr explicit operator bool() const noexcept;-11- Returns:
trueif and only if*thiscontains a value.-12- Remarks: This function is a constexpr function.constexpr bool has_value() const noexcept;-13- Returns:
trueif and only if*thiscontains a value.-14- Remarks: This function is a constexpr function.
Modify 22.5.7 [optional.relops] as indicated:
template<class T, class U> constexpr bool operator==(const optional<T>& x, const optional<U>& y);-1- […]
-2- […]-3- Remarks: Specializations of this function template for which*x == *yis a core constant expression are constexpr functions.template<class T, class U> constexpr bool operator!=(const optional<T>& x, const optional<U>& y);-4- […]
-5- […]-6- Remarks: Specializations of this function template for which*x != *yis a core constant expression are constexpr functions.template<class T, class U> constexpr bool operator<(const optional<T>& x, const optional<U>& y);-7- […]
-8- […]-9- Remarks: Specializations of this function template for which*x < *yis a core constant expression are constexpr functions.template<class T, class U> constexpr bool operator>(const optional<T>& x, const optional<U>& y);-10- […]
-11- […]-12- Remarks: Specializations of this function template for which*x > *yis a core constant expression are constexpr functions.template<class T, class U> constexpr bool operator<=(const optional<T>& x, const optional<U>& y);-13- […]
-14- […]-15- Remarks: Specializations of this function template for which*x <= *yis a core constant expression are constexpr functions.template<class T, class U> constexpr bool operator>=(const optional<T>& x, const optional<U>& y);-16- […]
-17- […]-18- Remarks: Specializations of this function template for which*x >= *yis a core constant expression are constexpr functions.template<class T, three_way_comparable_with<T> U> constexpr compare_three_way_result_t<T,U> operator<=>(const optional<T>& x, const optional<U>& y);-19- Returns: If
x && y,*x <=> *y; otherwisebool(x) <=> bool(y).-20- Remarks: Specializations of this function template for which*x <=> *yis a core constant expression are constexpr functions.
Modify 22.6.3.2 [variant.ctor] as indicated:
constexpr variant() noexcept(see below);[…]-1- […]
-2- […] -3- […] -4- […] -5- […] -6- Remarks:This function isThe expression insideconstexprif and only if the value-initialization of the alternative typeT0would satisfy the requirements for a constexpr function.noexceptis equivalent tois_nothrow_default_constructible_v<T0>. [Note: See also classmonostate. — end note]template<class T> constexpr variant(T&& t) noexcept(see below);-14- […]
[…] -19- Remarks: The expression insidenoexceptis equivalent tois_nothrow_constructible_v<Tj, T>.IfTj's selected constructor is a constexpr constructor, this constructor is a constexpr constructor.template<class T, class... Args> constexpr explicit variant(in_place_type_t<T>, Args&&... args);-20- […]
[…]-24- Remarks: IfT's selected constructor is a constexpr constructor, this constructor is a constexpr constructor.template<class T, class U, class... Args> constexpr explicit variant(in_place_type_t<T>, initializer_list<U> il, Args&&... args);-25- […]
[…]-29- Remarks: IfT's selected constructor is a constexpr constructor, this constructor is a constexpr constructor.template<size_t I, class... Args> constexpr explicit variant(in_place_index_t<I>, Args&&... args);-30- […]
[…]-34- Remarks: IfTI's selected constructor is a constexpr constructor, this constructor is a constexpr constructor.template<size_t I, class U, class... Args> constexpr explicit variant(in_place_index_t<I>, initializer_list<U> il, Args&&... args);-35- […]
[…]-38- Remarks: IfTI's selected constructor is a constexpr constructor, this constructor is a constexpr constructor.
Modify 24.5.4.11 [move.sent.ops] as indicated:
constexpr move_sentinel();-1- Effects: Value-initializes
last.Ifis_trivially_default_constructible_v<S>istrue, then this constructor is a constexpr constructor.
Modify 22.11.3 [bit.cast] as indicated:
template<class To, class From> constexpr To bit_cast(const From& from) noexcept;-1- […]
-3- Remarks: This functioniscan be used in a constant expression if and only ifconstexprTo,From, and the types of all subobjects ofToand From are typesTsuch that:
(3.1) —
is_union_v<T>isfalse;(3.2) —
is_pointer_v<T>isfalse;(3.3) —
is_member_pointer_v<T>isfalse;(3.4) —
is_volatile_v<T>isfalse; and(3.5) —
Thas no non-static data members of reference type.
Modify 30.5 [time.duration] as indicated:
-5- The defaulted
copyconstructors of durationshall be a constexpr functioncan be used in a constant expression if and only if the required initialization of the memberrep_for copy and move, respectively, would satisfy the requirements for a constexpr functioncan be used in a constant expression.
a_uniq.insert(i, j)Section: 23.2.7 [associative.reqmts], 23.2.8 [unord.req] Status: Open Submitter: Matt Austern Opened: 2016-12-14 Last modified: 2020-02-14
Priority: 3
View other active issues in [associative.reqmts].
View all other issues in [associative.reqmts].
View all issues with Open status.
Discussion:
If we write a_uniq.insert(i, j) and [i, j) has multiple elements with keys that compare equivalent,
which ones get inserted? Consider, for example, inserting into a map<string, int> with
m.insert({{"red", 5}, {"green", 3}, {"red", 7}, {"blue", 2}, {"pink", 6}});
Which value for "red" will the map have?
"red" -> 5, and I suspect that's true on most or all implementations, but I don't believe
that's guaranteed by anything in the requirements. The wording in Table 90 just says that it "inserts each element from
the range [i, j) if and only if there is no element with key equivalent to the key of that element", but that
doesn't tell us what happens if [i, j) contains duplicate keys because it doesn't say what order the insertions
are performed in. The standard should either guarantee that the first value is the one that gets inserted, or explicitly
say that this is unspecified.
The same issue applies to the range constructor, and to the unordered associative containers.
[2017-01-27 Telecon]
Priority 3; Nico to provide wording.
[2020-02-14, Prague]
LWG discussion. Suggestion to specify that we like the direction of the wording for insert of
unordered containers, but would also like to clarify that the loop is meant to be "in order" of the
sequence elements.
Proposed resolution:
enable_if, result_of, common_type and aligned_storage do not meet the definition
of TransformationTraitSection: 21.3.2 [meta.rqmts] Status: New Submitter: Tim Song Opened: 2016-12-14 Last modified: 2017-02-02
Priority: 3
View all other issues in [meta.rqmts].
View all issues with New status.
Discussion:
[meta.rqmts]/3 defines TransformationTrait as follows:
A
TransformationTraitmodifies a property of a type. It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the modification. It shall define a publicly accessible nested type namedtype, which shall be a synonym for the modified type.
enable_if, result_of and common_type do not necessarily "define a publicly accessible nested type named type".
aligned_storage takes no template type argument (it only has two non-type parameters). Yet [meta.trans]/2
says that they are all TransformationTraits.
decay, it's not clear that any of the traits in [meta.trans.other]
could really be described as "modify[ing] a property of a type".
[2017-01-27 Telecon]
Priority 3
Proposed resolution:
Section: 29.4.10 [cmplx.over], 29.7.1 [cmath.syn] Status: New Submitter: Jens Maurer Opened: 2016-12-15 Last modified: 2019-03-18
Priority: 3
View all other issues in [cmplx.over].
View all issues with New status.
Discussion:
In [cmplx.over] and [cmath.syn], when talking about "sufficient additional overloads", we use the phrase "effectively cast", but that is not a defined term.
A hostile interpretation could read "reinterpret_cast" here.
Likely we mean "apply floating-point promotions, floating-integral
conversions, and floating-point conversions", but that should be
spelled out somewhere, e.g. in the library definitions section.
(Source: Editorial issue #1248)
[2017-01-27 Telecon]
Priority 3
[2019-03-16; Daniel comments and provides wording]
I decided to use the form "implicitly converted" and to refer to 7.3 [conv] for 29.7.1 [cmath.syn] and
29.4.10 [cmplx.over], because those conversions can all be done implicitly. This also holds for the
pow specification 29.4.10 [cmplx.over] p3, because the described conversions of
complex<T> to complex<U> involve only the need of non-explicit constructors.
Proposed resolution:
This wording is relative to N4810.
Change 29.4.10 [cmplx.over], as indicated:
-2- The additional overloads shall be sufficient to ensure:
-3 Function template
(2.1) — If the argument has type
long double, then it iseffectively castimplicitly converted (7.3 [conv]) tocomplex<long double>.(2.2) — Otherwise, if the argument has type
doubleor an integer type, then it iseffectively castimplicitly converted tocomplex<double>.(2.3) — Otherwise, if the argument has type
float, then it iseffectively castimplicitly converted tocomplex<float>.powshall have additional overloads sufficient to ensure, for a call with at least one argument of typecomplex<T>:
(3.1) — If either argument has type
complex<long double>or typelong double, then both arguments areeffectively castimplicitly converted (7.3 [conv]) tocomplex<long double>.(3.2) — Otherwise, if either argument has type
complex<double>,double, or an integer type, then both arguments areeffectively castimplicitly converted tocomplex<double>.(3.3) — Otherwise, if either argument has type
complex<float>orfloat, then both arguments areeffectively castimplicitly converted tocomplex<float>.
Change 29.7.1 [cmath.syn], as indicated:
-2- For each set of overloaded functions within
<cmath>, with the exception ofabs, there shall be additional overloads sufficient to ensure:
If any argument of arithmetic type corresponding to a
doubleparameter has typelong double, then all arguments of arithmetic type (6.9.2 [basic.fundamental]) corresponding todoubleparameters areeffectively castimplicitly converted (7.3 [conv]) tolong double.Otherwise, if any argument of arithmetic type corresponding to a
doubleparameter has typedoubleor an integer type, then all arguments of arithmetic type corresponding todoubleparameters areeffectively castimplicitly converted todouble.Otherwise, all arguments of arithmetic type corresponding to
doubleparameters have typefloat.
sin(float) should call sinf(float)Section: 29.7.1 [cmath.syn] Status: New Submitter: Jens Maurer Opened: 2016-12-15 Last modified: 2020-09-06
Priority: 3
View other active issues in [cmath.syn].
View all other issues in [cmath.syn].
View all issues with New status.
Discussion:
With P0175R1, we now show in [cmath.syn] three overloads for
the sin function: One taking a float, one taking a double,
and one taking a long double. However, there is no statement that sin(long double)
should actually invoke sinl, presumably delivering extra precision.
inline long double sin(long double x)
{ return sinf(x); }
seems to satisfy the "effectively cast" requirement, but is certainly unintentional.
The same issue arises for all math functions inherited from C. (Source: Editorial issue #1247)[2017-01-27 Telecon]
Priority 3
Proposed resolution:
Section: 20.5.5.2 [mem.res.pool.options] Status: New Submitter: Jens Maurer Opened: 2016-12-15 Last modified: 2017-02-02
Priority: 3
View all issues with New status.
Discussion:
20.5.5.2 [mem.res.pool.options] p3 talks about a "pass-through-threshold".
First, the phrase is not defined and it seems it could be easily avoided given the context. Second, given the phrasing here, it seems the implementation is essentially allowed to ignore the valuelargest_required_pool_block as it sees fit. It is unclear whether
that is the intention.
[2017-01-27 Telecon]
Priority 3; Jonathan will ask Alisdair for wording.
Proposed resolution:
Section: 24.5.1 [reverse.iterators] Status: New Submitter: Hubert Tong Opened: 2017-01-28 Last modified: 2020-09-06
Priority: 4
View all other issues in [reverse.iterators].
View all issues with New status.
Discussion:
Further to LWG 2472(i), the case of reverse_iterator comparisons is a regression introduced
by LWG 280(i).
#include <utility>
#include <iterator>
using namespace std::rel_ops;
bool f(std::reverse_iterator<int *> it) { return it != it; }
Under C++03, the operator!= in lib.reverse.iterator is more specialized than the operator!= in std::rel_ops.
operator!= candidate is more specialized than the other. The program is
observed to fail with libc++.
Online compiler example, see here.
Suggested resolution:
Reintroduce the homogeneous comparison operators from C++03 alongside the new ones.
[2017-03-04, Kona]
Set priority to 4. STL to write a paper deprecating relops Alisdair to provide an example for Annex C.
Proposed resolution:
This wording is relative to N4618.
Modify 24.5.1 [reverse.iterators], class template reverse_iterator synopsis, as indicated:
template <class Iterator1, class Iterator2>
constexpr bool operator==(
const reverse_iterator<Iterator1>& x,
const reverse_iterator<Iterator2>& y);
template <class Iterator1, class Iterator2>
constexpr bool operator<(
const reverse_iterator<Iterator1>& x,
const reverse_iterator<Iterator2>& y);
template <class Iterator1, class Iterator2>
constexpr bool operator!=(
const reverse_iterator<Iterator1>& x,
const reverse_iterator<Iterator2>& y);
template <class Iterator1, class Iterator2>
constexpr bool operator>(
const reverse_iterator<Iterator1>& x,
const reverse_iterator<Iterator2>& y);
template <class Iterator1, class Iterator2>
constexpr bool operator>=(
const reverse_iterator<Iterator1>& x,
const reverse_iterator<Iterator2>& y);
template <class Iterator1, class Iterator2>
constexpr bool operator<=(
const reverse_iterator<Iterator1>& x,
const reverse_iterator<Iterator2>& y);
template <class Iterator>
constexpr bool operator==(
const reverse_iterator<Iterator>& x,
const reverse_iterator<Iterator>& y);
template <class Iterator>
constexpr bool operator<(
const reverse_iterator<Iterator>& x,
const reverse_iterator<Iterator>& y);
template <class Iterator>
constexpr bool operator!=(
const reverse_iterator<Iterator>& x,
const reverse_iterator<Iterator>& y);
template <class Iterator>
constexpr bool operator>(
const reverse_iterator<Iterator>& x,
const reverse_iterator<Iterator>& y);
template <class Iterator>
constexpr bool operator>=(
const reverse_iterator<Iterator>& x,
const reverse_iterator<Iterator>& y);
template <class Iterator>
constexpr bool operator<=(
const reverse_iterator<Iterator>& x,
const reverse_iterator<Iterator>& y);
Modify [reverse.iter.op==] as indicated:
template <class Iterator1, class Iterator2> constexpr bool operator==( const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y); template <class Iterator> constexpr bool operator==( const reverse_iterator<Iterator>& x, const reverse_iterator<Iterator>& y);-1- Returns:
x.current == y.current.
Modify [reverse.iter.op<] as indicated:
template <class Iterator1, class Iterator2> constexpr bool operator<( const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y); template <class Iterator> constexpr bool operator<( const reverse_iterator<Iterator>& x, const reverse_iterator<Iterator>& y);-1- Returns:
x.current > y.current.
Modify [reverse.iter.op!=] as indicated:
template <class Iterator1, class Iterator2> constexpr bool operator!=( const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y); template <class Iterator> constexpr bool operator!=( const reverse_iterator<Iterator>& x, const reverse_iterator<Iterator>& y);-1- Returns:
x.current != y.current.
Modify [reverse.iter.op>] as indicated:
template <class Iterator1, class Iterator2> constexpr bool operator>( const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y); template <class Iterator> constexpr bool operator>( const reverse_iterator<Iterator>& x, const reverse_iterator<Iterator>& y);-1- Returns:
x.current < y.current.
Modify [reverse.iter.op>=] as indicated:
template <class Iterator1, class Iterator2> constexpr bool operator>=( const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y); template <class Iterator> constexpr bool operator>=( const reverse_iterator<Iterator>& x, const reverse_iterator<Iterator>& y);-1- Returns:
x.current <= y.current.
Modify [reverse.iter.op<=] as indicated:
template <class Iterator1, class Iterator2> constexpr bool operator<=( const reverse_iterator<Iterator1>& x, const reverse_iterator<Iterator2>& y); template <class Iterator> constexpr bool operator<=( const reverse_iterator<Iterator>& x, const reverse_iterator<Iterator>& y);-1- Returns:
x.current >= y.current.
Section: 22.6.3 [variant.variant] Status: New Submitter: Switzerland Opened: 2017-02-03 Last modified: 2020-09-06
Priority: 3
View all other issues in [variant.variant].
View all issues with New status.
Discussion:
Addresses CH 7Consider making the variant statically !valueless_by_exception() for cases where is_nothrow_move_constructible_v<T_i> for all alternative types Ti
Proposed change: Adopt section III of P0308R0.
[2017-07 Toronto Thurs Issue Prioritization]
Priority 3. This is similar to 2904(i), Casey to investigate
Proposed resolution:
string_view parameters instead or in addition for functions
defined with char const * or string const & as parameter types.Section: 27.3 [string.view] Status: LEWG Submitter: Switzerland Opened: 2017-02-03 Last modified: 2025-10-21
Priority: 4
View other active issues in [string.view].
View all other issues in [string.view].
View all issues with LEWG status.
Discussion:
Addresses CH 9
The standard library should provide string_view parameters instead or in addition for functions defined with
char const * or string const & as parameter types. Most notably in cases where both such
overloads exist or where an internal copy is expected anyway.
It might be doubted that the non-null termination of string_view could be an issue with functions that pass
the char * down to OS functions, such as fstream_buf::open() etc. and those shouldn't provide it
and favour generating a std::string temporary instead in that case. However, std::path demonstrates
it is usable to have string_view overloads and there might be many places where it can be handy, or even better.
Proposed change: Provide the overloads for std::regex, the exception classes, std::bitset,
std::locale and more.
[Post-Kona 2017]
Most (all?) of these changes were proposed in P0506r1, which was discussed by LEWG in Kona.
[2017-07 Toronto Thurs Issue Prioritization]
Status LEWG - they're already looking at this.
[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
Section: 23 [containers], 22 [utilities] Status: LEWG Submitter: Finland Opened: 2017-02-03 Last modified: 2025-10-21
Priority: 4
View other active issues in [containers].
View all other issues in [containers].
View all issues with LEWG status.
Discussion:
Addresses FI 16Relational operators for containers should sfinae; if the underlying type is not comparable, neither should the container be. Same applies to tuple and pair.
Proposed change: Make the relational operators of containers and utility components reflect the validity of the underlying element types.
[ 2017-06-27 Moved to LEWG after 5 positive votes on c++std-lib. ]
[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
shared_ptr from a
unique_ptrSection: 20.3.2.2.2 [util.smartptr.shared.const] Status: New Submitter: United States Opened: 2017-02-03 Last modified: 2017-07-15
Priority: 3
View other active issues in [util.smartptr.shared.const].
View all other issues in [util.smartptr.shared.const].
View all issues with New status.
Discussion:
Addresses US 130There is no ability to supply an allocator for the control block when constructing a shared_ptr from a unique_ptr.
Note that no further shared_ptr constructors need an allocator, as they all have pre-existing control blocks that are shared,
or already have the allocator overload.
Proposed change: Add an additional shared_ptr constructor,
template <class Y, class D, class A> shared_ptr(unique_ptr<Y, D>&& r, A alloc), with the same semantics
as the existing constructor taking a unique_ptr, but using the alloc argument to supply memory as required.
[2017-07 Toronto Thurs Issue Prioritization]
Priority 3; Alisdair to provide wording
Proposed resolution:
noexcept is inconsistently applied across headers which import components of the C standard librarySection: 29.7.1 [cmath.syn] Status: New Submitter: United States Opened: 2017-02-03 Last modified: 2017-07-15
Priority: 4
View other active issues in [cmath.syn].
View all other issues in [cmath.syn].
View all issues with New status.
Discussion:
Addresses US 172noexcept is inconsistently applied across headers which import components of the C standard library into the C++ library;
some functions (std::abort(), std::_Exit(), etc) are defined as noexcept in some places, but not in others.
Some functions which seem like they should be noexcept (std::abs(), std::div(), etc) are not defined as
noexcept.
Proposed change: Make the majority of the C library functions (with exceptions such as std::qsort() and
std::bsearch(), which can call user code) noexcept.
[2017-07 Toronto Thurs Issue Prioritization]
Priority 4
Proposed resolution:
std::nextSection: 24.4.3 [iterator.operations] Status: Open Submitter: Morwenn Opened: 2017-02-04 Last modified: 2018-12-03
Priority: 3
View other active issues in [iterator.operations].
View all other issues in [iterator.operations].
View all issues with Open status.
Discussion:
It seems that std::next is missing an optimization opportunity when taking a single parameter. The standard
mandates that std::next shall call std::advance on the passed iterator and return it. For random-access
iterators, it means that operator+= will be called on the iterator. However, if a single-argument overload was
added to std::next, it could call ++it directly instead of std::advance(it, 1), which means
that operator++ would be called instead of operator+=. This might make a small performance difference
for complicated iterators such as std::deque's ones, where operator++ has a simpler logic than
operator+=.
std::prev too.
[2017-03-04, Kona]
Set priority to 3. Alisdair to provide wording.
[2018-11-30, Jonathan comments, recommending NAD]
Jonathan suggested NAD, because the proposed "just use increment when n==1" optimization can be done in
std::next (and/or std::advance, and/or complicated iterators like deque::iterator) without
adding an overload. Billy said the overload would avoid metaprogramming costs for dispatching to the right
std::advance, and help in non-optimized builds. Zhihao said the overload would make it clear to users that
the n==1 case is optimized (Jonathan thinks this is irrelevant as there's no requirement that we tell users
what we optimize).
Proposed resolution:
Section: 21.3.3 [meta.type.synop] Status: Open Submitter: Daniel Krügler Opened: 2017-03-02 Last modified: 2024-08-21
Priority: 2
View other active issues in [meta.type.synop].
View all other issues in [meta.type.synop].
View all issues with Open status.
Discussion:
LWG 2797(i) (RU 2) suggests that certain type-traits should be required to
diagnose violations of their pre-conditions. The basic idea is founded and I see no problems for
requiring this for the mentioned traits alignment_of or is_base_of,
for example. But if we want to require this diagnostics for some other traits, such as is_convertible,
is_constructible (and friends), or is_callable (and possibly some others), we really should
be sure that our current requirements are OK.
X:
struct X; // Never defined static_assert(std::is_convertible_v<X, const X&>);
Unfortunately we cannot always allow incomplete types, because most type constructions or conversions indeed require a complete type, so generally relaxing the current restrictions is also not an option.
The core language has a solution for this "small" gap of situations, where the response of the compiler might depend on type completeness: Undefined behaviour. So, I believe we need a somewhat more detailled form to express the intend here. Informally, I would suggest that the program should only be ill-formed in the situation described by LWG 2797(i), if there exists the possibility that the compiler would require complete types for the considered operation. The example shown above,std::is_convertible_v<X, const X&>, would never
require the need to complete X, so here no violation should exist.
The presented example might seem a tiny one, but the Standard Library type traits are extreme fundamental tools and we should
try to not give the impression that an approximate rule of the current type constraints breaks reasonable code.
It is correct, that above example has currently undefined behaviour due to the breakage of pre-conditions, therefore
this issue suggests to fix the current situation before enforcing a diagnostic for such valid situations.
[2017-03-04, Kona]
Set priority to 2. Is related to 2797(i), but really needs an audit of the type traits.
[2018-08 Batavia Monday issue discussion]
Issues 2797(i), 2939(i), 3022(i), and 3099(i) are all closely related. Walter to write a paper resolving them.
[2020-02 Prague Thursday issue discussion]
Two of the issues (2797(i) and 3022(i)) had been resolved by the acceptance of P1285R0.
[2024-05-09; Jonathan provides wording]
We could also relax the type completeness requirements for
reference_converts_from_temporary and reference_constructs_from_temporary,
as the result is always false if the first type is a non-reference,
so we don't need complete types in that case.
This doesn't seem important to support, but if we wanted to then we could say:
EitherTis not a reference type, orTandUshall be a complete typecomplete types, cvvoid, or an arrayarraysof unknown bound.
Previous resolution [SUPERSEDED]:
This wording is relative to N4981.
- In 21.3.6.4 [meta.unary.prop] Table 51, change the Preconditions text for
is_constructible,is_trivially_constructible,is_nothrow_constructible,is_convertible, andis_nothrow_convertible, as indicated.
Template Condition Preconditions … … … template<class T, class... Args> struct is_constructible;For a function type Tor for a cvvoidtypeT,is_constructible_v<T, Args...>isfalse, otherwise see below .Either Tis a reference type andArgscontains a single type that is similar (7.3.6 [conv.qual]) toremove_reference_t<T>, orTand all types in the template parameter packArgsshall be complete types, cvvoid, or arrays of unknown bound.… … … template<class T, class... Args> struct is_trivially_constructible;is_constructible_v<T, Args...>istrueand the variable definition foris_constructible, as defined below, is known to call no operation that is not trivial (6.9.1 [basic.types.general], 11.4.4 [special]).Either Tis a reference type andArgscontains a single type that is similar (7.3.6 [conv.qual]) toremove_reference_t<T>, orTand all types in the template parameter packArgsshall be complete types, cvvoid, or arrays of unknown bound.… … … template<class T, class... Args> struct is_nothrow_constructible;is_constructible_v<T, Args...>istrueand the variable definition foris_constructible, as defined below, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]).Either Tis a reference type andArgscontains a single type that is similar (7.3.6 [conv.qual]) toremove_reference_t<T>, orTand all types in the template parameter packArgsshall be complete types, cvvoid, or arrays of unknown bound.… … … - In 21.3.8 [meta.rel] Table 53, change the Comments text for
is_convertibleandis_nothrow_convertibleas indicated.
Template Condition Comments … … … template<class From, class To> struct is_convertible;see below Either Tois a reference type andFromis similar (7.3.6 [conv.qual]) toremove_reference_t<To>, orFromandToshall be complete types, cvvoid, or arrays of unknown bound.template<class From, class To> struct is_nothrow_convertible;is_convertible_v<From, To>istrueand the conversion, as defined byis_convertible, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]).Either Tois a reference type andFromis similar (7.3.6 [conv.qual]) toremove_reference_t<To>, orFromandToshall be complete types, cvvoid, or arrays of unknown bound.
[2024-08-21; Jonathan provides improved wording]
Following on LWG telecon review, change "Args contains a single type that is similar" to "Args contains a single type and that type is similar".
Proposed resolution:
This wording is relative to N4988.
- In 21.3.6.4 [meta.unary.prop] Table 51, change the Preconditions text for
is_constructible,is_trivially_constructible,is_nothrow_constructible,is_convertible, andis_nothrow_convertible, as indicated.
Template Condition Preconditions … … … template<class T, class... Args> struct is_constructible;For a function type Tor for a cvvoidtypeT,is_constructible_v<T, Args...>isfalse, otherwise see below .Either Tis a reference type andArgscontains a single type and that type is similar (7.3.6 [conv.qual]) toremove_reference_t<T>, orTand all types in the template parameter packArgsshall be complete types, cvvoid, or arrays of unknown bound.… … … template<class T, class... Args> struct is_trivially_constructible;is_constructible_v<T, Args...>istrueand the variable definition foris_constructible, as defined below, is known to call no operation that is not trivial (6.9.1 [basic.types.general], 11.4.4 [special]).Either Tis a reference type andArgscontains a single type and that type is similar (7.3.6 [conv.qual]) toremove_reference_t<T>, orTand all types in the template parameter packArgsshall be complete types, cvvoid, or arrays of unknown bound.… … … template<class T, class... Args> struct is_nothrow_constructible;is_constructible_v<T, Args...>istrueand the variable definition foris_constructible, as defined below, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]).Either Tis a reference type andArgscontains a single type and that type is similar (7.3.6 [conv.qual]) toremove_reference_t<T>, orTand all types in the template parameter packArgsshall be complete types, cvvoid, or arrays of unknown bound.… … … - In 21.3.8 [meta.rel] Table 53, change the Comments text for
is_convertibleandis_nothrow_convertibleas indicated.
Template Condition Comments … … … template<class From, class To> struct is_convertible;see below Either Tois a reference type andFromis similar (7.3.6 [conv.qual]) toremove_reference_t<To>, orFromandToshall be complete types, cvvoid, or arrays of unknown bound.template<class From, class To> struct is_nothrow_convertible;is_convertible_v<From, To>istrueand the conversion, as defined byis_convertible, is known not to throw any exceptions (7.6.2.7 [expr.unary.noexcept]).Either Tois a reference type andFromis similar (7.3.6 [conv.qual]) toremove_reference_t<To>, orFromandToshall be complete types, cvvoid, or arrays of unknown bound.
Section: 31.12.8.1 [fs.enum.path.format], 31.12.11 [fs.class.directory.iterator], 31.12.13.4 [fs.op.copy] Status: New Submitter: Thomas Köppe Opened: 2017-03-14 Last modified: 2023-02-07
Priority: 3
View all issues with New status.
Discussion:
During the application of P0430R2, we came across several terms that seem insufficiently clear and lacking proper definitions.
We would like clarification on what those terms mean, and we would welcome wording suggestions, or alternatively a verbose explanation and dispensation to change the presentation editorially. The items in question are:What does it mean to 'always interpret [the character sequence] in the same way'? ([fs.enum.path.format], which is [path.format] in the paper)
What is a "directory-like" file type? ([fs.class.directory_iterator])
What is an "implementation-defined file type"? (e.g. [fs.op.copy])
[2017-07 Toronto Wed Issue Prioritization]
Priority 3
[2018-01-16, Jonathan comments]
In 31.12.8.1 [fs.enum.path.format] "always interpreted in the same way" means
irrespective of the path::format value, or the content of the string.
Maybe add ", rather than depending on the path::format value or the
content of the character sequence".
Proposed resolution:
Section: 16 [library] Status: New Submitter: Jens Maurer Opened: 2017-03-20 Last modified: 2017-07-15
Priority: 4
View other active issues in [library].
View all other issues in [library].
View all issues with New status.
Discussion:
This is from editorial issue #1088:
It is not always made explicit whether the requirement is referring to time or space complexity, or both."Linear time." vs. "Linear."
"Constant time." vs. "Constant."
16.3.2.4 [structure.specifications] says that the Complexity element specifies "the time and/or space complexity of the function", so being explicit about this would be good.
Examples:[sequence.reqmts] a.clear()
[deque.cons]
[vector.cons]
[map.cons]
[multimap.cons]
[set.cons]
[multiset.cons]
[unord.map.cnstr]
[unord.multimap.cnstr]
etc.
[2017-07 Toronto Wed Issue Prioritization]
Priority 4; Robert to look at
Proposed resolution:
char_traits<char16_t>::eof is a valid UTF-16 code unitSection: 27.2.4.4 [char.traits.specializations.char16.t] Status: New Submitter: Jonathan Wakely Opened: 2017-05-05 Last modified: 2019-04-02
Priority: 3
View all other issues in [char.traits.specializations.char16.t].
View all issues with New status.
Discussion:
The standard requires that char_traits<char16_t>::int_type is
uint_least16_t, so when that has the same representation as char16_t
there are no bits left to represent the eof value.
— The member
eof()shall return an implementation-defined constant that cannot appear as a valid UTF-16 code unit.
Existing practice is to use the "noncharacter" u'\uffff' for this
value, but the Unicode spec is clear that U+FFFF and other
noncharacters are valid, and their appearance in a UTF-16 string does
not make it ill-formed. See here and
here:
The fact that they are called "noncharacters" and are not intended for open interchange does not mean that they are somehow illegal or invalid code points which make strings containing them invalid.
In practice this means there's no way to tell if
basic_streambuf<char16_t>::sputc(u'\uffff') succeeded or not. If it
can insert the character it returns to_int_type(u'\uffff') and
otherwise it returns eof(), which is the same value.
char_traits<char16_t>::to_int_type(char_type c) can be
defined to transform U+FFFF into U+FFFD, so that the invariant
eq_int_type(eof(), to_int_type(c)) == false holds for any c (and the
return value of sputc will be distinct from eof). I don't think any
implementation currently meets that invariant.
I think at the very least we need to correct the statement "The member
eof() shall return an implementation-defined constant that cannot
appear as a valid UTF-16 code unit", because there are no such
constants if sizeof(uint_least16_t) == sizeof(char16_t).
This issue is closely related to LWG 1200(i), but there it's a
slightly different statement of the problem, and neither the
submitter's recommendation nor the proposed resolution solves this
issue here. It seems that was closed as NAD before the Unicode corrigendum
existed, so at the time our standard just gave "surprising results"
but wasn't strictly wrong. Now it makes a normative statement that
conflicts with Unicode.
[2017-07 Toronto Wed Issue Prioritization]
Priority 3
Proposed resolution:
InputIteratorSection: 24.3.5.3 [input.iterators] Status: Open Submitter: Gašper Ažman Opened: 2017-05-10 Last modified: 2025-10-15
Priority: 2
View other active issues in [input.iterators].
View all other issues in [input.iterators].
View all issues with Open status.
Discussion:
In Table 95 in 24.3.5.3 [input.iterators], it is specified that the expression *a returns reference,
which must be convertible to value_type. This is not true for move-only types, which incidentally means that
std::vector<std::unique_ptr<int>> does not possess even a lowly InputIterator, which is, of
course, absurd.
Convertibility is too strong for all algorithms
No algorithm in the standard library requires convertibility tovalue_type. If algorithms require things that smell of that, they specify the assignment or constructibility flavor they need directly. I checked this by going through the specification of each and every one of them in<algorithm>and<numeric>, which highlighted several issues unrelated to this one. These issues are presented in Algorithms with underspecified iterator requirements (LWG 2963(i)).referenceneeds to be related tovalue_typeAlgorithms need this for the following reasons:
lifetime-extension: served as adequately by
T const&as byT. Also works for iterators that return by value.T&&also correctly binds toT const&.passing to predicates: again, served adequately by
T const&writing to
*result: not provided by the requirement anyway.capture-by-copy: currently implicitly guaranteed, but unused in the standard library (always specified separately). A separate specification can always be made for algorithms that need to capture-by-copy.
We must give due consideration to code that so far required its inputs to be
Since such code is already compiling today, relaxing this requirement does not break code. The only code this could possibly break is if, in a concept checking library, theCopyConstructibleimplicitly by requiring convertibility toT. This is done in the issue LWG 2963(i), which presents the results of a comb-through of<algorithm>and<numeric>to find algorithms that have this requirement, but where it is not specified. While related issues have been identified, no algorithms seems to require more thanT const&convertibility without separately requiring convertibility toT.InputIteratorconcept requirement onreferencebeing convertible tovalue_typegets relaxed. Such a library, if it offered overloading based on most-specific modeled concept, could now, once fixed, resolve the call to a different algorithm, which could break user code that uses a hypothetical algorithm with a move-only container and was relying to select some other overload for move-only types based on the implicitCopyConstructibleassertion provided by the iterator. In our internal concepts-checking library, we have had this issue "fixed" since the very beginning — move-only types were too important for our internal algorithms library, and also no algorithm in it seems to require something likeIterator::value_type x = *firstwithout also requiring copy-constructibility anyway.
[2017-07 Toronto Monday issue prioritization]
Priority 2; also could affect the ranges TS
Previous resolution [SUPERSEDED]:
This wording is relative to N4659.
Change Table 95 — "Input iterator requirements", 24.3.5.3 [input.iterators] as indicated:
Table 107 — Input iterator requirements (in addition to Iterator) Expression Return type Operational
semanticsAssertion/note pre-/post-condition …*areference,
convertible toT
that binds toconst T&[…] …*r++convertible toT
that binds toconst T&{Tauto&& tmp = *r;
++r;
return tmp; }
[2018-04-20; Eric Niebler provides improved wording]
The revised wording makes it clear that you can only rely on those operational semantics when
the value type is constructible from the reference type and is movable. When those conditions
aren't met, we can make no guarantees about the operational semantics of *r++ (which
is why *r++ is no longer a required expression of the InputIterator concept
in the Ranges TS).
*r++ on input iterators. Another
option would be to simply deprecate this requirement for input iterators, but that might need
a paper. (For forward iterators, *r++ is already required to return reference
exactly, and the multi-pass guarantee gives it the proper semantics.)
I also now have a question about the proposed return type of *a and *r++,
which says they must be something that "binds to const T&". Does this mean that an
iterator with a reference type reference-to-[const?]-volatile-T is no longer
considered an iterator? I don't think that's what we want to say. Perhaps these should read
"binds to const volatile T& instead, except that has the problem for InputIterators
that return prvalues that a prvalue is not bindable to a volatile reference.
[2018-11 San Diego Thursday night issue processing]
Look at Ranges; EricWF to investigate. Status to Open
[2025-10-15; additional suggestion from Eric Niebler during 2018-04 discussion]
Regarding the issue of whether int volatile* is an iterator or not,
I think the best way to solve the problem is with the common_reference_t
type trait.
The return type for *a could be
"reference, where
common_reference_t<reference&&, T&>
is well-formed"
and for *r++ could be:
"A type U, where
common_reference_t<U&&, T&>
is well-formed".
Previous resolution [SUPERSEDED]:
This wording is relative to N4741.
Change Table 89 — "Input iterator requirements", 24.3.5.3 [input.iterators] as indicated:
Table 89 — Input iterator requirements (in addition to Iterator) Expression Return type Operational
semanticsAssertion/note pre-/post-condition …*areference,
convertible toT
that binds toconst T&[…] …*r++convertible toT
that binds toconst T&When T tmp = *ris well-formed and
TisMoveConstructible, then
{ T tmp = *r;
++r;
return tmp; }
[2022-04-25; Daniel rebases wording on N4910]
Proposed resolution:
This wording is relative to N4910.
Change 24.3.5.3 [input.iterators], Table 83 — "Cpp17InputIterator requirements (in addition to Cpp17Iterator) [tab:inputiterator]" as indicated:
Table 83 — Cpp17InputIterator requirements (in addition to Cpp17Iterator) [tab:inputiterator] Expression Return type Operational
semanticsAssertion/note pre-/post-condition …*areference,
convertible toT
that binds toconst T&[…] …*r++convertible toT
that binds toconst T&When T tmp = *ris well-formed and
Tis Cpp17MoveConstructible, then
{ T tmp = *r;
++r;
return tmp; }
Section: 26 [algorithms], 29 [numerics] Status: New Submitter: Gašper Ažman Opened: 2017-05-10 Last modified: 2017-07-12
Priority: 3
View other active issues in [algorithms].
View all other issues in [algorithms].
View all issues with New status.
Discussion:
While researching whether the proposed resolution of Iterators of Containers of move-only types do not model InputIterator
(LWG 2962(i)), I came across several algorithms that underspecify their requirements, mostly with regard to some associated type of the iterator type they operate on. A list can be found below.
<algorithm> and <numeric> follows.
With the advent of concepts, these algorithms will need better specifications if we are ever hoping to be allowed to overload based on them. I want this issue to bring the standard algorithms closer to having their concept requirements directly transcribable to library annotations.
Suggested resolution:
copy, copy_if, copy_n, copy_backward
Add to description: *result shall be assignable from *first.
move, move_backward
Add to description: *result shall be move-assignable from *first.
transform
Add to description: The result of the expression op(*first) or binary_op(*first1, *first2)
shall be writable to result.
rotate_copy
Add to description: *first shall be writable to result.
merge
Add to description: *first1 and *first2 shall be writable to result..
set_union, set_intersection, set_difference, set_symmetric_difference
Add to description: *first1 and *first2 shall be writable to result.
partial_sum
acc is not defined.
acc, a variable of InputIterator's value type, shall be constructible
adjacent_difference
acc is not defined.
acc, a variable of InputIterator's value type, shall be MoveAssignable and shall be
constructible from the type of *first.
iota
iota is mis-specified. Since the expression we need to support is *first = value: *first
is required to be of type InputIterator::reference, and value is an lvalue of type T. The current
specification allows calling iota with a const output iterator!
T shall be convertible to ForwardIterator's value typevalue shall be writable to
first. The expression *first = value shall not modify value.
[2017-07 Toronto Monday issue prioritization]
Priority 3; Marshall to work with Gaspar to improve wording.
Proposed resolution:
inplace_merge exact comparison count complexity prohibits useful real-world optimizationsSection: 26.8.6 [alg.merge] Status: LEWG Submitter: Billy Robert O'Neal III Opened: 2017-06-08 Last modified: 2025-10-21
Priority: 4
View all other issues in [alg.merge].
View all issues with LEWG status.
Discussion:
At the moment, inplace_merge requires exactly N - 1 comparisons, if enough additional memory is
available (and in practice enough additional memory is always available). However, this prohibits implementing the
merge operation using forms of binary search, as in Timsort's
'Galloping Mode', a useful optimization
for non-uniform input data. It's not really useful to prohibit standard libraries from trying a few extra speculative
compares like this, given that users must be prepared for the fallback "not enough memory"
𝒪(N lg N) algorithm.
[2017-07 Toronto Monday issue prioritization]
Status to LEWG
[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
This wording is relative to N4659.
Edit 26.8.6 [alg.merge] as indicated:
template<class BidirectionalIterator> void inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last); template<class ExecutionPolicy, class BidirectionalIterator> void inplace_merge(ExecutionPolicy&& exec, BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last); template<class BidirectionalIterator, class Compare> void inplace_merge(BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last, Compare comp); template<class ExecutionPolicy, class BidirectionalIterator, class Compare> void inplace_merge(ExecutionPolicy&& exec, BidirectionalIterator first, BidirectionalIterator middle, BidirectionalIterator last, Compare comp);[…]
-8- Complexity: LetN = last - first:
(8.1) — For the overloads with no
ExecutionPolicy, if enough additional memory is available,exactlyN - 1comparisons on average, 𝒪(N) comparisons in the worst case.(8.2) — For the overloads with no
ExecutionPolicyif no additional memory is available, 𝒪(N log N) comparisons.(8.3) — For the overloads with an
ExecutionPolicy, 𝒪(N log N) comparisons.-9- Remarks: Stable (16.4.6.8 [algorithm.stable]).
money_put::do_put underspecifiedSection: 28.3.4.7.3.3 [locale.money.put.virtuals] Status: New Submitter: Jonathan Wakely Opened: 2017-06-21 Last modified: 2017-06-27
Priority: 3
View all other issues in [locale.money.put.virtuals].
View all issues with New status.
Discussion:
Whether you get ".99" or "0.99" for the following depends on the implementation:
std::cout.imbue(std::locale("en_US"));
std::cout << std::put_money(99.L);
I don't see any justification in [locale.money.put.virtuals] for the leading 0, although that seems more useful.
[2017-06-27, Jonathan comments and provides wording]
I suggest that we require a leading zero. The wording below is similar
to how C specifies the %f format specifier for fprintf.
Proposed resolution:
This wording is relative to N4659.
Edit 28.3.4.7.3.3 [locale.money.put.virtuals] as indicated:
iter_type do_put(iter_type s, bool intl, ios_base& str, char_type fill, long double units) const; iter_type do_put(iter_type s, bool intl, ios_base& str, char_type fill, const string_type& digits) const;[…]
-2- Remarks: The currency symbol is generated if and only if(str.flags() & str.showbase)is nonzero. If the format specifies a decimal point, at least one digit character appears before it. If the number of characters generated for the specified format is less than the value returned bystr.width()on entry to the function, then copies offillare inserted as necessary to pad to the specified width. For the valueafequal to(str.flags() & str.adjustfield), if(af == str.internal)istrue, the fill characters are placed wherenoneorspaceappears in the formatting pattern; otherwise if(af == str.left)istrue, they are placed after the other characters; otherwise, they are placed before the other characters.
put_money(99) is unnecessarily undefinedSection: 31.7.8 [ext.manip] Status: New Submitter: Jonathan Wakely Opened: 2017-06-22 Last modified: 2017-06-26
Priority: 3
View all other issues in [ext.manip].
View all issues with New status.
Discussion:
[ext.manip] p5 says:
Requires: The type
moneyTshall be eitherlong doubleor a specialization of thebasic_stringtemplate (Clause 24).
This means that put_money(99), put_money(99.), put_money("99"), and
put_money(string_view{"99"}) are all undefined, when in practice they will compile fine and
do the right thing, converting the argument to long double or std::string as needed.
long double or a std::basic_string, but that will unnecessarily
break code that works fine today. We should accept types convertible to long double or the relevant
money_put facet's string_type (which is not known until we attempt to write the unspecified
type to an ostream).
The requirement is also insufficient, because cout << put_money(wstring(L"99")) won't compile on any
implementation, despite the argument type being a specialization of basic_string. This same problem exists
for std::get_money.
[2017-06-24, Daniel comments and provides wording]
The wording changes below are supposed to support all moneyT types that are convertible to either
long double or to money_put/get<Ch, o/istreambuf_iterator<Ch, Tr>>::string_type
(but not to both), where Ch and Tr are determined by the concrete instantiated specialization of the
exposition-only function template f that is used to specify the semantics of put_money and
get_money, respectively. XOR-ing the requirements outlaws types that are convertible to both, which
would cause an ambiguity unless we would provide wording that would introduce an ordered application of these
convertibility constraints. This is the rationale for the seemingly odd new Remarks formulation. Note also,
that the wording provided below intentionally attempts to distinguish between the statically testable
conditions based on the is_convertible_v expressions within the Remarks: element and the well-defined
runtime behaviour requirement of the actually provided argument of deduced type moneyT within the
pre-existing Requires: element. Another point worth pointing out is that the wording attempts to fix an
currently existing ambiguity of the meaning of the type moneyT (and to a lesser extend for charT and
traits) as either the template parameter of put/get_money or that of the corresponding template
argument of the exposition-only f templates. The revised form makes it clearer that it refers to the latter.
put_money(99),
put_money(99.), and put_money("99"), but not yet for put_money(string_view{"99"}),
because string_view is not convertible to string. To realize support for the latter, this wording
approach could be extended by referring to is_constructible instead of is_convertible, though.
Proposed resolution:
This wording is relative to N4659.
Edit 31.7.8 [ext.manip] as indicated:
template <class moneyT> unspecified get_money(moneyT& mon, bool intl = false);-?- For an expression
-2- Requires:in >> get_money(mon, intl)described below, letMo,Ch, andTrbe the deduced template argument types of the template parametersmoneyT,charT, andtraits, respectively, of the instantiated specialization of the templatef.The typemoneyTshall be eitherlong doubleor a specialization of thebasic_stringtemplate (Clause 27 [strings])Moshall be either convertible tolong doubleor shall be convertible tomoney_get<Ch, istreambuf_iterator<Ch, Tr>>::string_type. -?- Remarks: Ifis_convertible_v<Mo, long double> == is_convertible_v<Mo, money_get<Ch, istreambuf_iterator<Ch, Tr>>::string_type>, the program is ill-formed. -3- Effects: The expressionin >> get_money(mon, intl)described below behaves as a formatted input function (31.7.5.3.1 [istream.formatted.reqmts]). -4- Returns: An object of unspecified type such that ifinis an object of typebasic_istream<then the expressioncharTCh,traitsTr>in >> get_money(mon, intl)behaves as if it calledf(in, mon, intl), where the functionfis defined as:template <class charT, class traits, class moneyT> void f(basic_ios<charT, traits>& str, moneyT& mon, bool intl) { using Iter = istreambuf_iterator<charT, traits>; using MoneyGet = money_get<charT, Iter>; ios_base::iostate err = ios_base::goodbit; const MoneyGet& mg = use_facet<MoneyGet>(str.getloc()); mg.get(Iter(str.rdbuf()), Iter(), intl, str, err, mon); if (ios_base::goodbit != err) str.setstate(err); }The expression
in >> get_money(mon, intl)shall have typebasic_istream<and valuecharTCh,traitsTr>&in.template <class moneyT> unspecified put_money(const moneyT& mon, bool intl = false);-?- For an expression
-5- Requires:out << put_money(mon, intl)described below, letMo,Ch, andTrbe the deduced template argument types of the template parametersmoneyT,charT, andtraits, respectively, of the instantiated specialization of the templatef.The typemoneyTshall be eitherlong doubleor a specialization of thebasic_stringtemplate (Clause 27 [strings])Moshall be either convertible tolong doubleor shall be convertible tomoney_put<Ch, ostreambuf_iterator<Ch, Tr>>::string_type. -?- Remarks: Ifis_convertible_v<Mo, long double> == is_convertible_v<Mo, money_put<Ch, ostreambuf_iterator<Ch, Tr>>::string_type>, the program is ill-formed. -6- Returns: An object of unspecified type such that ifoutis an object of typebasic_ostream<then the expressioncharTCh,traitsTr>out << put_money(mon, intl)behaves as a formatted output function (31.7.6.3.1 [ostream.formatted.reqmts]) that callsf(out, mon, intl), where the functionfis defined as:template <class charT, class traits, class moneyT> void f(basic_ios<charT, traits>& str, const moneyT& mon, bool intl) { using Iter = ostreambuf_iterator<charT, traits>; using MoneyPut = money_put<charT, Iter>; const MoneyPut& mp = use_facet<MoneyPut>(str.getloc()); const Iter end = mp.put(Iter(str.rdbuf()), intl, str, str.fill(), mon); if (end.failed()) str.setstate(ios::badbit); }The expression
out << put_money(mon, intl)shall have typebasic_ostream<and valuecharTCh,traitsTr>&out.
regex FSM is underspecifiedSection: 28.6.12 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2017-06-25 Last modified: 2017-07-12
Priority: 4
View other active issues in [re.grammar].
View all other issues in [re.grammar].
View all issues with New status.
Discussion:
In N4660 subclause 31.13 [re.grammar] paragraph 5:
The productions
ClassAtomExClass,ClassAtomCollatingElementandClassAtomEquivalenceprovide functionality equivalent to that of the same features in regular expressions in POSIX.
The broadness of the above statement makes it sound like it is merely a statement of intent; however, this appears to
be a necessary normative statement insofar as identifying the general semantics to be associated with the syntactic
forms identified. In any case, if it is meant for ClassAtomCollatingElement to provide functionality equivalent
to a collating symbol in a POSIX bracket expression, multi-character collating elements need to be considered.
The behavior of the internal finite state machine representation when used to match a sequence of characters is as described in ECMA-262. The behavior is modified according to any
match_flag_typeflags specified when using the regular expression object in one of the regular expression algorithms. The behavior is also localized by interaction with the traits class template parameter as follows: [bullets 14.1 to 14.4]
In none of the bullets does the wording handle multi-character collating elements in a clear manner:
14.1 deals in characters.
14.2 deals in characters (traits_inst.translate accepts only a single character).
14.3 might handle a multi-character collating element; however, there is no specification of how such a collating element is to be identified from the sequence of characters. Additionally, the definition of primary equivalence class specifies that it is a set of characters (not of collating elements).
14.4 deals in characters.
The ECMA-262 specification for ClassRanges also deals in characters.
[2017-07 Toronto Monday issue prioritization]
Priority 4
Proposed resolution:
traits_inst.lookup_collatename and the regex FSM is underspecified with
regards to ClassAtomCollatingElementSection: 28.6.12 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2017-06-25 Last modified: 2017-07-12
Priority: 3
View other active issues in [re.grammar].
View all other issues in [re.grammar].
View all issues with New status.
Discussion:
For a user to implement a regular expression traits class meaningfully, the relationship between the return value of traits_inst.lookup_collatename to the behaviour of the finite state machine corresponding to a regular expression
needs to be better specified.
traits_inst.lookup_collatename
only feeds clearly into two operations:
a test if the returned string is empty ([re.grammar]/8), and
a test if the result of traits_inst.transform_primary, with the returned string,
is empty ([re.grammar]/10).
Note: It is unclear if bullet 14.3 in [re.grammar]/14 refers to the result of traits_inst.lookup_collatename when
it refers to a "collating element"; and if it does, it is unclear what input is to be used.
traits_inst.lookup_collatename substitutes another member of the
equivalence class as its output.
For example, when processing "[[.AA.]]" as a pattern under a locale da_DK.utf8, what is the expected
behaviour difference (if any) should traits_inst.lookup_collatename return, for "AA", "\u00C5"
(where U+00C5 is A with ring, which sorts the same as "AA")?
[2017-07 Toronto Monday issue prioritization]
Priority 3
Proposed resolution:
optional::value_type is not always a value typeSection: 22.5.3 [optional.optional] Status: Open Submitter: Casey Carter Opened: 2017-06-27 Last modified: 2018-01-28
Priority: 3
View all other issues in [optional.optional].
View all issues with Open status.
Discussion:
optional<T>::value_type is T, which can be a cv-qualified object type. This is
inconsistent with the uses of the name value_type elsewhere in the standard. We should either require
optional<T>::value_type to be remove_cv_t<T> — a true value type —
or rename the type alias to element_type.
[2017-07 Toronto Tuesday PM issue prioritization]
Priority 3; may also affect array
[2018-1-26 issues processing telecon]
Status to 'Open'
Proposed resolution:
This wording is relative to N4659.
Edit 22.5.3 [optional.optional], class template optional synopsis, as indicated:
template <class T>
class optional {
public:
using value_type = remove_cv_t<T>;
[…]
};
variant copy constructor missing noexcept(see below)Section: 22.6.3.2 [variant.ctor] Status: Open Submitter: Peter Dimov Opened: 2017-06-27 Last modified: 2025-10-20
Priority: Not Prioritized
View other active issues in [variant.ctor].
View all other issues in [variant.ctor].
View all issues with Open status.
Discussion:
The copy constructor of std::variant is not conditionally noexcept (I think
it was in the original proposal.)
constexpr variant() noexcept(see below); variant(variant&&) noexcept(see below); template <class T> constexpr variant(T&&) noexcept(see below);
and second, variant itself makes use of is_nothrow_copy_constructible, so
it's inconsistent for it to take a stance against it.
[2017-07 Toronto Tuesday PM issue prioritization]
Status to LEWG
[Wrocław 2024-11-18; LEWG approves the direction]
In P0088R1 the copy constructor was conditionally noexcept in the synopsis, but not the detailed description. This was pointed out during LWG review in Jacksonville. The approved paper, P008R3, doesn't have it in either place.
Previous resolution [SUPERSEDED]:
This wording is relative to N4659.
Edit 22.6.3 [variant.variant], class template
variantsynopsis, as indicated:template <class... Types> class variant { public: // 23.7.3.1, constructors constexpr variant() noexcept(see below); variant(const variant&) noexcept(see below); variant(variant&&) noexcept(see below); […] };Edit 22.6.3.2 [variant.ctor] as indicated:
variant(const variant& w) noexcept(see below);[…]
-8- Remarks: This function shall not participate in overload resolution unlessis_copy_constructible_v<Ti>istruefor alli. The expression insidenoexceptis equivalent to the logical AND ofis_nothrow_copy_constructible_v<Ti>for alli.
[2025-10-20; Jonathan provides updated wording]
Proposed resolution:
This wording is relative to P5014.
Edit 22.6.3 [variant.variant], class template variant synopsis, as indicated:
template <class... Types>
class variant {
public:
// 23.7.3.1, constructors
constexpr variant() noexcept(see below);
variant(const variant&) noexcept(see below);
variant(variant&&) noexcept(see below);
[…]
};
Edit 22.6.3.2 [variant.ctor] as indicated:
variant(const variant& w) noexcept(see below);[…]
-8- Remarks: This function is defined as deleted unlessis_copy_constructible_v<Ti>istruefor alli. The exception specification is equivalent to the logical and ofis_nothrow_copy_constructible_v<Ti>for alli.
assert(E) inconsistent with CSection: 19.3 [assertions] Status: Open Submitter: Jonathan Wakely Opened: 2017-08-18 Last modified: 2018-08-20
Priority: 2
View all other issues in [assertions].
View all issues with Open status.
Discussion:
The C standard says that the expression in an assert must have a scalar type, and implies (or at least allows)
that the condition is tested by comparison to zero. C++ says that the expression is a constant subexpression if it can
be contextually converted to bool. Those ways to test the condition are not equivalent.
#include <stdlib.h>
// A toy implementation of assert:
#define assert(E) (void)(((E) != 0) || (abort(), 0))
struct X {
constexpr explicit operator bool() const { return true; }
};
constexpr bool f(const X& x) {
assert(x);
return true;
}
C++ says that assert(x) is a constant subexpression, but as it doesn't have scalar type it's not even a valid expression.
bool"
is the right condition, or if we should use comparison to zero instead.
[2017-11 Albuquerque Wednesday night issues processing]
Priority set to 2; status to Open
Jonathan is discussing this with WG14
[2018-08-20, Jonathan comments]
This was reported to WG14 as N2207.
Proposed resolution:
error_category" [syserr.errcat.derived] unclear and contains mistakesSection: 19.5.3.4 [syserr.errcat.derived] Status: New Submitter: Thomas Köppe Opened: 2017-09-20 Last modified: 2017-11-09
Priority: 3
View all issues with New status.
Discussion:
The presentation of section [syserr.errcat.derived] is currently somewhat problematic:
It is not clear why this section exists and what it is specifying. Presumably, it is the subject of the phrase "in this subclause" of the sibling section [syserr.errcat.overview], but that's confusing (because this would require the interpretation of "this subclause" as the containing superclause). It would be an improvement to say "shall behave as specified in [syserr.errcat.derived]" in the introduction.
The current wording of [syserr.errcat.derived] requires that derived classes keep the name member function pure-virtual,
making it impossible to have non-abstract derived classes. This appears to be an editorial error. Surely name should
just not be required to be pure-virtual.
There seems to be no requirement concerning the message virtual member function.
We should use override rather than virtual.
I would welcome a short, introductory paragraph in [syserr.errcat.derived] that states (non-redundantly) that the following are requirements on users' derived classes.
Partial wording proposal:
In 19.5.3.1 [syserr.errcat.overview] p1, change:
-1- The class
error_categoryserves as a base class for types used to identify the source and encoding of a particular category of error code. Classes may be derived fromerror_categoryto support categories of errors in addition to those defined in this International Standard. Such classes shall behave as specified inthis subclause19.5.3.4 [syserr.errcat.derived]. [Note:error_categoryobjects are passed by reference, and two such objects are equal if they have the same address. This means that applications using customerror_categorytypes should create a single object of each such type. — end note]
In 19.5.3.4 [syserr.errcat.derived], change:
virtualconst char* name() const noexcept override= 0;-1- Returns: A string naming the error category.
virtualerror_condition default_error_condition(int ev) const noexcept override;-2- Returns: An object of type
error_conditionthat corresponds toev.virtualbool equivalent(int code, const error_condition& condition) const noexcept override;-3- Returns:
trueif, for the category of error represented by*this,codeis considered equivalent tocondition; otherwise,false.virtualbool equivalent(const error_code& code, int condition) const noexcept override;-4- Returns:
trueif, for the category of error represented by*this,codeis considered equivalent to condition; otherwise,false.
[2017-11 Albuquerque Wednesday night issues processing]
Priority set to 3.
Jonathan to talk to Chris K and Walter about writing a paper describing the use of error_code, error_condition and defining your own.
Proposed resolution:
ConstBufferSequenceSection: 16.2.2 [networking.ts::buffer.reqmts.constbuffersequence] Status: New Submitter: Vinnie Falco Opened: 2017-09-20 Last modified: 2020-09-06
Priority: 3
View all issues with New status.
Discussion:
Addresses: networking.ts
The post-condition buffer sequence requirements mandate pointer equivalence. This means that a copies of buffer sequences must
point to the same pieces of underlying memory. While this is appropriate for MutableBufferSequence, it is unnecessary
for ConstBufferSequence and can actually prevent useful implementation strategies such as the following constant buffer
sequence which avoids dynamic allocations:
/// A buffer sequence containing a chunk-encoding header
class chunk_size
{
public:
// Storage for the longest hex string we might need
class value_type
{
friend class chunk_size;
// First byte holds the length
char buf_[1 + 2 * sizeof(std::size_t)];
template<class = void>
void prepare(std::size_t n);
template<class OutIter>
static OutIter to_hex(OutIter last, std::size_t n)
{
if (n == 0)
{
*--last = '0';
return last;
}
while (n)
{
*--last = "0123456789abcdef"[n & 0xf];
n >>= 4;
}
return last;
}
public:
operator boost::asio::const_buffer() const
{
return {
buf_ + sizeof(buf_) - buf_[0],
static_cast(buf_[0])
};
}
};
using const_iterator = value_type const*;
chunk_size(chunk_size const& other) = default;
/** Construct a chunk header
@param n The number of octets in this chunk.
*/
chunk_size(std::size_t n)
{
value_.prepare(n);
}
const_iterator begin() const
{
return &value_;
}
const_iterator end() const
{
return begin() + 1;
}
private:
value_type value_;
};
Proposed resolution:
This wording is relative to N4588.
Modify 16.2.2 [networking.ts::buffer.reqmts.constbuffersequence] Table 13 "ConstBufferSequence requirements" as indicated:
Table 13 — ConstBufferSequencerequirementsexpression return type assertion/note
pre/post-condition[…]X u(x);post:
equal( net::buffer_sequence_begin(x), net::buffer_sequence_end(x), net::buffer_sequence_begin(u), net::buffer_sequence_end(u), [](const typename X::value_type& v1, const typename X::value_type& v2) { const_buffer b1(v1); const_buffer b2(v2);return b1.data() == b2.data() && b1.size() == b2.size()return b1.size() == b2.size() && memcmp(b1.data(), b2.data(), b1.size()) == 0; })
DynamicBuffer prepare exception specificationSection: 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer] Status: New Submitter: Vinnie Falco Opened: 2017-10-16 Last modified: 2020-09-06
Priority: 3
View other active issues in [networking.ts::buffer.reqmts.dynamicbuffer].
View all other issues in [networking.ts::buffer.reqmts.dynamicbuffer].
View all issues with New status.
Discussion:
Addresses: networking.ts
The current wording for the DynamicBuffer prepare
member function implies that std::length_error is the
only allowable thrown exception. This should be changed to reflect that
any exception may be thrown, with std::length_error thrown
in particular when size() + n exceeds max_size().
[2017-11-08]
Priority set to 3 after five votes on mailing list
Proposed resolution:
This wording is relative to N4588.
Change 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer], Table 14 "DynamicBuffer requirements", as indicated:
Table 14 — DynamicBufferrequirementsexpression return type assertion/note pre/post-condition […]x.prepare(n)X::mutable_buffers_typeReturns a mutable buffer sequence u
representing the writable bytes, and where
buffer_size(u) == n. The dynamic buffer
reallocates memory as required. All constant or
mutable buffer sequences previously obtained using
data()orprepare()are invalidated.
Throws:length_errorifsize() + n
exceedsmax_size()or any other exception
if the request cannot otherwise be satisfied.
pop_heap over-constrains inputSection: 26.8.8.3 [pop.heap] Status: Open Submitter: Mathias Stearn Opened: 2017-11-04 Last modified: 2020-09-06
Priority: 3
View all issues with Open status.
Discussion:
The spec for <algorithms> pop_heap includes
-1- Requires: The range
[first, last)shall be a valid non-empty heap.
This has the unfortunate consequence that to pop a value and push a new value is substantially less efficient than necessary.
The popped value must be extracted by pop_heap (using up to 2 log N compares and swaps), and then, in
push_heap, the new value must be inserted (for up to N compares and swaps, but more usually something
like log N).
-1- Requires: The range
[first, last - 1)shall be a valid heap.
enables use of pop_heap in an integrated push-and-pop operation, with less than half the number of expected compare
and swap operations. Furthermore, if, as is often the case, the newly pushed value would have ended up at position first,
the push/pop operation could complete in time 𝒪(1), instead of (3 log N).
pop_heap offers no hint
that this usage is not already allowed. This change would bless such reliance as formally permitted.
After this change, minor extensions to std::priority_queue would enable it to take advantage of the newly efficient operation,
perhaps:
void pop_push(const Type&); void pop_push(Type&&); template <class... Args> void pop_emplace(Args&&... args);
These will appear in a formal proposal if the resolution is accepted.
[2017-11 Albuquerque Wednesday night issues processing]
Priority set to 3
[2017-11 Albuquerque Saturday issues processing]
status to Open; Marshall to review
Proposed resolution:
This wording is relative to N4700.
Change 26.8.8.3 [pop.heap] as indicated:
template<class RandomAccessIterator> void pop_heap(RandomAccessIterator first, RandomAccessIterator last); template<class RandomAccessIterator, class Compare> void pop_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);-1- Requires: The range
[first, last - 1)shall be a validnon-emptyheap.RandomAccessIteratorshall satisfy the requirements ofValueSwappable(16.4.4.3 [swappable.requirements]). The type of*firstshall satisfy the requirements ofMoveConstructible(Table 23) and ofMoveAssignable(Table 25).
max_size() for an allocatorSection: 16.4.4.6 [allocator.requirements] Status: New Submitter: Jon Cohen Opened: 2017-12-06 Last modified: 2025-10-18
Priority: 3
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with New status.
Discussion:
Table 31 in the C++17 standard specifies X::max_size() (where X is an allocator type) as "The largest value
that can meaningfully be passed to X::allocate()". Noticeably missing is the statement "Throws: Nothing".
vector::max_size() and allocator_traits::max_size() are
both marked noexcept. We must then interpret max_size() as being allowed to sometimes call
std::terminate, or else {vector, allocator_traits, ...}::max_size() must be allowed to directly calculate
numeric_limits<size_type>::max() / sizeof(value_type) instead of querying the allocator, even if
Alloc::max_size() exists. This seems like a bug in the wording for the requirements of max_size() in an
allocator type. I think an issue should be opened on this subject to add Throws: Nothing or similar to the requirements
of max_size() for an allocator.
As an example consider writing up a framework to test the exception-safety of types in a given framework, since they were all
written in an exception-free environment. One of the types in the framework is an allocator which, in a controlled way,
can throw an exception at any point where it is allowed by the standard. It's important that the test framework be as pedantic
as possible, so the allocator type throws on max_size(), since it is currently allowed to by the standard. When a
reasonable vector implementation (at least those in libstdc++ and msvc) is, for example, asked to construct a
vector from an initializer_list, it will call allocator_traits<Alloc>::max_size(), which will
terminate the program because the exception thrown in Alloc::max_size() propagated through the noexcept
traits function. Although this is conformant behavior, I think it's a bug in the standard that a function as benign as
max_size() can terminate the program in this manner, and I think the fix is that a conformant allocator should be
required to supply a non-throwing max_size() member function.
Daniel:
This problem was shortly discussed during review of LWG 2162(i) (see comment 2012-08-05). At that time
the more drastic but also more consistent requirement that an allocator's max_size function shall not throw
exceptions has not been added. IMO this position should be reconsidered to follow the spirit of the new issue LWG
3044(i).
[2018-01; Priority set to 3 after mailing list discussion]
[2018-08-21, Jonathan comments and provides wording]
The phrase "the largest value that can meaningfully be passed to X::allocate()" is meaningless. Is it a
requirement on the caller, so that larger values must not be passed? Or a hint from the allocator implementor that larger
values will produce a bad_alloc exception? Can the return value change dynamically, based on the free memory
available to the allocator?! — LWG 197(i) says it can't change.
const object
(so allocator_traits will not use the allocator's max_size() if it's non-const, although that was
unclear before DR 2284(i)). In addition to adding "Throws: nothing" we should ensure it's callable
on const lvalues, and clarify what "meaningfully" means and who is supposed to care about it. My proposed
resolution doesn't achieve all of this, but is a start.
Previous resolution [SUPERSEDED]:
This wording is relative to N4762.
Change 16.4.4.6 [allocator.requirements], Table 32 — "Descriptive variable definitions", as indicated:
Table 32 — Descriptive variable definitions Variable Definition T, U, Cany cv-unqualified object type (3.9) …a, a1, a2lvalues of type Xa3an lvalue of type const X…Change 16.4.4.6 [allocator.requirements], Table 33 — "Cpp17Allocator requirements", as indicated:
Table 33 — Cpp17Allocator requirements Expression Return type Assertion/note
pre-/post-conditionDefault …a3.max_size()X::size_typethe largest value that can
meaningfully be passed to
X::allocate().
[Note: Larger values might cause
an exception to be thrown. — end note]
Throws: Nothing.numeric_limits<size_type>::max()
/ sizeof(value_type)…
[2022-04-25; Daniel rebases wording on N4910]
Previous resolution [SUPERSEDED]:
This wording is relative to N4910.
Change 16.4.4.6.1 [allocator.requirements.general] as indicated:
-2- In subclause 16.4.4.6 [allocator.requirements],
(2.1) — […]
[…]
(2.6) —
a,a1,a2denote lvalues of typeX,(?.?) —
a3denotes an lvalue of typeconst X,[…]
[…]
a3.max_size()-50- Result:
-51- Returns: The largest value that can meaningfully be passed toX::size_typeX::allocate(). [Note: Larger values might cause an exception to be thrown. — end note] -?- Throws: Nothing. -52- Remarks: Default:numeric_limits<size_type>::max() / sizeof(value_type)
[2025-10-10; Jonathan provides improved wording]
[2025-10-18; Jonathan removes the note from the wording]
After reflector discussion, I've remove the following note from the proposed resolution: [Note: Larger values can cause an exception to be thrown. — end note]
I also suggested an alternative:
[Note:
Containers and other users of allocators can use this function to avoid
making calls to X::allocate which are unlikely ever to succeed.
— end note]
Those notes will be recorded here, but are not part of the proposed resolution.
Proposed resolution:
This wording is relative to N5014.
Change 16.4.4.6.1 [allocator.requirements.general] as indicated:
-2- In subclause 16.4.4.6 [allocator.requirements],
(2.1) — […]
[…]
(2.6) —
a,a1,a2denote lvalues of typeX,(?.?) —
a3denotes an lvalue of type (possibly const)X,[…]
[…]
a3.max_size()-50- Result:
-51- Returns: The largest value that can meaningfully be passed toX::size_typeX::allocate(). -?- Throws: Nothing. -52- Remarks: Default:numeric_limits<size_type>::max() / sizeof(value_type)
reference_wrapper to support non-referenceable function typesSection: 22.10.6 [refwrap] Status: New Submitter: Jonathan Wakely Opened: 2017-12-14 Last modified: 2020-09-06
Priority: 3
View all other issues in [refwrap].
View all issues with New status.
Discussion:
[refwrap] says that reference_wrapper<T> is a "wrapper around a reference to an object or function of type T"
but this doesn't actually constrain it, and doesn't forbid non-referenceable function types like int() const.
reference_wrapper<int() const> but implementations are required to provide partial
specializations for functions with cv-qualifiers and ref-qualifiers in order to define a nested result_type.
It should be undefined to instantiate reference_wrapper<T> with a non-referenceable type, or with a reference type
(since references to references are not possible). Making it undefined (rather than ill-formed or unspecified) means implementations
are not required to diagnose such invalid specializations, but also don't have to go to the effort of supporting weak result types etc.
[2018-01; Priority set to 3 after mailing list discussion]
Previous resolution [SUPERSEDED]:
This wording is relative to N4713.
Modify 22.10.6 [refwrap] as indicated:
-1-
-2-reference_wrapper<T>is aCopyConstructibleandCopyAssignablewrapper around a reference to an object or function of typeT.Tshall be a referenceable type (3.45 [defns.referenceable]) that is not a reference type.reference_wrapper<T>shall be a trivially copyable type (6.9 [basic.types]).
[2019-03-15; Daniel comments and provides revised wording]
The current wording is now far behind the working draft and a synchronization is therefore recommended. In particular, with the
acceptance of P0357R1, the specification of reference_wrapper has no longer any
weak result type. Second, I would like to concur with a remark
from Tomasz to change the wording to replace the undefined behavior by an ill-formed program instead, because every
attempt to instantiate the definition of reference_wrapper will instantiate its member declarations, and this would
cause the program to become ill-formed anyway because of the illegal formation of references to non-referenceable function
types for member functions such as T& get() const noexcept.
reference_wrapper is instantiated, because in the absence of a constrained template parameter
we shouldn't require implementations to diagnose even forming the name of a reference_wrapper specialization such
as in the following example:
using X = reference_wrapper<int() const>;
The wording below does not take advantage of a Mandates: element to prevent a dependency on LWG 3193(i) and because such an element is rarely used to specify class templates. If the committee wishes to use such an element, the equivalent wording would be:
Mandates:
reference_wrapperis instantiated with a referenceable type (3.45 [defns.referenceable]) as the argument for the template parameterT.
Proposed resolution:
This wording is relative to N4800.
Modify 22.10.6 [refwrap] as indicated:
-1-
-2-reference_wrapper<T>is a Cpp17CopyConstructible and Cpp17CopyAssignable wrapper around a reference to an object or function of typeT. Ifreference_wrapperis instantiated with a non-referenceable type (3.45 [defns.referenceable]) as the argument for the template parameterT, the program is ill-formed.reference_wrapper<T>is a trivially copyable type (6.9 [basic.types]). -3- The template parameterTofreference_wrappermay be an incomplete type.
atomic compound assignment operators can cause undefined behavior when corresponding
fetch_meow members don'tSection: 32.5.8.3 [atomics.types.int], 32.5.8.5 [atomics.types.pointer], 32.5.8.6 [atomics.types.memop] Status: New Submitter: Tim Song Opened: 2017-12-15 Last modified: 2020-09-06
Priority: 3
View all issues with New status.
Discussion:
Given atomic<int> meow{INT_MAX};, meow.fetch_add(1) has well-defined behavior because 32.5.8.3 [atomics.types.int] p7 says that
butRemarks: For signed integer types, arithmetic is defined to use two's complement representation. There are no undefined results.
meow += 1 and ++meow have undefined behavior, because these operator functions are defined (by, respectively,
32.5.8.3 [atomics.types.int] p8 and 32.5.8.6 [atomics.types.memop]) to be equivalent to return fetch_add(1) + 1;,
and so the addition of 1 to the result of fetch_add — which causes an integer overflow in this case — occurs
outside the protection of fetch_add magic. Additionally, the return value might differ from what fetch_add actually
wrote since that addition isn't required to use two's complement. This seems like a trap for the unwary. Is it intended?
A similar issue affects the atomic<T*> partial specialization for pointers.
[2018-01; Priority set to 3 after mailing list discussion]
[2019-04-15; JF Bastien comments and provides wording]
As discussed by LWG during the San Diego 2018 meeting, Jens removed LWG 3047 from "P1236R1: Alternative Wording for P 0907R4 Signed Integers are Two's Complement".
Proposed resolution:
This wording is relative to N4810.
Modify 32.5.7.3 [atomics.ref.int] as indicated:
integral operator op=(integral operand) const noexcept;-7- Effects: Equivalent to:
return static_cast<integral>(static_cast<make_unsigned_t<integral>>(fetch_key(operand)) op static_cast<make_unsigned_t<integral>>(operand));
Modify 32.5.7.6 [atomics.ref.memop] as indicated:
T* operator++() const noexcept;-3- Effects: Equivalent to:
return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_add(1)) + static_cast<make_unsigned_t<T>>(1));T* operator--(int) const noexcept;-4- Effects: Equivalent to:
return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_sub(1)) - static_cast<make_unsigned_t<T>>(1));
Modify 32.5.8.3 [atomics.types.int] as indicated:
T operator op=(T operand) volatile noexcept; T operator op=(T operand) noexcept;-8- Effects: Equivalent to:
return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_key(operand)) op static_cast<make_unsigned_t<T>>(operand));
[Drafting note:
[Drafting note:atomic<integral>'s working foroperator++/operator--is shared withatomic<T*>. — end drafting note]atomic<floating-point>seems to be correct, LWG should confirm that it is. — end drafting note]
Modify 32.5.8.5 [atomics.types.pointer] as indicated:
T* operator op=(ptrdiff_t operand) volatile noexcept; T* operator op=(ptrdiff_t operand) noexcept;-8- Effects: Equivalent to:
Remarks: The result may be an undefined address, but the operations otherwise have no undefined behavior.return reinterpret_cast<T*>(reinterpret_cast<ptrdiff_t>(fetch_key(operand)) op operand);
Modify 32.5.8.6 [atomics.types.memop] as indicated:
T operator++() volatile noexcept; T operator++() noexcept;-3- Effects: Equivalent to:
return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_add(1)) + static_cast<make_unsigned_t<T>>(1));T operator--() volatile noexcept; T operator--() noexcept;-4- Effects: Equivalent to:
return static_cast<T>(static_cast<make_unsigned_t<T>>(fetch_sub(1)) - static_cast<make_unsigned_t<T>>(1));
[Drafting note: Alternatively, LWG may want to separate the integral overload of
operator++/operator--from that ofatomic<T*>. end drafting note]
Section: 26.2 [algorithms.requirements] Status: Open Submitter: Jared Hoberock Opened: 2017-12-04 Last modified: 2022-04-25
Priority: 3
View other active issues in [algorithms.requirements].
View all other issues in [algorithms.requirements].
View all issues with Open status.
Discussion:
When designing the parallel algorithms library, we intended for parallel algorithms to copy their function objects parameters when it is possible and useful to do so, but there doesn't appear to be any wording to enable that latitude. To the contrary, algorithm specifications refer to their function object parameters by name, implying that a copy of the parameter may not be used as a substitute.
This was noticed when Billy O'Neal observed that parallelgenerate() did not share parallel for_each() and
for_each_n()'s special requirement for a CopyConstructible user-provided function object.
This CopyConstructible Function requirement was added to relax legacy for_each()'s MoveConstructible Function
requirement to allow parallel implementations to make copies as necessary. All parallel algorithms need similar permissions,
but a strong requirement for CopyConstructible in all algorithms is too restrictive.
What we require is to allow algorithm implementations to use copies of function objects as substitutes for their original parameters,
while not requiring that all function object parameters be copyable.
Casey Carter noted that 26.2 [algorithms.requirements] p8 grants permission to all algorithms to copy their function
object parameters. However, this paragraph is not normative and does not indicate how the algorithm is allowed to use such copies.
Additionally, it does not specify which algorithm parameters are the ones called out as function objects. For example,
26.7.7 [alg.generate] refers to gen as a function object, but 26.6.5 [alg.foreach] does not refer to f
as a function object. All the other types of callable algorithm parameters (i.e. Predicate, BinaryPredicate,
Compare, UnaryOperation, BinaryOperation, BinaryOperation1, and BinaryOperation2)
are defined to be function objects in 26.2 [algorithms.requirements] and 26.3.2 [algorithms.parallel.user]. This
list intentionally omits Function and Generator by design.
A potential resolution would introduce normative wording to explicitly allow algorithms to use copies of function object parameters
as substitutes for their function object parameters, and remove ambiguity in algorithm specifications about which parameters are
function objects.
[2018-01; Priority set to 3 after mailing list discussion]
[2018-3-14 Wednesday evening issues processing; move to Open]
We thought that the notes in [alg.foreach]/1 and /11 should be unwrapped as well. Bryce to work with Jared on updated wording.
Previous resolution [SUPERSEDED]:
This wording is relative to N4713.
Modify 26.2 [algorithms.requirements] as indicated:
-8-
[Note:Unless otherwise specified, algorithms that take function objects as arguments are permitted to copy those function objects freely. When an algorithm's specification requires the invocation of a function object parameter, such a copy may be invoked as a substitute for the original function object parameter. [Note: This implies that copyable user-supplied function objects should not rely on their identity. Programmers for whom object identity is important should consider using a wrapper class that points to a noncopied implementation object such asreference_wrapper<T>(22.10.6 [refwrap]), or some equivalent solution. — end note]Modify 26.6.5 [alg.foreach] as indicated:
template<class InputIterator, class Function> constexpr Function for_each(InputIterator first, InputIterator last, Function f);[…]
-2- Effects: Applies the function objectfto the result of dereferencing every iterator in the range[first, last), […] […]template<class ExecutionPolicy, class ForwardIterator, class Function> void for_each(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last, Function f);-7- Effects: Applies the function object
-6- Requires:Functionshall meet the requirements ofCopyConstructible.fto the result of dereferencing every iterator in the range[first, last). […] […]template<class InputIterator, class Size, class Function> constexpr InputIterator for_each_n(InputIterator first, Size n, Function f);[…]
-13- Effects: Applies the function objectfto the result of dereferencing every iterator in the range[first, first + n)in order. […] […]template<class ExecutionPolicy, class ForwardIterator, class Size, class Function> ForwardIterator for_each_n(ExecutionPolicy&& exec, ForwardIterator first, Size n, Function f);[…] -18- Effects: Applies the function object
-16- Requires:Functionshall meet the requirements ofCopyConstructible.fto the result of dereferencing every iterator in the range[first, first + n).[…] […]
[2022-04-25; Daniel rebases wording on N4910]
The previously refactored note term "can" in 26.2 [algorithms.requirements] p10 has been reverted to "permitted" to specify a normative implementation freedom.
[2022-04-25; Daniel comments]
Bryce and Jared have unassigned from this issue.
Proposed resolution:
This wording is relative to N4910.
Modify 26.2 [algorithms.requirements] as indicated:
-10-
[Note 2:Unless otherwise specified, algorithms that take function objects as argumentscanare permitted to copy those function objects freely. When an algorithm's specification requires the invocation of a function object parameter, such a copy may be invoked as a substitute for the original function object parameter. [Note: This implies that copyable user-supplied function objects should not rely on their identity. If object identity is important, a wrapper class that points to a noncopied implementation object such asreference_wrapper<T>(22.10.6 [refwrap]), or some equivalent solution, can be used. — end note]
Modify 26.6.5 [alg.foreach] as indicated:
template<class InputIterator, class Function> constexpr Function for_each(InputIterator first, InputIterator last, Function f);[…]
-2- Effects: Applies the function objectfto the result of dereferencing every iterator in the range[first, last), […] […]template<class ExecutionPolicy, class ForwardIterator, class Function> void for_each(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last, Function f);-7- Effects: Applies the function object
-6- Preconditions:Functionmeets the Cpp17CopyConstructible requirements.fto the result of dereferencing every iterator in the range[first, last). […] […]template<class InputIterator, class Size, class Function> constexpr InputIterator for_each_n(InputIterator first, Size n, Function f);[…]
-18- Effects: Applies the function objectfto the result of dereferencing every iterator in the range[first, first + n)in order. […] […]template<class ExecutionPolicy, class ForwardIterator, class Size, class Function> ForwardIterator for_each_n(ExecutionPolicy&& exec, ForwardIterator first, Size n, Function f);[…]
-22- Preconditions:n >= 0istrue.-23- Effects: Applies the function objectFunctionmeets theCpp17CopyConstructiblerequirements.fto the result of dereferencing every iterator in the range[first, first + n).[…] […]
error_code construction from rvalues of error_categorySection: 19.5.4.1 [syserr.errcode.overview] Status: New Submitter: Antony Polukhin Opened: 2018-01-24 Last modified: 2020-09-06
Priority: 3
View all other issues in [syserr.errcode.overview].
View all issues with New status.
Discussion:
Constructor error_code(int val, const error_category& cat) and
member function void assign(int val, const error_category& cat)
could be misused if a custom error_category is provided:
error_code ec{1, test_category{}}; // ec holds a pointer/reference to a temporary
[2018-06-18 after reflector discussion]
Priority set to 3
Proposed resolution:
This wording is relative to N4713.
Modify 19.5.4.1 [syserr.errcode.overview] as indicated:
namespace std {
class error_code {
public:
// 19.5.4.2 [syserr.errcode.constructors], constructors
[…]
error_code(int val, const error_category& cat) noexcept;
error_code(int val, const error_category&& cat) = delete;
[…]
// 19.5.4.3 [syserr.errcode.modifiers], modifiers
void assign(int val, const error_category& cat) noexcept;
void assign(int val, const error_category&& cat) = delete;
[…]
};
[…]
}
copy_file() copies which attributes?Section: 31.12.13.5 [fs.op.copy.file] Status: New Submitter: Davis Herring Opened: 2018-01-26 Last modified: 2020-09-06
Priority: 3
View all other issues in [fs.op.copy.file].
View all issues with New status.
Discussion:
(To resolve C++17 CD comment Late 25.)
It is not stated which attributes are copied by copy_file().
[2018-1-26 issues processing telecon]
Priority 3
Proposed resolution:
This wording is relative to N4713.
Modify 31.12.13.5 [fs.op.copy.file] as indicated:
Rationale:
The attributes specified are the useful subset of the attributes that can be queried in C++17. Existing practice is complicated: POSIX "cp -p" attempts to preserve user/group IDs, for instance, but cannot in general do so, andsetuid/setgidpermissions may be stripped.
bool copy_file(const path& from, const path& to, copy_options options); bool copy_file(const path& from, const path& to, copy_options options, error_code& ec) noexcept;[…]
[…]
[…]
(4.2) — Otherwise, copy the contents, permissions, and data modification time
and attributesof the filefromresolves to, to the filetoresolves to, if:[…]
(4.2.3) —
(options & copy_options::update_existing) != copy_options::noneandfromis more recent thanto, determined as if by use of thelast_write_timefunction (31.12.13.26 [fs.op.last.write.time]).Other implementation-defined attributes may be copied. Failure (or partial failure) to copy attributes is not an error.
[…]
copy_options handlingSection: 31.12.13.4 [fs.op.copy] Status: Open Submitter: Davis Herring Opened: 2018-01-29 Last modified: 2020-09-06
Priority: 2
View all other issues in [fs.op.copy].
View all issues with Open status.
Discussion:
(The resolution of #3 resolves part of C++17 NB comment US 36.)
The handling of several options forfilesystem::copy() is wrong:
Single-level directory copying is silently suppressed by any flag other than
copy_options::recursive (even copy_options::directories_only). Single-level
directory copying operates via using some unspecified flag to trigger this misfeature.
copy_options::create_symlinks and copy_options::skip_symlinks affect
the interpretation of the destination name; the latter shouldn't ever, and the former should
affect only broken symlinks (since it would want to replace them).
The copy_options groups for existing target files and the form of copying are
consulted only for creating regular files.
copy("file", "dir") creates dir/file, but copy("symlink", "dir",
copy_options::copy_symlinks) fails.
If a symlink is encountered with copy_options::copy_symlinks and
copy_options::create_symlinks, the latter flag is ignored (but its otherwise sensible
restriction to absolute paths applies) rather than the former.
copy_options::create_symlinks without copy_options::copy_symlinks
fails if it encounters a symlink. (This is particularly damaging for recursive operation.)
This issue, since it replaces so much text, also addresses two error-handling concerns in passing:
The significance of equivalent(from, to) failing is unspecified. (Ignoring such
failures entirely would make dangerous those operations that replace the target with a link.)
Copying a directory involves several operations. When an error_code is being used,
the process continues past errors and (because successful functions call ec.clear())
may suppress them.
This expands on the resolution for LWG 2681(i).
This also addresses the same issue as LWG 2682(i), but has a different result (based on the fact that the Example successfully copies directories to new, non-existent names).[2018-06; Rapperswil Wednesday evening, discussing LWG 2682(i)]
JW: can we use the words we are shipping already since two years?
BO: what we got is better than what we had before
no objection to moving to Ready
ACTION move to Ready
ACTION link LWG 2682(i) and LWG 3057 and set a priority 2 and look at 3057 in San Diego
[2018-11 San Diego Thursday night issue processing]
Need to gather implementation experience; revisit in Kona. Status to Open.
[2018-11-13; Billy O'Neal comments]
I (Billy O'Neal) prefer Davis' solution to LWG 3057, as I think the wording follows the meaning of the individual enum values more closely, and enables more scenarios to function correctly instead of reporting such cases as errors.
However, I don't want to adopt that wording as is because it requires my implementation to detect errors in places that force us to do a bunch of extra system calls, and I don't believe those specific ways error handling happens is relevant to what the copy API wants to do. Ideally, the wording would be structured such that it said "here's a list of error conditions, if they happen we aren't going to tell you when exactly they are detected" and then listed the behavior irrespective of when errors happen. That way implementations can do the error checks when it makes sense according to what their system APIs report. For example, anything that requires symlink resolution is very expensive on my platform so I'd want to be able to defer anything related to status (rather thansymlink_status) to after I've detected that
there's actually a symlink (or junction) involved.
Proposed resolution:
This wording is relative to N4750.
Modify Table 115 — "Enum class copy_options" as indicated:
Option group controlling copyandcopy_filefunction effects for existing target filesConstant Meaning […] […]
Modify 31.12.13.4 [fs.op.copy] as indicated:
Rationale:
POSIX.1-2008 allows the implementation to create hard links "to" symbolic links, and provideslinkat()to choose between the symlink and its target. 31.12.13.4 [fs.op.copy]/4.9 is redundant given 31.12.5 [fs.err.report]/3.1.
void copy(const path& from, const path& to, copy_options options); void copy(const path& from, const path& to, copy_options options, error_code& ec) noexcept;-3- Requires: At most one element from each option group (31.12.8.3 [fs.enum.copy.opts]) is set in
-4- Effects:options.Before the first use offandt:If each is needed below,
(4.1) — If […]
[…]
(4.10) — Otherwise, no effects.auto linkf = (options & (copy_options::copy_symlinks | copy_options::skip_symlinks)) != copy_options::none; auto f = linkf ? symlink_status(from) : status(from), t = status(to); auto to2 = !is_directory(f) && is_directory(t) ? to/from.filename() : to. bool linkt = (options & (copy_options::create_symlinks | copy_options::create_hard_links)) != copy_options::none || is_symlink(f); auto t2 = linkt ? symlink_status(to2) : status(to2);Effects are then as follows:[Drafting note:
copy_options::create_symlinksis intentionally omitted for linkf; it may simply have been a typo forcopy_options::copy_symlinks(which was added by LWG 2681(i)) since at least N3940.]-5- Throws: As specified in 31.12.5 [fs.err.report]. -6- Remarks: For the signature with argument
(?.?) — If
f.type()ort.type()is an implementation-defined file type [fs.enum.file_type], then the effects are implementation-defined.[Drafting note: the text between the previous drafting note and this one is the only unchanged text under /4.]
(?.?) — Otherwise, if
exists(f)isfalse, report an error as specified in 31.12.5 [fs.err.report].(?.?) — Otherwise, do nothing if
(?.?.?) —
(options & copy_options::directories_only) != copy_options::noneandis_directory(f)isfalse, or(?.?.?) —
(options & copy_options::skip_symlinks) != copy_options::noneandis_symlink(f)istrue, or(?.?.?) —
(options & copy_options::skip_existing) != copy_options::noneandexists(t2)istrue.(?.?) — Otherwise, report an error as specified in 31.12.5 [fs.err.report] if:
(?.?.?) —
is_other(f) || is_other(t2)istrue, or(?.?.?) —
exists(t2) && exists(from) == exists(to2) && equivalent(from, to)istrue.(?.?) — Otherwise, if
is_directory(f)istrue, then:
(?.?.?) —
create_directory(to, from).(?.?.?) — If
(options & copy_options::recursive) != copy_options::noneor if(options & copy_options::directories_only) == copy_options::none, iterate over the files infrom, as if byfor (const directory_entry& x : directory_iterator(from)) if ((options & copy_options::recursive) != copy_options::none || !is_directory(linkf ? symlink_status(x.path()) : status(x.path()))) copy(x.path(), to/x.path().filename(), options);(?.?) — Otherwise, do nothing if
(options & copy_options::update_existing) != copy_options::none, exists(to2)istrue, andfromis not more recent thanto2, determined as if by use of thelast_write_timefunction ( [fs.op.last_write_time]).(?.?) — Otherwise, report an error as specified in 31.12.5 [fs.err.report] if:
(?.?.?) —
is_directory(t2)istrue, or(?.?.?) —
(options & (copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::noneandexists(t2)istrue.(?.?) — Otherwise, if
linktistrue, then:
(?.?.?) —
remove(to2)if an existingto2would prevent the following link creation.(?.?.?) — If
(options & copy_options::create_symlinks) != copy_options::none,create_symlink(from, to2). [Note: Iffromis a symbolic link, it is not followed. — end note](?.?.?) — Otherwise, if
(options & copy_options::create_hard_links) != copy_options::none, then create a hard link tofrom, iflinkfistrue, or else to the file thatfromresolves to. [Note: Not all file systems that support hard links and symbolic links support creating hard links to symbolic links. — end note](?.?.?) — Otherwise,
copy_symlink(from, to2).(?.?) — Otherwise,
copy_file(from, to2, options).ec, any library functions called by the implementation shall have anerror_codeargument if applicable. If any such function fails,copyreturns immediately without (further) modifyingec.
Section: 23.2 [container.requirements] Status: New Submitter: Richard Smith Opened: 2018-02-05 Last modified: 2018-02-14
Priority: 3
View all other issues in [container.requirements].
View all issues with New status.
Discussion:
What are the requirements for
a = b;
... where a and b are of map-like associative container type (map,
multimap, unordered_map, unordered_multimap)?
r = a // Postconditions: r == a
(Incidentally, earlier in the table, there is a clear error: the general container requirements permit
"a = rv" for assignment from an rvalue, but "a" here is a potentially-const container.
Oops.) Oddly. there are no requirements at all on T here.
a = t // Requires: T is CopyInsertable into X and CopyAssignable.
... where T is the container's value_type, that is, pair<const key_type,
mapped_type>. Note that such a pair is not CopyAssignable for "normal" key types
that disallow assignment to const objects. They also add:
a = rv // Requires: if !POCMA, T is MoveInsertable into X and MoveAssignable.
... which has the same problem in the !POCMA case.
The associative container requirements and unordered associative container requirements have a similar problem for assignment from an initializer list:a = il // Requires: value_type is CopyInsertable into X and CopyAssignable.
Presumably these assignments are intended to actually work, but what are the intended constraints?
Do we wish to allow implementations to perform node reuse for these map-like containers?
Presumably yes, and if so, the key_type portion of the node must be assigned as well as the
value_type portion (for instance, with whatever implementation technique is used to power
node_handle) as we cannot assume that key equivalence (or, for unordered_*map,
even key equality) implies substitutability.
a = t", "a = rv", and "a = il" for the map-like containers; specifically:
for "a = t" and "a = il", we should require that value_type is
CopyInsertable into X, and key_type and mapped_type are
CopyAssignable
for "a = rv", if !POCMA, we should require that value_type is
MoveInsertable into X and key_type and mapped_type are
MoveAssignable
(And we should fix the general container requirements to constrain "r = rv", not "a = rv".)
a = rv" problematic is already handled by LWG 3028(i).
[2018-02-13, Priority set to 3 after mailing list discussion]
Proposed resolution:
XXX_scan algorithms are specified to work with move-only T, but are
specified to make N copies of T into the destination rangeSection: 26.10.8 [exclusive.scan], 26.10.9 [inclusive.scan], 26.10.10 [transform.exclusive.scan], 26.10.11 [transform.inclusive.scan] Status: New Submitter: Billy O'Neal III Opened: 2018-02-06 Last modified: 2019-01-20
Priority: 2
View all other issues in [exclusive.scan].
View all issues with New status.
Discussion:
All of the scan algorithms ([exclusive.scan], [inclusive.scan], [transform.exclusive.scan], [transform.inclusive.scan]) have language like "If init is provided, T shall be
MoveConstructible (Table 23); otherwise, ForwardIterator1's value type shall be
MoveConstructible.". However, the algorithms operational semantics require that that type
be written "by copy" to the destination range, making support for move only types impossible.
GENERALIZED_SUM and GENERALIZED_NONCOMMUTATIVE_SUM
need to specify the type used to store intermediate calculations.
[2019-01-20 Reflector prioritization]
Set Priority to 2
Proposed resolution:
<memory> are underspecifiedSection: 26.11 [specialized.algorithms] Status: New Submitter: Alisdair Meredith Opened: 2018-02-12 Last modified: 2020-09-06
Priority: 3
View other active issues in [specialized.algorithms].
View all other issues in [specialized.algorithms].
View all issues with New status.
Discussion:
The parallel forms of the uninitialized memory algorithms in <memory> are
underspecified in two ways. First, they missed the change that all parallel
algorithms require at least Forward Iterators, even for input ranges. See
P0467R2 for more details.
[2018-02-20, Priority set to 3 after mailing list discussion]
Proposed resolution:
Section: 29.7.6 [sf.cmath] Status: New Submitter: Casey Carter Opened: 2018-02-17 Last modified: 2018-06-19
Priority: 3
View all issues with New status.
Discussion:
29.7.6 [sf.cmath]/1 uses the phrase "report a domain error" (emphasis mine):
If any argument value to any of the functions specified in this subclause is a NaN (Not a Number), the function shall return a NaN but it shall not report a domain error. Otherwise, the function shall report a domain error for just those argument values for which:
the function description's Returns: clause explicitly specifies a domain and those argument values fall outside the specified domain, or
the corresponding mathematical function value has a nonzero imaginary component, or
the corresponding mathematical function is not mathematically defined.
The behavior this phrase is attempting to convey is unclear. A quick search through the text of the
standard for "domain error" finds only the domain_error exception type defined in
19.2.4 [domain.error]. Is the intent of "report a domain error" that the special math functions
throw an exception of type domain_error, or is it that they behave as specified in C11
7.12.1 "Treatment of error conditions" para 2 which defines the term "domain error"?
[2018-06-18 after reflector discussion]
Priority set to 3
Proposed resolution:
variant's subobject corrupts dataSection: 22.6.3.4 [variant.assign] Status: New Submitter: Antony Polukhin Opened: 2018-02-20 Last modified: 2020-09-06
Priority: 3
View other active issues in [variant.assign].
View all other issues in [variant.assign].
View all issues with New status.
Discussion:
variant::emplace functions in 22.6.3.5 [variant.mod] destroy the currently
contained value before initializing it to a new value. Assignments in
22.6.3.4 [variant.assign] are described in terms on emplace.
variant into the same
variant corrupts data:
#include <variant>
#include <memory>
#include <iostream>
using str_t = std::string;
using str_ptr_t = std::unique_ptr<str_t>;
using var_t = std::variant<str_t, str_ptr_t>;
int main()
{
var_t v = str_ptr_t{
new str_t{"Long string that does not fit into SS buffer and forces dynamic allocation"}
};
// Any of the following lines corrupt the variant's content:
v = *std::get<str_ptr_t>(v);
//v = std::move(*std::get<str_ptr_t>(v));
std::cout << std::get<str_t>(v) << std::endl; // SEGV - 'str_t' inside 'v' is invalid
}
Such behavior confuses users, especially those users who are used to
boost::variant's behavior. Consider making variant assignments safer
by defining them close to copy-and-swap.
[2018-06-18 after reflector discussion]
Priority set to 3; Antony volunteered to write a paper for Rapperswil.
Proposed resolution:
This wording is relative to N4727.
Change 22.6.3.4 [variant.assign] as indicated:
variant& operator=(const variant& rhs);-1- Let
-2- Effects:jberhs.index().[…]
(2.1) — If neither
*thisnorrhsholds a value, there is no effect.(2.2) — Otherwise, if
*thisholds a value butrhsdoes not, destroys the value contained in*thisand sets*thisto not hold a value.(2.3) — Otherwise, if
index() == j, assigns the value contained inrhsto the value contained in*this.
(2.4) — Otherwise, if eitheris_nothrow_copy_constructible_v<Tj>istrueoris_nothrow_move_constructible_v<Tj>isfalse, equivalent toemplace<j>(get<j>(rhs)).(2.5) — Otherwise, equivalent to
emplace<j>(Tj{get<j>(rhs)}).operator=(variant(rhs))variant& operator=(variant&& rhs) noexcept(see below);-6- Let
-7- Effects:jberhs.index().[…]
(7.1) — If neither
*thisnorrhsholds a value, there is no effect.(7.2) — Otherwise, if
*thisholds a value butrhsdoes not, destroys the value contained in*thisand sets*thisto not hold a value.(7.3) — Otherwise, if
index() == j, assignsget<j>(std::move(rhs))to the value contained in*this.(7.4) — Otherwise, equivalent to
emplace<j>(Tj{get<j>(std::move(rhs))}).-10- Let
-11- Effects:Tjbe a type that is determined as follows: build an imaginary functionFUN(Ti)for each alternative typeTi. The overloadFUN(Tj)selected by overload resolution for the expressionFUN(std::forward<T>(t))defines the alternativeTjwhich is the type of the contained value after assignment.[…]
(11.1) — If
*thisholds aTj, assignsstd::forward<T>(t)to the value contained in*this.
(11.2) — Otherwise, ifis_nothrow_constructible_v<Tj, T> || !is_nothrow_move_constructible_v<Tj>istrue, equivalent toemplace<j>(std::forward<T>(t)).(11.3) — Otherwise, equivalent to
emplace<j>(Tj{std::forward<T>(t)}).operator=(variant(std::forward<T>(t)))
DynamicBuffer object lifetimes underspecifiedSection: 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer], 17.6 [networking.ts::buffer.async.read], 17.8 [networking.ts::buffer.async.write], 17.10 [networking.ts::buffer.async.read.until] Status: New Submitter: Christopher Kohlhoff Opened: 2018-02-26 Last modified: 2020-09-06
Priority: 3
View other active issues in [networking.ts::buffer.reqmts.dynamicbuffer].
View all other issues in [networking.ts::buffer.reqmts.dynamicbuffer].
View all issues with New status.
Discussion:
Addresses: networking.tsThe DynamicBuffer overloads of async_read and async_write, and
async_read_until, are underspecified with respect to the lifetime of the dynamic
buffer argument b.
Asio's implementation (and the intended specification) performs DECAY_COPY(b)
in the async_read, async_write, and async_read_until
initiating functions. All operations performed on b are actually performed on that
decay-copy, or on a move-constructed descendant of it. The copy is intended to refer to the same
underlying storage and be otherwise interchangeable with the original in every way.
Most initiating functions' argument lifetimes are covered by [async.reqmts.async.lifetime]. As an rvalue reference it falls under the second bullet, which specifies that the object is copied (but doesn't say decay-copied).
The proposed resolution adds a postcondition for DynamicBuffer move construction, and
specifies that DECAY_COPY(b) be used for each of these functions. The following
two alternative resolutions may also be considered:
Add an extra bullet to [async.reqmts.async.lifetime] to cover rvalue parameters (but specifically exclude CompletionTokens).
Change the DynamicBuffer arguments to be by-value. (And also change the corresponding synchronous operations to be consistent.)
However, the proposed resolution below is presented as a change that minimizes the scope of the impact.
[2018-06-18 after reflector discussion]
Priority set to 3
Proposed resolution:
This wording is relative to N4711.
Edit 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer] as indicated:
-3- In Table 14,
xdenotes a value of typeX,x1denotes a (possibly const) value of typeX,andmx1denotes an xvalue of typeX,ndenotes a (possibly const) value of typesize_t, andudenotes an identifier.
Table 14 — DynamicBuffer requirements expression type assertion/note pre/post-conditions X u(mx1);post:
u.size()is equal to the prior value ofmx1.size().u.max_size()is equal to the prior value ofmx1.max_size().u.capacity()is equal to the prior value ofmx1.capacity().u.data()satisfies the ConstBufferSequence requirements (16.2.2 [buffer.reqmts.constbuffersequence]) as if copy constructed from the prior value ofmx1.data().- All valid const or mutable buffer sequences that were previously obtained using
mx1.data()ormx1.prepare()remain valid.
Edit 17.6 [networking.ts::buffer.async.read] as indicated:
-11- Let
bdbe the result ofDECAY_COPY(b). Data is placed into the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) object. A mutable buffer sequence (16.2.1 [buffer.reqmts.mutablebuffersequence]) is obtained prior to eachbbdread_somecall usingbd.prepare(N), whereNis an unspecified value less than or equal tobd.max_size() - bd.size(). [Note: Implementations are encouraged to usebd.capacity()when determiningN, to minimize the number ofread_somecalls performed on the stream. -- end note] After eachread_somecall, the implementation performsbd.commit(n), wherenis the return value fromread_some.[…]
-13- The synchronous read operation continues until:
bd.size() == bd.max_size(); orthe completion condition returns
0.
Edit 17.8 [networking.ts::buffer.async.write] as indicated:
-11- Let
bdbe the result ofDECAY_COPY(b). Data is written from the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) objectbd. A constant buffer sequence (16.2.2 [buffer.reqmts.constbuffersequence]) is obtained usingbd.data(). After the data has been written to the stream, the implementation performsbd.consume(n), wherenis the number of bytes successfully written.[…]
-13- The asynchronous write operation continues until:
bd.size() == 0; orthe completion condition returns
0.
Edit 17.10 [networking.ts::buffer.async.read.until] as indicated:
-3- Effects: Let
bdbe the result ofDECAY_COPY(b). Initiates an asynchronous operation to read data from the buffer-oriented asynchronous read stream (17.1.2 [buffer.stream.reqmts.asyncreadstream]) objectstreamby performing zero or more asynchronous read_some operations on the stream, until the readable bytes of the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) objectbdcontain the specified delimiterdelim.-4- Data is placed into the dynamic buffer object
bd. A mutable buffer sequence (16.2.1 [buffer.reqmts.mutablebuffersequence]) is obtained prior to eachasync_read_somecall usingbd.prepare(N), whereNis an unspecified value such thatN <= max_size() - size(). [Note: Implementations are encouraged to usebd.capacity()when determiningN, to minimize the number of asynchronous read_some operations performed on the stream. — end note] After the completion of each asynchronousread_someoperation, the implementation performsbd.commit(n), wherenis the value passed to the asynchronousread_someoperation's completion handler.-5- The asynchronous
read_untiloperation continues until:
the readable bytes of
bdcontain the delimiterdelim; or
bd.size() == bd.max_size(); oran asynchronous
read_someoperation fails.[…]
-8- On completion of the asynchronous operation, if the readable bytes of
bdcontain the delimiter,ecis set such that!ecistrue. Otherwise, ifbd.size() == bd.max_size(),ecis set such thatec == stream_errc::not_found. Ifbd.size() < bd.max_size(),ecis theerror_codefrom the most recent asynchronousread_someoperation.nis the number of readable bytes inbdup to and including the delimiter, if present, otherwise0.
async_)read and (async_)write
don't support DynamicBuffer lvaluesSection: 17 [networking.ts::buffer.stream] Status: New Submitter: Christopher Kohlhoff Opened: 2018-02-27 Last modified: 2020-09-06
Priority: 3
View all issues with New status.
Discussion:
Addresses: networking.tsSuppose that we have a user-defined class dyn_buf that satisfies the DynamicBuffer
requirements ([buffer.reqmts.dynamicbuffer]), and is additionally copy-constructible. The following snippet compiles, as expected:
dyn_buf b; net::read_until(my_socket, b, "\n");
However, this next snippet will not compile, when it should:
dyn_buf b; net::read(my_socket, b);
This is due to:
DynamicBuffer template parameter being deduced as dyn_buf&, andread overload being specified to not participate in overload resolution unless is_dynamic_buffer_v<DynamicBuffer> is true.This can fixed by changing the test to is_dynamic_buffer_v<decay_t<DynamicBuffer>>.
[2019-01-20 Reflector prioritization]
Set Priority to 3
Proposed resolution:
This wording is relative to N4711.
Edit 17.5 [networking.ts::buffer.read] as indicated:
-14- Remarks: This function shall not participate in overload resolution unless
is_dynamic_buffer_v<decay_t<DynamicBuffer>>istrue.
Edit 17.6 [networking.ts::buffer.async.read] as indicated:
-16- Remarks: This function shall not participate in overload resolution unless
is_dynamic_buffer_v<decay_t<DynamicBuffer>>istrue.
Edit 17.7 [networking.ts::buffer.write] as indicated:
-14- Remarks: This function shall not participate in overload resolution unless
is_dynamic_buffer_v<decay_t<DynamicBuffer>>istrue.
Edit 17.8 [networking.ts::buffer.async.write] as indicated:
-16- Remarks: This function shall not participate in overload resolution unless
is_dynamic_buffer_v<decay_t<DynamicBuffer>>istrue.
directory_entry, directory_iterator and recursive_directory_iterator perform needless path copiesSection: 31.12.10 [fs.class.directory.entry], 31.12.11 [fs.class.directory.iterator], 31.12.12 [fs.class.rec.dir.itr] Status: New Submitter: Gor Nishanov Opened: 2018-03-05 Last modified: 2019-04-02
Priority: 3
View all other issues in [fs.class.directory.entry].
View all issues with New status.
Discussion:
An implementation of directory_entry class is likely to store a filesystem::path as a member.
Constructors and assign member functions take filesystem::path by const& thus forcing
creation of a copy.
directory_iterator class is likely to store a directory_entry or a
path as a part of its state. Constructors take filesystem::path by const&
thus forcing creation of a copy.
An implementation of recursive_directory_iterator class is likely to store a directory_entry
or a path as a part of its state. Constructors take filesystem::path by const&
thus forcing creation of a copy.
Suggested resolution:
Add overloads to directory_entry, directory_iterator, and recursive_directory_iterator
that take filesystem::path by &&.
Make it unspecified in case an exception is thrown from those new members where an argument was moved from or not.
explicit directory_entry(const filesystem::path& p); explicit directory_entry(filesystem::path&& p); directory_entry(const filesystem::path& p, error_code& ec); directory_entry(filesystem::path&& p, error_code& ec); void directory_entry::assign(const filesystem::path& p); void directory_entry::assign(filesystem::path&& p); void directory_entry::assign(const filesystem::path& p, error_code& ec); void directory_entry::assign(filesystem::path&& p, error_code& ec); explicit directory_iterator(const path& p); explicit directory_iterator(path&& p); directory_iterator(const path& p, directory_options options); directory_iterator(path&& p, directory_options options); directory_iterator(const path& p, error_code& ec) noexcept; directory_iterator(path&& p, error_code& ec) noexcept; directory_iterator(const path& p, directory_options options, error_code& ec) noexcept; directory_iterator(path&& p, directory_options options, error_code& ec) noexcept; explicit recursive_directory_iterator(const path& p); explicit recursive_directory_iterator(path&& p); recursive_directory_iterator(const path& p, directory_options options); recursive_directory_iterator(path&& p, directory_options options); recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept; recursive_directory_iterator(path&& p, directory_options options, error_code& ec) noexcept; recursive_directory_iterator(const path& p, error_code& ec) noexcept; recursive_directory_iterator(path&& p, error_code& ec) noexcept;
[2018-03-20 Priority set to 3 after discussion on the reflector.]
Proposed resolution:
from_chars API does not distinguish between overflow and underflowSection: 28.2.3 [charconv.from.chars] Status: Open Submitter: Greg Falcon Opened: 2018-03-12 Last modified: 2023-03-29
Priority: 2
View other active issues in [charconv.from.chars].
View all other issues in [charconv.from.chars].
View all issues with Open status.
Discussion:
strtod() distinguishes between overflow and underflow by returning a value that is either
very large or very small. Floating point from_chars does not currently offer any way for
callers to distinguish these two cases.
strtod() to from_chars without
loss of functionality.
I recommend that floating point from_chars use value as an overflow-vs-underflow
reporting channel, in the same manner as strtod().
My proposed wording gives from_chars the same wide latitude that strtod() enjoys
for handling underflow. A high-quality implementation would likely set ec == result_out_of_range
for underflow only when the nearest representable float to the parsed value is a zero and
the parsed mantissa was nonzero. In this case value would be set to (an appropriately-signed) zero.
It is worth considering giving from_chars this more predictable behavior, if library writers
feel they can provide this guarantee for all platforms. (I have a proof-of-concept integer-based
implementation for IEEE doubles with this property.)
[2018-06 Rapperswil Wednesday issues processing]
Marshall to provide updated wording and propose Tentatively Ready on the reflector.
Priority set to 2
[2018-08-23 Batavia Issues processing]
Status to Open; Marshall to reword
[2023-03-29; Jonathan adds further discussion]
There are conflicting interpretations of "not in the range representable"
for floating-point types. One view is that 1e-10000 and 1e+10000 are outside
the representable range for a 64-bit double-precision double
(which has min/max exponents of -1022 and 1023). Another view is that the
representable range for floating-point types is [-inf,+inf], which means
that there are values that cannot be accurately represented,
but there are no values "not in the range representable". And 1e-10000 is
clearly within the range [0,numeric_limits<double>::max()],
even if we don't use infinity as the upper bound of the range.
Under the second interpretation, the result will be ±0.0 for underflow
and ±inf for overflow, but ec will not be set.
The current proposed resolution does address this, by making it clear
that value should be set to a very small or very large value
(with appropriate sign), but that ec should also be set.
The use of the word "overflow" for the integer overloads is a problem though,
because the result cannot "overflow" an unsigned integer type,
but can certainly be outside its range.
Proposed resolution:
This wording is relative to N4727.
Edit 28.2.3 [charconv.from.chars] as indicated:
[…] Otherwise, the characters matching the pattern are interpreted as a representation of a value of the type of
value. The memberptrof the return value points to the first character not matching the pattern, or has the valuelastif all characters match. If the parsed value is not in the range representable by the type ofvalue,the membervalueis unmodified andecof the return value is equal toerrc::result_out_of_range. Otherwise,valueis set to the parsed value, after rounding according toround_to_nearest(17.3.4 [round.style]), and the memberecis value-initialized.from_chars_result from_chars(const char* first, const char* last, see below& value, int base = 10);-2- Requires:
-3- Effects: The pattern is the expected form of the subject sequence in thebasehas a value between 2 and 36 (inclusive)."C"locale for the given nonzero base, as described forstrtol, except that no"0x"or"0X"prefix shall appear if the value ofbaseis 16, and except that'-'is the only sign that may appear, and only ifvaluehas a signed type. On overflow,valueis unmodified. […]from_chars_result from_chars(const char* first, const char* last, float& value, chars_format fmt = chars_format::general); from_chars_result from_chars(const char* first, const char* last, double& value, chars_format fmt = chars_format::general); from_chars_result from_chars(const char* first, const char* last, long double& value, chars_format fmt = chars_format::general);-6- Requires:
-7- Effects: The pattern is the expected form of the subject sequence in thefmthas the value of one of the enumerators ofchars_format."C"locale, as described forstrtod, except that
(7.1) […]
(7.2) […]
(7.3) […]
(7.4) […]
In any case, the resulting value is one of at most two floating-point values closest to the value of the string matching the pattern. On overflow,
[…]valueis set to plus or minusstd::numeric_limits<T>::max()of the appropriate type. On underflow,valueis set to a value with magnitude no greater thanstd::numeric_limits<T>::min().
from_chars specification regarding floating point rounding is inconsistentSection: 28.2.3 [charconv.from.chars] Status: Open Submitter: Greg Falcon Opened: 2018-03-12 Last modified: 2024-12-04
Priority: 2
View other active issues in [charconv.from.chars].
View all other issues in [charconv.from.chars].
View all issues with Open status.
Discussion:
P0682R1 added the requirement that from_chars use
round_to_nearest when converting from string, but later text in the section suggests that
the implementation has latitude in its choice of rounding logic.
from_chars behavior,
the rounding-mode text should be weakened. If the intent is to always require round_to_nearest,
the text suggesting a latitude in rounding logic should be removed.
[2018-03-27 Priority set to 2 after discussion on the reflector.]
[2018-06 Rapperswil Wednesday issues processing]
Status to open; also this needs to say that the intent is to be independent of the floating point environment.
[2018-08-23 Batavia Issues processing]
Marshall to talk to Jens about this
[2024-12-04; add comments from Richard Smith]
In editorial issue #6730 Richard said:
28.2.3 [charconv.from.chars]/6.4 says:In any case, the resulting value is one of at most two floating-point values closest to the value of the string matching the pattern.This is ambiguous. It could mean either:
- The resulting value is the implementation's choice of one of a set of values, and that set contains the two values closest to the value of the string.
- The resulting value is the implementation's choice of one of a set of values, and that set contains all values that are closest to the value of the string (of which it turns out there can be up to two).
I think the normal English interpretation would be (1), but the intended interpretation is actually (2).
(Under (1), the string
"1.0"can produce the value one ULP less than 1.0 or it can produce 1.0, and the string"1.00<lots of 0s>1"can produce those same two values, because the value one ULP less than 1.0 is closer to that string than the value one ULP greater than 1.0.)Perhaps the wording from 7.3.10 [conv.double]/2 and 7.6.1.9 [expr.static.cast]/11 can be used instead:
If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation-defined choice of either of those values.
Editorial pull request #6833 proposed a change along those lines, but conflicts with the proposed resolution to this issue. We should address Richard's comment as part of this issue.
Proposed resolution:
This wording is relative to N4727.
Edit 28.2.3 [charconv.from.chars] as indicated:
from_chars_result from_chars(const char* first, const char* last, float& value, chars_format fmt = chars_format::general); from_chars_result from_chars(const char* first, const char* last, double& value, chars_format fmt = chars_format::general); from_chars_result from_chars(const char* first, const char* last, long double& value, chars_format fmt = chars_format::general);-6- Requires:
-7- Effects: The pattern is the expected form of the subject sequence in thefmthas the value of one of the enumerators ofchars_format."C"locale, as described forstrtod, except that
(7.1) […]
(7.2) […]
(7.3) […]
(7.4) […]
In any case, the resulting value is
[…]one of at most twothe floating-point valuesclosest to the value of the string matching the pattern, with ties broken according toround_to_nearest.
Section: 17.5 [support.start.term], 17.9.5 [exception.terminate] Status: New Submitter: JF Bastien Opened: 2018-03-15 Last modified: 2024-07-26
Priority: 3
View other active issues in [support.start.term].
View all other issues in [support.start.term].
View all issues with New status.
Discussion:
It's unclear how different termination facilities in C++ interact (and how they interact with the C
termination facilities). Individually some of these functions try to handle corner cases, but hilarity
ensues when combined with each other. As a simple example, can an atexit handler call exit?
If not, can it call quick_exit, and can then at_quick_exit handler call exit?
Is it possible to install an atexit handler from an at_quick_exit, without strongly
happens before, while handling a separate atexit handler (and what happens then)?
returning from main calls atexit handlers.
atexit / exit
at_quick_exit / quick_exit
set_terminate
violating noexcept and other things that call std::terminate (see
[except.terminate])
violating exception specification
parallel algorithms leaving with uncaught exception
some std::signal such as SIGTERM, SIGSEGV, SIGINT,
SIGILL, SIGABRT, and (maybe?) SIGFPE.
set_unexpected (now a zombie)
unexpected_handler (now a zombie)
What's unclear is:
Is termination handling a DAG?
Which thread(s) are termination handlers called on?
Is program termination Turing complete?
I've written a sample program which exercises some of this, see here.
[2018-04-02, Jens comments]
Any potential wording should carefully take [basic.start] into account, and maybe should actually be integrated into the core wording, not the library wording.
[2018-04-02 Priority set to 3 after discussion on the reflector.]
[2024-07-26; Jonathan comments]
In C89 and C99 the spec for exit in C said
"If more than one call to the exit function is executed by a program,
the behavior is undefined."
Since C11 that was updated to also talk about at_quick_exit, saying
"If a program calls the exit function more than once,
or calls the quick_exit function in addition to the exit function,
the behavior is undefined." The spec for quick_exit is similar.
That answers most of the questions here. An atexit or at_quick_exit
handler cannot call exit or quick_exit, because if a handler is running
then it means that exit or quick_exit has already been called,
and calling either of them again would be undefined.
It doesn't matter whether an atexit handler installs an at_quick_exit
handler, because once exit handlers start running
it would be undefined to call quick_exit, and vice versa. So you should never
have a situation where both sets of handlers are running.
There is a suggestion
to relax this in POSIX so that calling exit or quick_exit again from other
threads would not be UB but would just block until the process exits,
which should happen eventually assuming exit handlers make forward progress
(calling exit or quick_exit from a handler would still be UB).
Why does C++ not make it undefined to call exit twice? Can we change that?
Proposed resolution:
Section: 17.6.3.2 [new.delete.single] Status: New Submitter: William M. Miller Opened: 2018-03-16 Last modified: 2020-09-06
Priority: 3
View other active issues in [new.delete.single].
View all other issues in [new.delete.single].
View all issues with New status.
Discussion:
In general requirements on a whole program, as opposed to a single translation unit, generally specify "no diagnostic required", since we don't want to require implementations to do multi-translation-unit analysis. However, 17.6.3.2 [new.delete.single] paragraph 11 says,
If a function with a
sizeparameter is defined, the program shall also define the corresponding version without thesizeparameter.
This is clearly not restricted to a single translation unit; should "no diagnostic required" be added?
[2018-04-03; Thomas Köppe and Tim Song suggest wording]
[2018-06-18 after reflector discussion]
Priority set to 3
Proposed resolution:
This wording is relative to N4727.
Edit 17.6.3.2 [new.delete.single] as indicated:
void operator delete(void* ptr) noexcept; void operator delete(void* ptr, std::size_t size) noexcept; void operator delete(void* ptr, std::align_val_t alignment) noexcept; void operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept;-10- Effects: […]
-11- Replaceable: A C++ program may define functions with any of these function signatures, and thereby displace the default versions defined by the C++ standard library. If a function without asizeparameter is defined, the program should also define the corresponding function with asizeparameter. If a function with asizeparameter is defined, the program shall also define the corresponding version without thesizeparameter; no diagnostic is required. [Note: The default behavior below may change in the future, which will require replacing both deallocation functions when replacing the allocation function. — end note]
Edit 17.6.3.3 [new.delete.array] as indicated:
void operator delete[](void* ptr) noexcept; void operator delete[](void* ptr, std::size_t size) noexcept; void operator delete[](void* ptr, std::align_val_t alignment) noexcept; void operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept;-9- Effects: […]
-10- Replaceable: A C++ program may define functions with any of these function signatures, and thereby displace the default versions defined by the C++ standard library. If a function without asizeparameter is defined, the program should also define the corresponding function with asizeparameter. If a function with asizeparameter is defined, the program shall also define the corresponding version without thesizeparameter; no diagnostic is required. [Note: The default behavior below may change in the future, which will require replacing both deallocation functions when replacing the allocation function. — end note]
copy_n should require non-overlapping rangesSection: 26.7.1 [alg.copy] Status: New Submitter: Marshall Clow Opened: 2018-03-21 Last modified: 2022-11-06
Priority: 3
View other active issues in [alg.copy].
View all other issues in [alg.copy].
View all issues with New status.
Discussion:
All the copy algorithms have some kind of prohibition on having the input and output ranges overlap.
The serial version ofcopy says:
Requires:
resultshall not be in the range[first, last).
The parallel version of copy says:
Requires: The ranges
[first, last)and[result, result + (last - first))shall not overlap.
copy_if says:
Requires: The ranges
[first, last)and[result, result + (last - first))shall not overlap.
copy_backwards says:
Requires:
resultshall not be in the range[first, last).
But copy_n has no such requirement.
What formulation do we want here? Is it sufficient to say "... shall not be in the range ..." or should we use the stronger "... shall not overlap ..."? Some copy variants use one, some use the other. Should we be consistent? Issue 3085(i) is a similar issue for char_traits::copy.
[2018-06-18 after reflector discussion]
Priority set to 3
Previous resolution [SUPERSEDED]:
This wording is relative to N4727.
Edit 26.7.1 [alg.copy] as indicated:
[Drafting note: I'm using the permission in 26.2 [algorithms.requirements]/10 to do random-access arithmetic on (possibly) input iterators.]
template<class InputIterator, class Size, class OutputIterator> constexpr OutputIterator copy_n(InputIterator first, Size n, OutputIterator result); template<class ExecutionPolicy, class ForwardIterator1, class Size, class ForwardIterator2> ForwardIterator2 copy_n(ExecutionPolicy&& exec, ForwardIterator1 first, Size n, ForwardIterator2 result);-?- Requires:
-9- Effects: For each non-negative integerresultshall not be in the range[first, first + n).i < n, performs*(result + i) = *(first + i). -10- Returns:result + n. -11- Complexity: Exactlynassignments.
[2022-11-06; Daniel syncs wording with recent working draft]
Proposed resolution:
This wording is relative to N4917.
Edit 26.7.1 [alg.copy] as indicated:
[Drafting note: I'm using the permission in 26.2 [algorithms.requirements]/10 to do random-access arithmetic on (possibly) input iterators.]
template<class InputIterator, class Size, class OutputIterator> constexpr OutputIterator copy_n(InputIterator first, Size n, OutputIterator result); template<class ExecutionPolicy, class ForwardIterator1, class Size, class ForwardIterator2> ForwardIterator2 copy_n(ExecutionPolicy&& exec, ForwardIterator1 first, Size n, ForwardIterator2 result); template<input_iterator I, weakly_incrementable O> requires indirectly_copyable<I, O> constexpr ranges::copy_n_result<I, O> ranges::copy_n(I first, iter_difference_t<I> n, O result);-10- Let
-11- Mandates: The typeNbe max(0, n).Sizeis convertible to an integral type (7.3.9, 11.4.8). -?- Preconditions:resultis not in the range[first, first + n). -12- Effects: For each non-negative integeri < N, performs*(result + i) = *(first + i). -13- Returns:
(13.1) —
result + Nfor the overloads in namespacestd.(13.2) —
{first + N, result + N}for the overload in namespaceranges.-14- Complexity: Exactly
Nassignments.
enum class bitmask typesSection: 16.3.3.3.3 [bitmask.types] Status: Open Submitter: Geoffrey Romer Opened: 2018-03-26 Last modified: 2020-09-06
Priority: 3
View other active issues in [bitmask.types].
View all other issues in [bitmask.types].
View all issues with Open status.
Discussion:
[bitmask.types] specifies the semantics of a bitmask type in terms of an "exposition only" enum definition,
together with some constants and overloads. Notably, it is depicted as an unscoped enum, which implies among other
things that it is implicitly convertible to int_type. At least some sources treat that as normative (as
of this writing, cppreference.com's documentation for
BitmaskType says the expression
(X & Y) != 0 is guaranteed to be well-formed), and it's hard to argue that they're wrong on the basis
of the existing wording.
[2018-04-23 Priority set to 2 after discussion on the reflector.]
[2018-08-23 Batavia Issues processing]
N3110 also touches on this.
Nico to survey the enums in the library and report back on which ones should be class.
[2019 Cologne Wednesday night]
Changing existing enums to class enums is an ABI break on some platforms; current wording does not require the use of enums. See N3110.
Daniel to provide requirements tables, Jonathan to assist. Reduce priority to 3
Proposed resolution:
This wording is relative to N4727.
Edit 16.3.3.3.3 [bitmask.types] as indicated:
-2- The bitmask type
bitmaskcan be written:// For exposition only. // int_type is an integral type capable of representing all values of the bitmask type.enumE bitmask : int_type { V0 = 1 << 0, V1 = 1 << 1, V2 = 1 << 2, V3 = 1 << 3, ..... }; […]-3- Here,
Emay represent eitherenumorenum class(the choice is implementation-defined unless otherwise specified), and the namesC0,C1, etc. represent bitmask elements for this particular bitmask type. The zero valuebitmask{}is used to represent an empty bitmask, in which no bitmask elements are set. Allsuchbitmask elements have distinct, nonzero values such that, for any pairCiandCjwherei ≠ j,Ci & Ciis nonzero andCi & Cjis zero.Additionally, the value0is used to represent an empty bitmask, in which no bitmask elements are set.
std::abs overloadSection: 29.7.2 [c.math.abs], 17.2.2 [cstdlib.syn], 31.13.2 [cinttypes.syn] Status: New Submitter: Richard Smith Opened: 2018-03-30 Last modified: 2018-06-19
Priority: 3
View all issues with New status.
Discussion:
LWG 2192(i) changed the library specification so that you always get all the std::abs overloads for fundamental
types if you get any of them.
<cinttypes> provides a std::abs (and std::div) overload for
intmax_t if it's not long long (since LWG 1449(i)). Presumably that one should also follow the
<cstdlib>/<cmath> pattern and we should make the complete abs overload set available
whenever any of the three headers is included? (And likewise we should make the complete std::div overload set
available whenever either <cstdlib> or <cinttypes> is included.)
[2018-06-18 after reflector discussion]
Priority set to 3
Proposed resolution:
strstreambuf refers to nonexistent member of fpos, fpos::offsetSection: 99 [depr.strstreambuf.virtuals] Status: New Submitter: Billy O'Neal III Opened: 2018-04-04 Last modified: 2020-09-06
Priority: 4
View all other issues in [depr.strstreambuf.virtuals].
View all issues with New status.
Discussion:
strstreambuf refers to a nonexistent member function of fpos in the specification of the member function
seekpos, 99 [depr.strstreambuf.virtuals]/18 (emphasize mine):
For a sequence to be positioned, if its next pointer is a null pointer, the positioning operation fails. Otherwise, the function determines
newofffromsp.offset():
The intent is clearly to get the corresponding streamoff from the fpos, as p19 says "the resultant
offset newoff (of type off_type)". The mechanism to make that conversion is a normal explicit conversion,
as indicated in the last row of the table in [fpos.operations].
[2018-06-18 after reflector discussion]
Priority set to 4
Proposed resolution:
This wording is relative to N4727.
Edit 99 [depr.strstreambuf.virtuals] as indicated:
pos_type seekpos(pos_type sp, ios_base::openmode which = ios_base::in | ios_base::out) override;-17- Effects: […]
-18- For a sequence to be positioned, if its next pointer is a null pointer, the positioning operation fails. Otherwise, the function determinesnewofffromstatic_cast<off_type>(sp): […].offset()
basic_stringbuf seekoff effects trigger undefined behavior and have contradictory returnsSection: 31.8.2.5 [stringbuf.virtuals] Status: New Submitter: Billy O'Neal III Opened: 2018-04-07 Last modified: 2020-09-06
Priority: 3
View other active issues in [stringbuf.virtuals].
View all other issues in [stringbuf.virtuals].
View all issues with New status.
Discussion:
Paragraph citations relative to N4727.
[stringbuf.virtuals]/10 says that newoff might be calculated from xnext - xbegin,
or from high_mark - xbegin. After newoff is calculated, it does the null pointer
check against and zero offset check. However, that means the effects may have already done
nullptr - non-nullptr, or non-nullptr - nullptr, which [expr.add]/5 says
is undefined behavior.
newoff, not the value actually used
which is newoff + off. For example, buf.seekoff(100, ios_base::beg, ios_base::out)
on a read-only streambuf would try to assign pptr() + newoff + off to pptr(), but
pptr() may have been nullptr, giving nullptr + 0 + 100 which triggers UB.
(Perhaps the "refers to an uninitialized character" bit protects that though).
Last, the Returns: element says that it returns newoff, but then also says it returns
the resulting stream position, which should be something like newoff + off. (I checked libc++
and MSVC++ and we both return newoff + off)
We probably want to resolve that by renaming the value that comes out of Table 108 to something like
"basis" and make "newoff" actually be the new offset instead of the starting offset.
[2018-04-16 Priority set to 3 after discussion on the reflector.]
Proposed resolution:
filesystem::path::filename()Section: 31.12.6.5.9 [fs.path.decompose] Status: New Submitter: Jonathan Wakely Opened: 2018-04-06 Last modified: 2020-09-06
Priority: 3
View all other issues in [fs.path.decompose].
View all issues with New status.
Discussion:
The example in [fs.path.decompose] p7 includes:
path("//host").filename(); // yields ""
This result isn't guaranteed, it depends whether the implementation interprets "//host" as a root-name
or as a root-directory (with a redundant directory-separator) followed by the filename "host".
Previous resolution [SUPERSEDED]:
This wording is relative to N4727.
Edit 31.12.6.5.9 [fs.path.decompose] as indicated:
path filename() const;-6- Returns:
[Example:relative_path().empty() ? path() : *--end().path("/foo/bar.txt").filename(); // yields "bar.txt" path("/foo/bar").filename(); // yields "bar" path("/foo/bar/").filename(); // yields "" path("/").filename(); // yields "" path("//host").filename(); // yields "" or "host" path(".").filename(); // yields "." path("..").filename(); // yields ".."— end example]
[2018-04-10, Jonathan comments and provides revised wording]
Based on the reflector discussion I'd like to change the P/R to Billy's suggestion of simply removing that line from the example.
[2018-06-18 after reflector discussion]
Priority set to 3
Proposed resolution:
This wording is relative to N4741.
Edit 31.12.6.5.9 [fs.path.decompose] as indicated:
path filename() const;-6- Returns:
[Example:relative_path().empty() ? path() : *--end().path("/foo/bar.txt").filename(); // yields "bar.txt" path("/foo/bar").filename(); // yields "bar" path("/foo/bar/").filename(); // yields "" path("/").filename(); // yields ""path("//host").filename(); // yields ""path(".").filename(); // yields "." path("..").filename(); // yields ".."— end example]
is_assignable<Incomplete&, Incomplete&>Section: 21.3.3 [meta.type.synop] Status: Open Submitter: Casey Carter Opened: 2018-04-10 Last modified: 2024-08-21
Priority: 2
View other active issues in [meta.type.synop].
View all other issues in [meta.type.synop].
View all issues with Open status.
Discussion:
LWG 2939(i) suggests that the the preconditions of the type traits need reevaluation.
This issue focuses specifically on is_assignable and, by extension, its variants:
is_copy_assignable<T>, equivalent to is_assignable<T&, const T&>,
is_move_assignable<T>, equivalent to is_assignable<T&, T>,
is_trivially_assignable<T, U>, equivalent to is_assignable<T, U> &&
/* magic */,
is_trivially_copy_assignable<T>, equivalent to is_assignable<T&, const T&> &&
/* magic */,
is_trivially_move_assignable<T>, equivalent to is_assignable<T&, T> &&
/* magic */,
is_nothrow_assignable<T, U>, equivalent to is_assignable<T, U> &&
noexcept(declval<T>() = declval<U>()),
is_nothrow_copy_assignable<T>, equivalent to is_assignable<T&, const T&> &&
noexcept(declval<T&>() = declval<const T&>()),
is_nothrow_move_assignable<T>, equivalent to is_assignable<T&, T> &&
noexcept(declval<T&>() = declval<T>()), and
We note a discrepancy: is_copy_assignable<T> requires T to be a complete type, but the
equivalent form is_assignable<T&, const T&> does not. The requirement for
is_copy_assignable<T> seems sensible, since there's no way to determine whether or not the assignment
declval<T&>() = declval<const T&>() is well-formed when T is incomplete.
It seems that the same argument should apply to all of the above "assignable" traits, and that they must require that
the referent type is complete when given a reference type parameter to be implementable.
[2018-08 Batavia Monday issue discussion]
Issues 2797(i), 2939(i), 3022(i), and 3099(i) are all closely related. Walter to write a paper resolving them.
[2020-02-14, Prague]
LWG discussions. Set priority to 2.
[2023-06-12; Varna]
P1285R0 is related to this issue.
Previous resolution [SUPERSEDED]:
This wording is relative to N4741.
- In 21.3.6.4 [meta.unary.prop] Table 42, change the Precondition text for
is_assignable,is_trivially_assignable, andis_nothrow_assignableas follows:remove_cvref_t<T>andremove_cvref_t<U>shall be complete types,cvvoid, or arrays of unknown bound.- In 21.3.6.4 [meta.unary.prop] Table 42, change the Precondition text for
is_copy_assignable,is_move_assignable,is_trivially_copy_assignable,is_trivially_move_assignable,is_nothrow_copy_assignable, andis_nothrow_move_assignableas follows:remove_cvref_t<T>shall be a complete type,cvvoid, or an array of unknown bound.
[2024-08-21; Jonathan provides improved wording]
During LWG telecon review Tomasz pointed out that we don't always require
a complete type for the right operand of an assignment. Given
T::operator=(U&)
we should be able to give a correct answer for
is_assignable<T&, U&>
whether of not U is complete.
This also affects e.g. is_constructible<T, U&>
if T::T(U&) exists.
So for the examples above,
remove_cvref_t<U>
is not needed to give a correct answer.
However, if T::operator=(U&) does not exist,
then we do need U to be complete so we can tell if there is
an implicit conversion sequence to T or another type that can be
assigned to T.
We do not know how to solve this problem, and it's broader than just
is_assignable.
It was suggested to make an incremental improvement to is_assignable
and open a new issue for the broader issue.
Proposed resolution:
This wording is relative to N4988.
- In 21.3.6.4 [meta.unary.prop] Table 51, change the Precondition text for
is_assignable,is_trivially_assignable, andis_nothrow_assignableas follows:remove_cvref_t<T>andUshall be complete types, cvvoid, or arrays of unknown bound.- In 21.3.6.4 [meta.unary.prop] Table 51, change the Precondition text for
is_copy_assignable,is_move_assignable,is_trivially_copy_assignable,is_trivially_move_assignable,is_nothrow_copy_assignable, andis_nothrow_move_assignableas follows:remove_cvref_t<T>shall be a complete type,cvvoid, or an array of unknown bound.
T1 is convertible to T2Section: 16 [library] Status: New Submitter: Jens Maurer Opened: 2018-04-24 Last modified: 2023-06-25
Priority: 3
View other active issues in [library].
View all other issues in [library].
View all issues with New status.
Discussion:
The library wording frequently uses the construction "type T1 is convertible to type T2",
but this is an undefined phrase.
T1
must satisfy the convertibility requirement, regardless of value category, or whether a single
value category is in view only.
Consider:
struct C
{
operator int() &&;
};
int main()
{
int x = C(); // prvalue can be implicitly converted to int
C c;
int y = c; // lvalue can't
}
The library has an "is_convertible<T1, T2>" trait, but that checks convertibility
only for a single value category, not all possible ones.
[2018-06-18 after reflector discussion]
Priority set to 3
[2023-06-24; Daniel comments]
This issue has very much overlap with LWG 484(i).
Proposed resolution:
istreambuf_iterator has public exposition-only memberSection: 24.6.4 [istreambuf.iterator] Status: New Submitter: Billy O'Neal III Opened: 2018-04-26 Last modified: 2020-09-06
Priority: 4
View other active issues in [istreambuf.iterator].
View all other issues in [istreambuf.iterator].
View all issues with New status.
Discussion:
LWG has said recently that we don't want public exposition-only things, as that may encourage users to try to use those names (and some implementers to actually use those names).
[2018-06-18 after reflector discussion]
Priority set to 4
Proposed resolution:
This wording is relative to N4741.
Edit 24.6.4 [istreambuf.iterator] as indicated:
namespace std {
template<class charT, class traits = char_traits<charT>>
class istreambuf_iterator {
public:
[…]
using istream_type = basic_istream<charT,traits>;
class proxy; // exposition only
constexpr istreambuf_iterator() noexcept;
[…]
private:
class proxy; // exposition only
streambuf_type* sbuf_; // exposition only
};
[…]
}
istreambuf_iterator::proxy::operator* should be constSection: 24.6.4.2 [istreambuf.iterator.proxy] Status: New Submitter: Billy O'Neal III Opened: 2018-04-26 Last modified: 2020-09-06
Priority: 3
View all issues with New status.
Discussion:
operator* on iterators is usually intended to be const; see 24.3.5.3 [input.iterators]
Table 87, *a, where a is of type X or const X. (Technically, proxy
is implementing the *r++ requirement in this table, and r doesn't imply a const iterator,
but there's no reason for the iterator's operator* to differ from the proxy)
[2018-06-18 after reflector discussion]
Priority set to 3
Proposed resolution:
This wording is relative to N4741.
Edit 24.6.4.2 [istreambuf.iterator.proxy] as indicated:
namespace std {
template<class charT, class traits = char_traits<charT>>
class istreambuf_iterator<charT, traits>::proxy { // exposition only
charT keep_;
basic_streambuf<charT,traits>* sbuf_;
proxy(charT c, basic_streambuf<charT,traits>* sbuf)
: keep_(c), sbuf_(sbuf) { }
public:
charT operator*() const { return keep_; }
};
}
strstreambuf is copyableSection: 99 [depr.strstreambuf] Status: New Submitter: Jonathan Wakely Opened: 2018-05-02 Last modified: 2018-06-19
Priority: 4
View all issues with New status.
Discussion:
In C++03 strstreambuf was not copyable, because basic_streambuf wasn't copyable.
In C++11 we made basic_streambuf copyable by derived classes, and strstreambuf
doesn't define any special members, so it (unintentionally?) became copyable, with completely
unspecified semantics.
filebuf and stringbuf.
[2018-06-18 after reflector discussion]
Priority set to 4
Proposed resolution:
DynamicBufferSection: 16 [networking.ts::buffer], 17 [networking.ts::buffer.stream] Status: LEWG Submitter: Vinnie Falco Opened: 2018-05-18 Last modified: 2025-10-21
Priority: 4
View all issues with LEWG status.
Discussion:
Addresses: networking.ts
Asynchronous algorithms are started by a call to an initiating function.
When these algorithms are constructed from calls to other initiating functions,
the result is called a composed operation. For example, async_read may be
implemented in terms of zero or more calls to a stream's async_read_some algorithm.
For operations where the caller cannot easily determine ahead of time the storage requirements
needed for an algorithm to meet its post-conditions, [networking.ts] introduces the
DynamicBuffer concept:
A dynamic buffer encapsulates memory storage that may be automatically resized as required, where the memory is divided into two regions: readable bytes followed by writable bytes. [buffer.reqmts.dynamicbuffer]
Signatures for algorithms in the technical specification which accept dynamic buffers use forwarding references:
// 17.10 [networking.ts::buffer.async.read.until], asynchronous delimited read operations: template< class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read_until( AsyncReadStream& s, DynamicBuffer&& b, char delim, CompletionToken&& token);
Because the initiating function returns immediately, and the associated composed operation executes later, it is necessary for the algorithm to manage the lifetime of the dynamic buffer. Guidance for doing so is given in the TS:
13.2.7.5 Lifetime of initiating function arguments [async.reqmts.async.lifetime]1. Unless otherwise specified, the lifetime of arguments to initiating functions shall be treated as follows: […] the implementation does not assume the validity of the argument after the initiating function completes […] The implementation may make copies of the argument, and all copies shall be destroyed no later than immediately after invocation of the completion handler.
Given the guidance above, the most sensible approach is for the implementation to move or make a decay-copy of the argument. An implementation of the TS, authored by the principal architect of the TS itself, does precisely that:
template <
typename AsyncReadStream,
typename DynamicBuffer,
typename ReadHandler>
class read_until_delim_op
{
public:
template <typename DeducedBuffers>
read_until_delim_op(
AsyncReadStream& stream,
DeducedBuffers&& buffers,
char delim, ReadHandler& handler)
: […]
buffers_(std::forward<DeducedBuffers>(buffers))
[…]
{
}
[…]
DynamicBuffer buffers_;
[…]
};
template <
typename AsyncReadStream,
typename DynamicBuffer,
typename ReadHandler>
NET_TS_INITFN_RESULT_TYPE(ReadHandler,
void (std::error_code, std::size_t))
async_read_until(
AsyncReadStream& s,
DynamicBuffer&& buffers,
char delim,
ReadHandler&& handler)
{
// If you get an error on the following line it means that your handler does
// not meet the documented type requirements for a ReadHandler.
NET_TS_READ_HANDLER_CHECK(ReadHandler, handler) type_check;
async_completion<ReadHandler,
void (std::error_code, std::size_t)> init(handler);
detail::read_until_delim_op<
AsyncReadStream,
typename decay<DynamicBuffer>::type,
NET_TS_HANDLER_TYPE(ReadHandler, void (std::error_code, std::size_t))>(
s,
DynamicBuffer&&buffers,
delim,
init.completion_handler)(std::error_code(), 0, 1);
return init.result.get();
}
Given the semantics of dynamic buffers implied by the wording, instances of
dynamic buffers behave more like references to storage types rather than
storage types, as copies refer to the same underlying storage. This can be
seen in the declaration of dynamic_string_buffer which meets
the requirements of DynamicBuffer:
template <typename Elem, typename Traits, typename Allocator>
class dynamic_string_buffer
{
[…]
private:
std::basic_string<Elem, Traits, Allocator>& string_;
std::size_t size_;
const std::size_t max_size_;
};
A dynamic string buffer contains a reference to the underlying string. Copies
of a dynamic string buffer refer to the same string. Note that the dynamic
string buffer also contains some state: the size_ and max_size_
data members. This additional metadata informs the dynamic string buffer of
the boundaries between the readable and writable bytes, as well as the maximum
allowed size of the total of the readable and writable bytes.
f and g, which both operate on an instance of
dynamic buffer. When f is invoked, it makes a copy of the dynamic
buffer and then calls g with the copy. At this point, g must
also make a copy. Copies share the underlying storage, but in the case of
dynamic string buffers each copy maintains its own distinct metadata. When
g has finished mutating the dynamic buffer and returns control back
to f by invoking the completion handler, the metadata in the copy of
the dynamic buffer held by f will not have the changes made by g.
Another design problem caused by adding metadata to the dynamic buffer concept
is illustrated in the following example code:
template<class MutableBufferSequence>
std::size_t read(const MutableBufferSequence&)
{
throw std::exception{};
}
int main()
{
std::string s;
assert(s.empty());
try
{
auto b = boost::asio::dynamic_buffer(s);
b.commit(read(b.prepare(32)));
}
catch(const std::exception&)
{
assert(s.empty()); // fails
}
}
While not technically incorrect, it may be surprising to the user that the string contains additional value-initialized data which was not part of the original readable bytes (which in this case was empty). The wording of the dynamic buffer concept does not address this case.
The solution we propose is to change the semantics of DynamicBuffer to represent a true storage type rather than a hybrid reference with metadata. Instances of dynamic buffers will be passed by reference, and callers will be required to manage the lifetime of dynamic buffer objects for the duration of any asynchronous operations. An additional benefit of this change is that it also solves the problem of exceptions described above.[2018-06-18 after reflector discussion]
Status to LEWG; there will be a paper P1100R0 in the post-Rapperswil mailing addressing this.
[2020-05-28; Billy Baker comments]
From Cologne 2019 paper discussion: P1100 has been superseded by P1790.
[2025-10-16; Updated with LEWG position.]
Discussed by LEWG in Wrocław, recommended to send to SG4.
There's no 'SG4' status in the LWG issues lists, so it remains 'LEWG' but SG4 have been made aware.
Proposed resolution:
This wording is relative to N4734.
[Drafting note: The project editor is kindly asked to replace all occurrences of
DynamicBuffer&&withDynamicBuffer&as indicated by the provided wording changes below. — end drafting note]
Modify 16.1 [networking.ts::buffer.synop], header <experimental/buffer> synopsis,
as indicated:
[…] // 16.11 [networking.ts::buffer.creation], buffer creation: […] template<class T, class Allocator> class dynamic_vector_buffer; template<class CharT, class Traits, class Allocator> class basic_dynamic_string_buffer;// 16.14 [networking.ts::buffer.dynamic.creation], dynamic buffer creation:template<class T, class Allocator> dynamic_vector_buffer<T, Allocator> dynamic_buffer(vector<T, Allocator>& vec) noexcept; template<class T, class Allocator> dynamic_vector_buffer<T, Allocator> dynamic_buffer(vector<T, Allocator>& vec, size_t n) noexcept;template<class CharT, class Traits, class Allocator> dynamic_string_buffer<CharT, Traits, Allocator> dynamic_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept; template<class CharT, class Traits, class Allocator> dynamic_string_buffer<CharT, Traits, Allocator> dynamic_buffer(basic_string<CharT, Traits, Allocator>& str, size_t n) noexcept;[…] // 17.5 [networking.ts::buffer.read], synchronous read operations: […] template<class SyncReadStream, class DynamicBuffer> size_t read(SyncReadStream& stream, DynamicBuffer&&b); template<class SyncReadStream, class DynamicBuffer> size_t read(SyncReadStream& stream, DynamicBuffer&&b, error_code& ec); template<class SyncReadStream, class DynamicBuffer, class CompletionCondition> size_t read(SyncReadStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition); template<class SyncReadStream, class DynamicBuffer, class CompletionCondition> size_t read(SyncReadStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition, error_code& ec); // 17.6 [networking.ts::buffer.async.read], asynchronous read operations: […] template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, DynamicBuffer&&b, CompletionToken&& token); template<class AsyncReadStream, class DynamicBuffer, class CompletionCondition, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition, CompletionToken&& token); // 17.7 [networking.ts::buffer.write], synchronous write operations: […] template<class SyncWriteStream, class DynamicBuffer> size_t write(SyncWriteStream& stream, DynamicBuffer&&; b); template<class SyncWriteStream, class DynamicBuffer> size_t write(SyncWriteStream& stream, DynamicBuffer&&b, error_code& ec); template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition> size_t write(SyncWriteStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition); template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition> size_t write(SyncWriteStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition, error_code& ec); // 17.8 [networking.ts::buffer.async.write], asynchronous write operations: […] template<class AsyncWriteStream, class DynamicBuffer, class CompletionToken> DEDUCED async_write(AsyncWriteStream& stream, DynamicBuffer&&b, CompletionToken&& token); template<class AsyncWriteStream, class DynamicBuffer, class CompletionCondition, class CompletionToken> DEDUCED async_write(AsyncWriteStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition, CompletionToken&& token); // 17.9 [networking.ts::buffer.read.until], synchronous delimited read operations: template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&&b, char delim); template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&&b, char delim, error_code& ec); template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&&b, string_view delim); template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&&b, string_view delim, error_code& ec); // 17.10 [networking.ts::buffer.async.read.until], asynchronous delimited read operations: template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read_until(AsyncReadStream& s, DynamicBuffer&&b, char delim, CompletionToken&& token); template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read_until(AsyncReadStream& s, DynamicBuffer&&b, string_view delim, CompletionToken&& token); […]
Modify 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer], as indicated:
-1- […]
-2- A typeXmeets theDynamicBufferrequirements if it satisfies the requirements ofDestructible(C++ 2014 [destructible])andas well as the additional requirements listed in Table 14.MoveConstructible(C++ 2014 [moveconstructible]),
Modify 16.12 [networking.ts::buffer.dynamic.vector], as indicated:
[…] template<class T, class Allocator = allocator<T>> class dynamic_vector_buffer { public: // types: using value_type = vector<T, Allocator>; using const_buffers_type = const_buffer; using mutable_buffers_type = mutable_buffer; // constructors: dynamic_vector_buffer() = default; explicit dynamic_vector_buffer(size_t maximum_size); explicit dynamic_vector_buffer(vector<T, Allocator>&vec)noexcept;dynamic_vector_buffer(vector<T, Allocator>&vec, size_t maximum_size)noexcept;dynamic_vector_buffer(dynamic_vector_buffer&&) = default; // members: size_t size() const noexcept; size_t max_size() const noexcept; void max_size(size_t maximum_size); size_t capacity() const noexcept; const_buffers_type data() const noexcept; mutable_buffers_type prepare(size_t n); void commit(size_t n); void consume(size_t n); span<const T> get() const noexcept value_type release(); private: vector<T, Allocator>&vec_; // exposition only size_t size_; // exposition onlyconstsize_t max_size_; // exposition only }; […]-2- […]
-3- […]explicit dynamic_vector_buffer(size_t maximum_size)-?- Effects: Default-constructs
vec_. Initializessize_with0, andmax_size_withmaximum_size.explicit dynamic_vector_buffer(vector<T, Allocator>&vec)noexcept-4- Effects: Initializes
vec_withmove(vec),size_withvec_.size(), andmax_size_withvec_.max_size()dynamic_vector_buffer(vector<T, Allocator>&vec, size_t maximum_size)noexcept;-5- Requires:
-6- Effects: Initializesvec.size() <= maximum_sizevec_withmove(vec),size_withvec_.size(), andmax_size_withmaximum_size. […]size_t max_size() const noexcept;-8- Returns:
max_size_.void max_size(size_t maximum_size)[…]-?- Effects: Performs
max_size_ = maximum_size.void consume(size_t n);-15- Effects: […]
span<const T> get() const noexcept-?- Returns:
span<const T>(vec_.data(), size_).value_type release()-?- Returns:
move(vec_).
Modify 16.13 [networking.ts::buffer.dynamic.string], as indicated:
template<class CharT, class Traits, class Allocator> class basic_dynamic_string_buffer { public: // types: using value_type = basic_string<CharT, Traits, Allocator>; using const_buffers_type = const_buffer; using mutable_buffers_type = mutable_buffer; // constructors: basic_dynamic_string_buffer() = default; explicit basic_dynamic_string_buffer(size_t maximum_size); explicit basic_dynamic_string_buffer(basic_string<CharT, Traits, Allocator>&str)noexcept;basic_dynamic_string_buffer(basic_string<CharT, Traits, Allocator>&str, size_t maximum_size)noexcept;basic_dynamic_string_buffer(basic_dynamic_string_buffer&&) = default; // members: size_t size() const noexcept; size_t max_size() const noexcept; void max_size(size_t maximum_size) size_t capacity() const noexcept; const_buffers_type data() const noexcept; mutable_buffers_type prepare(size_t n); void commit(size_t n) noexcept; void consume(size_t n); basic_string_view<CharT, Traits> get() const noexcept value_type release(); private: basic_string<CharT, Traits, Allocator>&str_; // exposition only size_t size_; // exposition onlyconstsize_t max_size_; // exposition only }; using dynamic_string_buffer = basic_dynamic_string_buffer<char, char_traits<char>, allocator<char>>-2- […]
-3- […]explicit basic_dynamic_string_buffer(size_t maximum_size)-?- Effects: Default-constructs
str_. Initializessize_with0, andmax_size_withmaximum_size.[…]
explicit basic_dynamic_string_buffer(basic_string<CharT, Traits, Allocator>&str)noexcept-4- Effects: Initializes
str_withmove(str),size_withstr_.size(), andmax_size_withstr_.max_size()basic_dynamic_string_buffer(string<CharT, Traits, Allocator>&str, size_t maximum_size)noexcept;-5- Requires:
str.size() <= maximum_size.-6- Effects: Initializes
str_withmove(str),size_withstr_.size(), andmax_size_withmaximum_size.[…]
size_t max_size() const noexcept;-8- Returns:
max_size_.void max_size(size_t maximum_size)-?- Effects: Performs
max_size_ = maximum_size.[…]
void consume(size_t n);-15- Effects: […]
basic_string_view<CharT, Traits> get() const noexcept-?- Returns:
basic_string_view<CharT, Traits>(str_).value_type release()-?- Returns:
move(str_).
Remove 16.14 [networking.ts::buffer.dynamic.creation] entirely
Modify 17.5 [networking.ts::buffer.read], as indicated:
[…] template<class SyncReadStream, class DynamicBuffer> size_t read(SyncReadStream& stream, DynamicBuffer&&b); template<class SyncReadStream, class DynamicBuffer> size_t read(SyncReadStream& stream, DynamicBuffer&&b, error_code& ec); template<class SyncReadStream, class DynamicBuffer, class CompletionCondition> size_t read(SyncReadStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition); template<class SyncReadStream, class DynamicBuffer, class CompletionCondition> size_t read(SyncReadStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition, error_code& ec); […]
Modify 17.6 [networking.ts::buffer.async.read], as indicated:
[…] template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, DynamicBuffer&&b, CompletionToken&& token); template<class AsyncReadStream, class DynamicBuffer, class CompletionCondition, class CompletionToken> DEDUCED async_read(AsyncReadStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition, CompletionToken&& token); […]-14- The program shall ensure both the
AsyncReadStreamobjectstreamand theDynamicBufferobjectbareisvalid until the completion handler for the asynchronous operation is invoked.
Modify 17.7 [networking.ts::buffer.write], as indicated:
[…] template<class SyncWriteStream, class DynamicBuffer> size_t write(SyncWriteStream& stream, DynamicBuffer&&b); template<class SyncWriteStream, class DynamicBuffer> size_t write(SyncWriteStream& stream, DynamicBuffer&&b, error_code& ec); template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition> size_t write(SyncWriteStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition); template<class SyncWriteStream, class DynamicBuffer, class CompletionCondition> size_t write(SyncWriteStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition, error_code& ec); […]
Modify 17.8 [networking.ts::buffer.async.write], as indicated:
[…] template<class AsyncWriteStream, class DynamicBuffer, class CompletionToken> DEDUCED async_write(AsyncWriteStream& stream, DynamicBuffer&&b, CompletionToken&& token); template<class AsyncWriteStream, class DynamicBuffer, class CompletionCondition, class CompletionToken> DEDUCED async_write(AsyncWriteStream& stream, DynamicBuffer&&b, CompletionCondition completion_condition, CompletionToken&& token); […]-14- The program shall ensure both the
AsyncWriteStreamobjectstreamand theDynamicBufferobjectbmemory associated with the dynamic bufferare valid until the completion handler for the asynchronous operation is invoked.b
Modify 17.9 [networking.ts::buffer.read.until], as indicated:
template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&&b, char delim); template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&&b, char delim, error_code& ec); template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&&b, string_view delim); template<class SyncReadStream, class DynamicBuffer> size_t read_until(SyncReadStream& s, DynamicBuffer&&b, string_view delim, error_code& ec); […]
Modify 17.10 [networking.ts::buffer.async.read.until], as indicated:
template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read_until(AsyncReadStream& s, DynamicBuffer&&b, char delim, CompletionToken&& token); template<class AsyncReadStream, class DynamicBuffer, class CompletionToken> DEDUCED async_read_until(AsyncReadStream& s, DynamicBuffer&&b, string_view delim, CompletionToken&& token); […]-6- The program shall ensure both the
AsyncReadStreamobjectstreamand theDynamicBufferobjectbareisvalid until the completion handler for the asynchronous operation is invoked.
execution_context is intended to store servicesSection: 13.7.5 [networking.ts::async.exec.ctx.globals] Status: New Submitter: Billy O'Neal III Opened: 2018-06-23 Last modified: 2020-09-06
Priority: 3
View all issues with New status.
Discussion:
Addresses: networking.ts
make_service and use_service create arbitrary numbers of type Service, a type provided
by the user, similar to how locale's use_facet works. As a result there's no amount of storage that could
be reserved inside execution_context to avoid allocating memory. However, there's no allocator support
here, and make_service is forbidden from throwing allocation related exceptions by
N4734 [async.exec.ctx.globals]/7.
execution_context to allocate memory, are user overloads of operator new
on type Service intended to be used?
[2018-07-20 Priority set to 3 after reflector discussion]
[Jonathan provides wording.]
Proposed resolution:
This wording is relative to the N4762.
Modify 13.7.5 [networking.ts::async.exec.ctx.globals] p7:
-7- Throws: service_already_exists if a corresponding service object of type Service::key_type is
already present in the set , bad_alloc, or an implementation-defined exception when a resource other than memory could
not be obtained. Any exception thrown by the constructor of Service.
std::sub_match::compare(string_view) overloadSection: 28.6.8 [re.submatch] Status: New Submitter: Jonathan Wakely Opened: 2018-06-26 Last modified: 2024-10-03
Priority: 3
View all other issues in [re.submatch].
View all issues with New status.
Discussion:
std::sub_match::compare can be called with a basic_string or a pointer to a null-terminated
character sequence, but can't be called with a basic_string_view. To compare to a string_view
requires either conversion to basic_string (with a potential allocation) or a redundant call to
traits_type::length to calculate a length that is already known.
[2018-07-02, Jonathan comments and completes proposed wording]
To make the relational and equality operators for sub_match support string views I propose specifying
the semantics, not adding another 12 overloaded operators to namespace std, in addition to the 42
already there. This allows them to be implemented as "hidden friends" if the implementation so desires, or to
retain namespace-scope declaration if backwards compatibility with C++11 - C++17 is preferred.
[2018-07-20 Priority set to 3 after reflector discussion]
Previous resolution [SUPERSEDED]:
This wording is relative to N4750.
Change 28.6.3 [re.syn], header
<regex>synopsis, as indicated:#include <initializer_list> namespace std { […] using csub_match = sub_match<const char*>; using wcsub_match = sub_match<const wchar_t*>; using ssub_match = sub_match<string::const_iterator>; using wssub_match = sub_match<wstring::const_iterator>;// 28.6.8.3 [re.submatch.op], sub_match non-member operators template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs); […] template<class BiIter> bool operator>=(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type& rhs);template<class charT, class ST, class BiIter> basic_ostream<charT, ST>& operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m); […] }Change 28.6.8 [re.submatch], class template
sub_matchsynopsis, as indicated:namespace std { template<class BidirectionalIterator> class sub_match : public pair<BidirectionalIterator, BidirectionalIterator> { public: using value_type = typename iterator_traits<BidirectionalIterator>::value_type; […] int compare(const sub_match& s) const; int compare(const string_type& s) const; int compare(const value_type* s) const; int compare(basic_string_view<value_type> s) const; }; }Change 28.6.8.2 [re.submatch.members] as indicated:
int compare(const value_type* s) const;-7- Returns:
str().compare(s).int compare(basic_string_view<value_type> s) const;-?- Returns:
str().compare(s).Change sub-clause 28.6.8.3 [re.submatch.op] as indicated:
31.9.2
sub_matchnon-member operators [re.submatch.op]template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);
-1- Returns:lhs.compare(rhs) == 0.[…]template<class BiIter> bool operator>=(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type& rhs);
-42- Returns:!(lhs < rhs).template<class charT, class ST, class BiIter> basic_ostream<charT, ST>& operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m);-43- Returns:
os << m.str().Class template
sub_matchprovides overloaded relational operators (7.6.9 [expr.rel]) and equality operators (7.6.10 [expr.eq]) for comparisons with anothersub_match, with astring, or with a single character. The expressions shown in Table ?? are valid when one of the operands is a typeS, that is a specialization ofsub_match, and the other expression is one of:
(?.?) — a value
xof a typeS, in which caseSTR(x)isx.str();(?.?) — a value
xof typebasic_string<S::value_type, T, A>for any typesTandA, in which caseSTR(x)isbasic_string_view<S::value_type>(x.data(), x.length());(?.?) — a value
xof typebasic_string_view<S::value_type, T>for any typeT, in which caseSTR(x)isbasic_string_view<S::value_type>(x.data(), x.length());(?.?) — a value
xof a type convertible toconst S::value_type*, in which caseSTR(x)isbasic_string_view<S::value_type>(x);(?.?) — a value
xof type convertible toS::value_type, in which caseSTR(x)isbasic_string_view<S::value_type>(&x, 1).
Table ?? — sub_matchcomparisonsExpression Return type Operational
semanticss == tboolSTR(s).compare(STR(t)) == 0s != tboolSTR(s).compare(STR(t)) != 0s < tboolSTR(s).compare(STR(t)) < 0s > tboolSTR(s).compare(STR(t)) > 0s <= tboolSTR(s).compare(STR(t)) <= 0s >= tboolSTR(s).compare(STR(t)) >= 0
[2024-10-03; Jonathan rebases the wording on the latest WP]
The proposed resolution has been implemented and tested in libstdc++.
Proposed resolution:
This wording is relative to N4988.
Change 28.6.3 [re.syn], header <regex> synopsis, as indicated:
using csub_match = sub_match<const char*>; using wcsub_match = sub_match<const wchar_t*>; using ssub_match = sub_match<string::const_iterator>; using wssub_match = sub_match<wstring::const_iterator>; // 28.6.8.3 [re.submatch.op], sub_match non-member operators template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs); template<class BiIter> bool operator<=>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs); template<class BiIter, class ST, class SA> bool operator==( const sub_match<BiIter>& lhs, const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs); template<class BiIter, class ST, class SA> bool operator<=>( const sub_match<BiIter>& lhs, const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs); template<class BiIter, class ST> bool operator==( const sub_match<BiIter>& lhs, const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs); template<class BiIter, class ST> bool operator<=>( const sub_match<BiIter>& lhs, const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs); template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type* rhs); template<class BiIter> bool operator<=>(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type* rhs); template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type& rhs); template<class BiIter> bool operator<=>(const sub_match<BiIter>& lhs, const typename iterator_traits<BiIter>::value_type& rhs); template<class charT, class ST, class BiIter> basic_ostream<charT, ST>& operator<<(basic_ostream<charT, ST>& os, const sub_match<BiIter>& m);
Change 28.6.8 [re.submatch], class template sub_match synopsis, as indicated:
namespace std {
template<class BidirectionalIterator>
class sub_match : public pair<BidirectionalIterator, BidirectionalIterator> {
public:
using value_type =
typename iterator_traits<BidirectionalIterator>::value_type;
[…]
int compare(const sub_match& s) const;
int compare(const string_type& s) const;
int compare(const value_type* s) const;
int compare(basic_string_view<value_type> s) const;
};
}
Change 28.6.8.2 [re.submatch.members] as indicated:
int compare(const value_type* s) const;-7- Returns:
str().compare(s).int compare(basic_string_view<value_type> s) const;-?- Returns:
str().compare(s).
Change sub-clause 28.6.8.3 [re.submatch.op] as indicated:
32.8.3
sub_matchnon-member operators [re.submatch.op]-1- Let
SV(I)bebasic_string_view<typename iterator_traits<I>::value_type>and letSM-CAT(I)becompare_three_way_result_t<basic_string<typename iterator_traits<I>::value_type>SV(I)>template<class BiIter> bool operator==(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);-2- Returns:
lhs.compare(rhs) == 0.template<class BiIter> bool operator<=>(const sub_match<BiIter>& lhs, const sub_match<BiIter>& rhs);-3- Returns:
static_cast<SM-CAT(BiIter)>(lhs.compare(rhs) <=> 0).template<class BiIter, class ST, class SA> bool operator==( const sub_match<BiIter>& lhs, const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);-4- Returns:
lhs.compare(.typename sub_match<BiIter>::string_typeSV(BiIter)(rhs.data(), rhs.size())) == 0template<class BiIter, class ST, class SA> auto operator<=>( const sub_match<BiIter>& lhs, const basic_string<typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);-5- Returns:
static_cast<SM-CAT(BiIter)>(lhs.compare(typename sub_match<BiIter>::string_typeSV(BiIter)(rhs.data(), rhs.size())) <=> 0 )template<class BiIter, class ST> bool operator==( const sub_match<BiIter>& lhs, const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs);-?- Returns:
lhs.compare(SV(BiIter)(rhs.data(), rhs.size())) == 0.template<class BiIter, class ST> auto operator<=>( const sub_match<BiIter>& lhs, const basic_string_view<typename iterator_traits<BiIter>::value_type, ST>& rhs);-?- Returns:
static_cast<SM-CAT(BiIter)>(lhs.compare(SV(BiIter)(rhs.data(), rhs.size())) <=> 0).
std::foo<incomplete> should be ill-formed NDRSection: 16.4.5.8 [res.on.functions] Status: New Submitter: Casey Carter Opened: 2018-07-07 Last modified: 2018-11-27
Priority: 3
View all other issues in [res.on.functions].
View all issues with New status.
Discussion:
16.4.5.8 [res.on.functions]/2 states:
While undefined behavior is appropriate for the other cases specified in the earlier bullets, which describe failure to meet (runtime) semantic requirements, "instantiating a template component or evaluating a concept" with an incomplete type happens at compile-time, and could potentially be diagnosed. It would therefore be more appropriate to specify that programs which do so are ill-formed with no diagnostic required.-2- In particular, the effects are undefined in the following cases:
[…]
(2.5) — if an incomplete type (6.9 [basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.
[2018-11 Reflector prioritization]
Set Priority to 3
Proposed resolution:
This wording is relative to N4762.
Change 16.4.5.8 [res.on.functions] as indicated:
(2.5) — if an incomplete type (6.9 [basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.-?- Unless explicitly stated otherwise, a program that instantiates a template component or evaluates a concept with an incomplete type (6.9 [basic.types]) as a template argument is ill-formed with no diagnostic required.
destroy and fancy pointer operations must be non-throwingSection: 16.4.4.6 [allocator.requirements] Status: New Submitter: Billy O'Neal III Opened: 2018-09-07 Last modified: 2018-12-16
Priority: 3
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with New status.
Discussion:
In annotating things required to be called by ~vector, Casey pointed out that several operations I guarded
with noexcept aren't actually mandated by the standard to be noexcept. However, the STL, and more
specifically here, containers, consider inability to destroy an element an unrecoverable condition. This is evidenced
for the whole STL by 16.4.6.14 [res.on.exception.handling]/3 "Every destructor in the C ++ standard library shall
behave as if it had a non-throwing exception specification.".
allocator::destroy and fancy pointer operations must be non-throwing for valid input, or the
containers don't make any sense. This is obvious for things like vector::~vector, but less obviously the
containers rely on these guarantees whenever inserting more than one element, etc.
Moreover, we too narrowly specify the domain of the pointer_traits::pointer_to requirement in the
Cpp17Allocator requirements, because any node-based container that uses container-internal sentinel
nodes needs to be able to form pointers to said sentinel nodes; that operation must also be non-throwing.
[2018-09 Reflector prioritization]
Set Priority to 3
Proposed resolution:
This wording is relative to N4762.
Modify 16.4.4.6 [allocator.requirements], Table 32 "Descriptive variable definitions" as indicated:
Table 32 — Descriptive variable definitions Variable Definition …YYthe type allocator_traits<Y>Zan allocator-aware container type (23.2.2 [container.requirements.general]) …ya value of type XX::const_void_pointerobtained by
conversion from a result value ofYY::allocate, or else a
value of type (possiblyconst)std::nullptr_t.zan lvalue of type Zsuch thatz.get_allocator() == ar1a reference to any member subobject of zna value of type XX::size_type.…
Modify 16.4.4.6 [allocator.requirements], Table 33 "Cpp17Allocator requirements" as indicated:
Table 33 — Cpp17AllocatorrequirementsExpression Return type Assertion/note
pre-/post-conditionDefault …pointer_traits<
X::pointer
>::pointer_to(r)X::pointerS same asp.
Throws: Nothing.pointer_traits<
X::pointer
>::pointer_to(r1)A value of type YY::pointeror
YY::const_pointerksuch that
*kisr1.
Throws: Nothing.…a.destroy(c)(not used) Effects: Destroys the object at c.
Throws: Nothing.c->~C()…
Modify 16.4.4.6 [allocator.requirements], p5, as indicated:
-5- An allocator type
Xshall satisfy theCpp17CopyConstructiblerequirements (Table 26). TheX::pointer,X::const_pointer,X::void_pointer, andX::const_void_pointertypes shall satisfy theCpp17NullablePointerrequirements (Table 30). No constructor, comparison function, copy operation, move operation, or swap operation on these pointer types shall exit via an exception.X::pointerandX::const_pointershall also satisfy the requirements for a random access iterator (24.3.5.7 [random.access.iterators]) and of a contiguous iterator (24.3.1 [iterator.requirements.general]) and operations in those requirements shall not exit via an exception so long as resulting iterators are dereferencable or past-the-end.
Modify 20.2.9.3 [allocator.traits.members], as indicated:
template<class T> static void destroy(Alloc& a, T* p);-6- Effects: Calls
-?- Throws: Nothing.a.destroy(p)if that call is well-formed; otherwise, invokesp->~T().
Section: 20.3.1.3 [unique.ptr.single] Status: New Submitter: Jonathan Wakely Opened: 2018-09-17 Last modified: 2018-10-06
Priority: 3
View other active issues in [unique.ptr.single].
View all other issues in [unique.ptr.single].
View all issues with New status.
Discussion:
20.3.1.3 [unique.ptr.single] p1 says:
The default type for the template parameter
Disdefault_delete. A client-supplied template argumentDshall be a function object type (19.14), lvalue reference to function, or lvalue reference to function object type for which, given a valuedof typeDand a value ptr of typeunique_ptr<T, D>::pointer, the expressiond(ptr)is valid and has the effect of disposing of the pointer as appropriate for that deleter.
That means this is undefined:
#include <memory>
struct IncompleteBase;
struct Deleter {
void operator()(IncompleteBase*) const;
};
struct IncompleteDerived;
struct X {
std::unique_ptr<IncompleteDerived, Deleter> p;
~X();
};
unique_ptr::pointer is IncompleteDerived*, but is_invocable<Deleter, IncompleteDerived*>
is unknowable until the type is complete (see LWG 3099(i) etc).
IncompleteDerived only needs to be complete when the deleter is invoked, which is in the
definition of X::~X() for this example. But the requirement for d(ptr) to be valid requires a complete type.
If the unique_ptr implementation adds static_assert(is_invocable_v<D, pointer>) to enforce the
requirement, the example above fails to compile. GCC recently added that assertion.
Do we want to relax that requirement, or do we want to force the code above to define Deleter::pointer as
IncompleteBase* so that the is_invocable condition can be checked?
The destructor and reset member function already require that the deleter can be invoked (and that Requires:
element will be turned into a Mandates: one soon). We can just remove that requirement from the preamble for the
class template, or say that the expression only needs to be valid when the destructor and reset member are
instantiated. We could also rephrase it in terms of is_invocable_v<D, unique_ptr<T, D>::pointer>.
[2018-10 Reflector prioritization]
Set Priority to 3
Proposed resolution:
emplace_back but don't require itSection: 23.6.6 [stack], 23.6.3 [queue] Status: Open Submitter: Marshall Clow Opened: 2018-10-02 Last modified: 2020-05-09
Priority: 3
View all other issues in [stack].
View all issues with Open status.
Discussion:
23.6.6 [stack] p1 says:
Any sequence container supporting operationsback(),push_back()andpop_back()can be used to instantiatestack.
but then in 23.6.6.2 [stack.defn] we have the following code:
template<class... Args>
decltype(auto) emplace(Args&&... args)
{ return c.emplace_back(std::forward<Args>(args)...); }
The same pattern appears in 23.6.3 [queue].
I see two ways to resolve this:
The first is to add emplace_back() to the list of requirements for underlying containers for stack and queue
The second is to replace the calls to c.emplace_back(std::forward<Args>(args)...) with c.emplace(c.end(), std::forward<Args>(args)...). We can do this w/o messing with the list above because emplace is part of the sequence container requirements, while emplace_back is not. I checked the libc++ implementation of vector, deque, and list, and they all do the same thing for emplace(end(), ...) and emplace_back(...).
[2019-02; Kona Wednesday night issue processing]
Status to Open; Casey to provide updated wording, and re-vote on reflector.
Polls were: NAD - 5-1-3; "Option B" - 2-5-2 and "Probe the container" - 7-2-0
Previous resolution [SUPERSEDED]:This wording is relative to N4762.
I have prepared two mutually exclusive options.
Option A a requirement foremplace_backto the underlying container.
Option B one replaces the calls toemplace_backwith calls toemplace.Option A
Edit 23.6.6 [stack], as indicated:
Any sequence container supporting operations
back(),push_back(),emplace_back()andpop_back()can be used to instantiatestack.Edit 23.6.3.1 [queue.defn], as indicated:
Any sequence container supporting operations
front(),back(),push_back(),emplace_back()andpop_front()can be used to instantiatequeue.Option B
Edit 23.6.6.2 [stack.defn], class template
stackdefinition, as indicated:template<class... Args> decltype(auto) emplace(Args&&... args) { return c.emplace_back(c.end(), std::forward<Args>(args)...); }Edit 23.6.3.1 [queue.defn], class template
queuedefinition, as indicated:template<class... Args> decltype(auto) emplace(Args&&... args) { return c.emplace_back(c.end(), std::forward<Args>(args)...); }
[2020-05 Casey provides new wording]
This is the "probe for emplace_back with fallback to emplace" approach that LWG
wanted to see wording for in Kona.
[2020-05-09; Reflector prioritization]
Set priority to 3 after reflector discussions.
Proposed resolution:
This wording is relative to N4861.
Edit 23.6.6.2 [stack.defn], class template stack definition, as indicated:
template<class... Args>
decltype(auto) emplace(Args&&... args) {
if constexpr (requires { c.emplace_back(std::forward<Args>(args)...); }) {
return c.emplace_back(std::forward<Args>(args)...);
} else {
return c.emplace(c.end(), std::forward<Args>(args)...);
}
}
Edit 23.6.3.1 [queue.defn], class template queue definition, as indicated:
template<class... Args>
decltype(auto) emplace(Args&&... args) {
if constexpr (requires { c.emplace_back(std::forward<Args>(args)...); }) {
return c.emplace_back(std::forward<Args>(args)...);
} else {
return c.emplace(c.end(), std::forward<Args>(args)...);
}
}
system_error::system_error(error_code ec) not explicitSection: 19.5.8.2 [syserr.syserr.members] Status: New Submitter: Peter Dimov Opened: 2018-10-02 Last modified: 2020-04-07
Priority: 3
View all other issues in [syserr.syserr.members].
View all issues with New status.
Discussion:
The constructor for std::system_error taking a single argument of type std::error_code is
not marked explicit, which allows implicit conversions from error_code to system_error. I
think that this is an oversight and not intentional, and that we should make this constructor explicit.
[2020-04-07 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
This wording is relative to N4762.
Change 19.5.8.1 [syserr.syserr.overview] p2, class system_error synopsis, as indicated
namespace std {
class system_error : public runtime_error {
public:
system_error(error_code ec, const string& what_arg);
system_error(error_code ec, const char* what_arg);
explicit system_error(error_code ec);
system_error(int ev, const error_category& ecat, const string& what_arg);
system_error(int ev, const error_category& ecat, const char* what_arg);
system_error(int ev, const error_category& ecat);
const error_code& code() const noexcept;
const char* what() const noexcept override;
};
}
Change 19.5.8.2 [syserr.syserr.members] as indicated
explicit system_error(error_code ec);-5- Effects: […]
-6- Ensures: […]
Section: 22.3.4 [pair.astuple], 22.6.4 [variant.helper], 23.3.3.7 [array.tuple], 20.2.3.2 [pointer.traits.types], 20.2.9.2 [allocator.traits.types], 20.6.2 [allocator.adaptor.types] Status: New Submitter: Walter Brown Opened: 2018-11-08 Last modified: 2020-09-06
Priority: 3
View all other issues in [pair.astuple].
View all issues with New status.
Discussion:
In N4778, 22.3.4 [pair.astuple], 22.6.4 [variant.helper], and 23.3.3.7 [array.tuple], are partly specified via a fictitious descriptive element Value: Moreover, 99 [span.tuple] is on track to do likewise in the near future.
Let's invent such a Value: element and properly document it within 16.3.2.4 [structure.specifications], or else let's respecify the offending uses.[2018-11 Reflector prioritization]
Set Priority to 3
[2020-05-01; Daniel comments]
It should be pointed out that the originally referred to Value: element has since then be renamed to Type: but the reported problem (its lack of definition) still exists in N4861.
Proposed resolution:
observer_ptr support function types?Section: 8.2 [fund.ts.v3::memory.observer.ptr] Status: Open Submitter: Alisdair Meredith Opened: 2018-11-14 Last modified: 2022-10-12
Priority: 3
View all issues with Open status.
Discussion:
Addresses: fund.ts.v3
From the wording, function pointers are never clearly considered. P1 infers support for only objects, but it is not clear that wording is intended to be a deliberate restriction, or is casual wording.
Paragraph 2 mandates that we cannot instantiate for reference types, and do support incomplete types, but still does not consider function types. Calling out references specifically, as they are not object types, suggests the inferred support for only objects in p1 is more a case of casual phrasing, than deliberate intent. However, if we did intend to support function pointers, we may want to consider adding a function-call operator, constrained to supporting pointer-to function types. One other possibility is that the explicit conversion topointer already serves that
purpose, although I need to double-check the core language that contextual
conversions to function pointer are considered when invoking the function call
operator. If this is the case, then I suggest a note to that effect in
[memory.observer.ptr.conv] instead.
[2018-11 Reflector prioritization]
Set Priority to 3
[2022-10-12 LWG telecon]
Discussed on reflector in July 2022, no consensus on how observer_ptr
is even meant to be used.
shared_ptr supports function types, no good reason to disallow them here.
No desire to make any change for LFTSv3, but keep the issue open until/unless
the TS is withdrawn. That way we will be less likely to forget about this if
observer_ptr is propsed for the IS.
Proposed resolution:
std::hypot is underspecified compared to the 2-arg overloadSection: 29.7.3 [c.math.hypot3] Status: New Submitter: Matthias Kretz Opened: 2018-12-06 Last modified: 2018-12-21
Priority: 3
View all issues with New status.
Discussion:
The 2-arg hypot function specified in the C standard is required to avoid overflow and underflow
(7.12.7.3 p2). Furthermore C's Annex F (IEC 60559 floating-point arithmetic) defines special cases for
inputs of ±0 and ±inf (F. 10.4.3). The 3-arg hypot function
29.7.3 [c.math.hypot3] is only specified as "Returns:
.". This is
inconsistent with the 2-arg overload.
<cmath> are the same as the C standard library
header <math.h>, […]".
[2018-12-21 Reflector prioritization]
Set Priority to 3
Proposed resolution:
This wording is relative to N4778.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A
Modify 29.7.3 [c.math.hypot3] as indicated:
float hypot(float x, float y, float z); double hypot(double x, double y, double z); long double hypot(long double x, long double y, long double z);-?- Effects: The
-1- Returns: . -?- Remarks: Ifhypotfunctions compute the square root of the sum of the squares ofx,y, andz, without undue overflow or underflow. A range error may occur.__STDC_IEC_559__is defined, the following guarantees hold:
hypot(x, y, z),hypot(x, z, y),hypot(z, y, x), andhypot(x, y, -z)are equivalent.if
y2 + z2 == ±0,hypot(x, y, z)is equivalent tofabs(x).
hypot(±∞, y, z)returns+∞, even ifyand/orzis a NaN.
Option B
Add a note that clarifies that the behavior of the 3-arg hypot function
differs from the C specification. I.e. that no special guarantees wrt. over-/underflow or
special values are given.
is_convertible is too strongSection: 21.3.8 [meta.rel] Status: New Submitter: Casey Carter Opened: 2018-12-09 Last modified: 2019-03-16
Priority: 3
View other active issues in [meta.rel].
View all other issues in [meta.rel].
View all issues with New status.
Discussion:
Per Table 49
in 21.3.8 [meta.rel], the preconditions for both
is_convertible<From, To> and
is_nothrow_convertible<From, To> are:
FromandToshall be complete types, arrays of unknown bound, or cvvoidtypes.
Consequently, this program fragment:
struct S; static_assert(is_convertible_v<S, const S&>);
has undefined behavior despite that the actual behavior of is_convertible
specified in [meta.rel]/5:
-5- The predicate condition for a template specialization
is_convertible<From, To>shall be satisfied if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function:To test() { return declval<From>(); }[ Note: …]
is well-formed: declval<S>() is an xvalue of type S,
which certainly does implicitly convert to const S&. We should
relax the precondition to allow this perfectly valid case (and similar cases
like is_convertible<S, S&&>), letting the cases that
would in fact be invalid fall through to the blanket "incompletely-defined
object type" wording in
[meta.rqmts]/5.
[2018-12-21 Reflector prioritization]
Set Priority to 3
Proposed resolution:
This wording is relative to N4791.
Modify Table 49 in 21.3.8 [meta.rel] as follows:
| Template | Condition | Comments |
|---|---|---|
| […] | […] | […] |
template<class From, class To> struct is_convertible; |
see below |
From andTo shall be acomplete type array or cv void |
template<class From, class To> struct is_nothrow_convertible; |
is_convertible_v<From, is true and theconversion, as defined by is_convertible, is knownnot to throw any exceptions (7.6.2.7 [expr.unary.noexcept]) |
From andTo shall be acomplete type array or cv void |
| […] | […] | […] |
istreambuf_iterator::pointer should not be unspecifiedSection: 24.6.4 [istreambuf.iterator] Status: New Submitter: Jonathan Wakely Opened: 2019-02-21 Last modified: 2019-10-30
Priority: 3
View other active issues in [istreambuf.iterator].
View all other issues in [istreambuf.iterator].
View all issues with New status.
Discussion:
The current working paper requires iterator_traits<Iter>::pointer to be void if Iter
doesn't define operator-> (24.3.2.3 [iterator.traits]). We recently removed operator->
from istreambuf_iterator via LWG 2790(i), therefore either its pointer member should be
void, or there should be a partial specialization of iterator_traits. Do we want to change
unspecified to void in the class synopsis in [istreambuf.iterator]?
[2019-03-03, Daniel provides concrete wording]
[2019-03-05 Priority set to 3 after reflector discussion]
[2019-03-05, Daniel comments]
With the acceptance of P1252R2 the committee decided to deprecate operator->
of move_iterator, interestingly without mentioning what should happen with its current pointer typedef (which
is equal to the template parameter Iterator and thus never void). Effectively this is a very similar situation
as for the here discussed istreambuf_iterator case and it seems attractive to me to solve both cases similarly.
[2019-10-30, Jonathan comments]
Also, reverse_iterator::operator->() is now constrained and so not always defined, but
reverse_iterator::pointer is defined unconditionally.
Proposed resolution:
This wording is relative to N4800.
Change class template istreambuf_iterator synopsis, 24.6.4 [istreambuf.iterator], as indicated:
template<class charT, class traits = char_traits<charT>>
class istreambuf_iterator {
public:
using iterator_category = input_iterator_tag;
using value_type = charT;
using difference_type = typename traits::off_type;
using pointer = voidunspecified;
using reference = charT;
[…]
};
std::priority_queueSection: 23.6.4 [priority.queue] Status: New Submitter: Jonathan Wakely Opened: 2019-02-21 Last modified: 2019-03-05
Priority: 3
View all other issues in [priority.queue].
View all issues with New status.
Discussion:
We don't require that the Compare template parameter can be invoked with arguments of the queue's value type.
It seems like something we can use Mandates: for, since it'll be ill-formed otherwise. Something like
is_invocable_r_v<bool, Compare&, value_type&, value_type&>.
Compare parameter for the merge and sort functions of
forward_list and list.
[2019-03-05 Priority set to 3 after reflector discussion]
Proposed resolution:
const typesSection: 20.2.8.2 [allocator.uses.construction] Status: New Submitter: Jonathan Wakely Opened: 2019-02-28 Last modified: 2020-05-01
Priority: 3
View other active issues in [allocator.uses.construction].
View all other issues in [allocator.uses.construction].
View all issues with New status.
Discussion:
The new functions added by P0591R4 misbehave for cv-qualified types.
A specialization std::uses_allocator<X, Alloc> will not match const X, so
std::uses_allocator_construction_args<const X> will return a different result from
std::uses_allocator_construction_args<X>. It makes no sense to construct X and const X
differently, either the type wants to use an allocator or it doesn't. I think
std::uses_allocator_construction_args<T> should remove cv-qualifiers before checking
uses_allocator, so that it works consistently.
std::make_obj_using_allocator to also strip cv-qualifiers, but it's not
necessary as C++17 guaranteed elision works even for prvalues of const types. We only need to make the
construction args ignore cv-qualifiers. We don't want to make cv-qualified types ill-formed, because
that would require users of uses-allocator construction to strip cv-qualifiers before using these functions,
e.g. in cases like std::tuple<const int> t(allocator_arg, alloc, 1);
[2019-03-15 Priority set to 3 after reflector discussion]
Previous resolution [SUPERSEDED]:This wording is relative to N4800.
Change 20.2.8.2 [allocator.uses.construction] as indicated:
template <class T, class Alloc, class... Args> auto uses_allocator_construction_args(const Alloc& alloc, Args&&... args) -> see below;[…]-4- Constraints:
-5- Returns: ATis not a specialization ofpair.tuplevalue determined as follows, whereUdenotes the typeremove_cv_t<T>:
(5.1) — If
uses_allocator_v<isTU, Alloc>falseandis_constructible_v<T, Args...>istrue, returnforward_as_tuple(std::forward<Args>(args)...).(5.2) — Otherwise, if
uses_allocator_v<isTU, Alloc>trueandis_constructible_v<T, allocator_arg_t, Alloc, Args...>istrue, returntuple<allocator_arg_t, const Alloc&, Args&&...>( allocator_arg, alloc, std::forward<Args>(args)...)(5.3) — Otherwise, if
uses_allocator_v<isTU, Alloc>trueandis_constructible_v<T, Args..., Alloc>istrue, returnforward_as_tuple(std::forward<Args>(args)..., alloc).(5.4) — Otherwise, the program is ill-formed.
template <class T, class Alloc, class Tuple1, class Tuple2> auto uses_allocator_construction_args(const Alloc& alloc, piecewise_construct_t, Tuple1&& x, Tuple2&& y) -> see below;-6- Constraints:
-7- Effects: ForTis a specialization ofpair.Tspecified as (possiblyconst)pair<T1, T2>, equivalent to: […]template <class T, class Alloc, class... Args> T* uninitialized_construct_using_allocator(T* p, const Alloc& alloc, Args&&... args);-17- Effects: Equivalent to:
return ::new(static_cast<void*>voidify(*p)) T(make_obj_using_allocator<T>(alloc, std::forward<Args>(args)...));
[2020-05-01; Daniel syncs wording with recent working draft]
The previously needed change for uninitialized_construct_using_allocator is no longer
required, because the reworded call to construct_at does do the right thing now.
Proposed resolution:
This wording is relative to N4861.
Change 20.2.8.2 [allocator.uses.construction] as indicated:
template <class T, class Alloc, class... Args> constexpr auto uses_allocator_construction_args(const Alloc& alloc, Args&&... args) noexcept -> see below;[…]-4- Constraints:
-5- Returns: ATis not a specialization ofpair.tuplevalue determined as follows, whereUdenotes the typeremove_cv_t<T>:
(5.1) — If
uses_allocator_v<isTU, Alloc>falseandis_constructible_v<T, Args...>istrue, returnforward_as_tuple(std::forward<Args>(args)...).(5.2) — Otherwise, if
uses_allocator_v<isTU, Alloc>trueandis_constructible_v<T, allocator_arg_t, const Alloc&, Args...>istrue, returntuple<allocator_arg_t, const Alloc&, Args&&...>( allocator_arg, alloc, std::forward<Args>(args)...)(5.3) — Otherwise, if
uses_allocator_v<isTU, Alloc>trueandis_constructible_v<T, Args..., const Alloc&>istrue, returnforward_as_tuple(std::forward<Args>(args)..., alloc).(5.4) — Otherwise, the program is ill-formed.
template <class T, class Alloc, class Tuple1, class Tuple2> constexpr auto uses_allocator_construction_args(const Alloc& alloc, piecewise_construct_t, Tuple1&& x, Tuple2&& y) noexcept -> see below;-6- Constraints:
-7- Effects: ForTis a specialization ofpair.Tspecified as (possiblyconst)pair<T1, T2>, equivalent to: […]
Section: 16.3.2.4 [structure.specifications] Status: New Submitter: Daniel Krügler Opened: 2019-03-04 Last modified: 2020-06-11
Priority: 3
View other active issues in [structure.specifications].
View all other issues in [structure.specifications].
View all issues with New status.
Discussion:
The working paper uses the special elements Mandates:, Expects: as well as Requires: to types, albeit 16.3.2.4 [structure.specifications] defines them only for functions, for example 16.3.2.4 [structure.specifications] sub-bullet (3.4):
Expects: the conditions (sometimes termed preconditions) that the function assumes to hold whenever it is called.
Examples for such usages on types are (from N4800):
27.2.3 [char.traits.typedefs] for types int_type and state_type
22.3.4 [pair.astuple] for tuple_element<I, pair<T1, T2>>::type
22.4.7 [tuple.helper] for tuple_element<I, tuple<Types...>>::type
22.4.11 [tuple.traits] for uses_allocator<tuple<Types...>, Alloc>
Table 62 — "Container requirements" for type XX::value_type
Table 65 — "Allocator-aware container requirements" for type allocator_type
Table 69 — "Associative container requirements" for types X::value_type and X::key_compare
Table 70 — "Unordered associative container requirements" for types X::value_type and X::key_equal
Instead of replacing these elements usages for these places by extra wording to reach the same effects I recommend to update instead 16.3.2.4 [structure.specifications] to ensure that requirement-expressing elements are defined in a way that it also allows to express requirements imposed on types by these elements to standardize "existing practice".
Considering details, it seems obvious that Mandates:, Expects: as well as Requires: are "suitable" to be defined for types (With the acceptance of P1463R1 there are now also Mandates: for types such as Table 65 — "Allocator-aware container requirements" for typeallocator_type).
For Constraints: the meaning would not be so clear: Should it mean that there is conditionally a type defined or not?
According to the submitters knowledge there are currently no known examples for Constraints: to specify
constraint on types, therefore I'm suggesting to restrict this extension to Mandates:, Expects:,
and Requires: alone.
[2019-03-15 Priority set to 3 after reflector discussion]
[2019-03-15; Daniel comments and provides wording]
During the preparation of the wording for this issue it was found that we should allow Remarks: elements to be used for other things than functions. One example of imposed restrictions can be found in 17.12.3 [cmp.common]:
template<class... Ts> struct common_comparison_category { using type = see below; };-2- Remarks: The member typedef-name
typedenotes the common comparison type (11.10.3 [class.spaceship]) ofTs..., the expanded parameter pack. […]
The discussion of this issue speaks of "type" restrictions (versus the specified restrictions on functions), because even the non-type template argument restrictions of 22.3.4 [pair.astuple] appear in the context of a member type specification, but there are examples where not really a single (member) type is involved, e.g. in the 22.4.7 [tuple.helper] example mentioned above.
Another example is when such elements are used for the specification of template specializations, e.g. in 22.4.7 [tuple.helper]:template<class T> struct tuple_size;-1- Remarks: All specializations of
tuple_sizeshall satisfy the Cpp17UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a base characteristic ofintegral_constant<size_t, N>for someN.
Besides class template specializations, a second relevant use-case is the specification of member types
(Which are not necessarily part of a template), typically within the requirement tables, e.g. in
Table 62 — "Container requirements"'s entry X::value_type:
Requires:
Tis Cpp17Erasable fromX
The suggested wording tries to cover the generalization by means of the term "non-function entities" in addition to the existing functions to prevent being enforced to enumerate all entities to which the extended rules apply.
Previous resolution [SUPERSEDED]:This wording is relative to N4810.
Change 16.3.2.4 [structure.specifications], as indicated:
-3- Descriptions of function semantics contain the following elements (as appropriate); some of these elements may also appear in the description of non-function entities as denoted below: (footnote […])
(3.1) — Requires: the preconditions imposed on a non-function entity, or for calling the function.
(3.2) — Constraints: […]
(3.3) — Mandates: the conditions that, if not met, render the program ill-formed. [Example: An implementation might express such a condition via the constant-expression in a static_assert-declaration (Clause 9). If the diagnostic is to be emitted only after the function has been selected by overload resolution, an implementation might express such a condition via a constraint-expression (13.5.3 [temp.constr.decl]) and also define the function as deleted. — end example]
(3.4) — Expects: the conditions (sometimes termed preconditions) imposed on a non-function entity, or that the function assumes to hold whenever it is called. [Example: An implementation might express such conditions via an attribute such as
[[expects]]( [dcl.attr.contract]) on a function declaration. However, some such conditions might not lend themselves to expression via code. — end example][…]
(3.11) — Remarks: additional semantic constraints
on the function.[…]
Change 99 [res.on.required], as indicated:
-1- Violation of any preconditions specified in a
-2- Violation of any preconditions specified in anfunction'sRequires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception whenthea function's precondition is violated.function'sExpects: element results in undefined behavior.
[2020-05-01; Daniel comments and adjusts wording to recent working draft]
It should be pointed out that the originally referred to Expects: element has since then be renamed to Preconditions: and that the Requires: element does now only occur in annex D.
[2020-06-11; Jonathan comments]
This issue also affects some type traits such as alignment_of
and make_signed/make_unsigned.
In addition to clarifying what Mandates: means on a non-function
we need to decide exactly what is being mandated in the type traits.
Is instantiating the class template ill-formed,
or just odr-using the nested type or value member?
Proposed resolution:
This wording is relative to N4861.
Change 16.3.2.4 [structure.specifications], as indicated:
-3- Descriptions of function semantics contain the following elements (as appropriate); some of these elements may also appear in the description of non-function entities as denoted below: (footnote […])
(3.1) — Constraints: […]
(3.2) — Mandates: the conditions that, if not met, render the program ill-formed. [Example: An implementation might express such a condition via the constant-expression in a static_assert-declaration (9.1 [dcl.pre]). If the diagnostic is to be emitted only after the function has been selected by overload resolution, an implementation might express such a condition via a constraint-expression (13.5.3 [temp.constr.decl]) and also define the function as deleted. — end example]
(3.3) — Preconditions: the conditions imposed on a non-function entity, or that the function assumes to hold whenever it is called.
[…]
(3.10) — Remarks: additional semantic constraints
on the function.[…]
Change [res.on.expects], as indicated:
-1- Violation of any preconditions specified in a
function'sPreconditions: element results in undefined behavior.
Change [depr.res.on.required], as indicated:
[Drafting note: Interestingly, albey the Requires: element has nearly vanished, the issue is still relevant, see D.13 [depr.meta.types]]
-1- Violation of any preconditions specified in a
function'sRequires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception whenthea function's precondition is violated.
std::prev should not require BidirectionalIteratorSection: 24.4.3 [iterator.operations] Status: New Submitter: Billy O'Neal III Opened: 2019-04-03 Last modified: 2024-06-18
Priority: 3
View other active issues in [iterator.operations].
View all other issues in [iterator.operations].
View all issues with New status.
Discussion:
MSVC++ (and apparently libc++) have asserts that std::prev only accepts BidirectionalIterators,
because it's declared in the standard as accepting only BidirectionalIterator. libc++ changed their tests
(in this commit),
apparently from a bug report from Ville and Jonathan, saying that one could theoretically call std::prev
with a negative number.
prev requires a BidirectionalIterator,
but I don't see the usual wording that connects template type parameters of that name to the <algorithm>
requirements or similar. So perhaps one could argue that the name Bidirectional there has no meaning. Even
if that is the case, that's a defect in the other direction.
[2019-06-12 Priority set to 3 after reflector discussion]
[2022-04-22; Jonathan adds a comment]
P2408 changes the requirements for
types substituting BidirectionalIterator etc. in the Algorithms clause.
We should consider whether that is appropriate here, especially as algorithms
might make use of std::prev internally.
An algorithm that was changed by P2408 to accept types that model
bidirectional_iterator instead of requiring
Cpp17BidirectionalIterator might have to stop using
std::prev if we don't resolve this issue to allow it.
We should consider whether distance, advance and next
need the same treatment.
[2024-06-18; Jonathan adds a comment]
Related to LWG 2353(i) which made a similar change to std::next.
Also, if we require a Cpp17BidirectionalIterator here, then that means
you can't use std::prev on a std::bidirectional_iterator unless it also
meets the Cpp17BidirectionalIterator requirements. That seems like an
unnecessary restriction, since std::prev doesn't do anything that wouldn't
work fine with any type that models std::bidirectional_iterator.
Proposed resolution:
This wording is relative to N4810.
[Drafting Note: Three mutually exclusive options are prepared, depicted below by Option A, Option B, and Option C, respectively.]
Option A
NAD, the name BidirectionalIterator actually means that prev requires bidirectional iterators, in which
case this change to libcxx is incorrect.
Option B
Modify 24.2 [iterator.synopsis], header <iterator> synopsis, as indicated:
// 24.4.3 [iterator.operations], iterator operations […] template<classBidirectionalInputIterator> constexprBidirectionalInputIterator prev(BidirectionalInputIterator x, typename iterator_traits<BidirectionalInputIterator>::difference_type n = 1);
Modify 24.4.3 [iterator.operations] as indicated:
template<classBidirectionalInputIterator> constexprBidirectionalInputIterator prev(BidirectionalInputIterator x, typename iterator_traits<BidirectionalInputIterator>::difference_type n = 1);-7- Effects: Equivalent to:
advance(x, -n); return x;
Option C
The intent of the wording is that the template parameters apply requirements, and the defect is that they do not. We should add a requirement in 24.4.3 [iterator.operations]/1 to the effect that the template parameter names impose said requirements.
decay_t in the new common_type fallback should be remove_cvref_tSection: 21.3.9.7 [meta.trans.other] Status: New Submitter: Casey Carter Opened: 2019-05-12 Last modified: 2022-04-25
Priority: 3
View all other issues in [meta.trans.other].
View all issues with New status.
Discussion:
P0898R4 "The One Ranges Proposal" added a new fallback case to
the definition of common_type in 21.3.9.7 [meta.trans.other], bullet 3.3.4:
Otherwise, if
COND_RES(CREF(D1), CREF(D2))denotes a type, letCdenote the typedecay_t<COND_RES(CREF(D1), CREF(D2))>.
Per para 3.3, D1 and D2 are decayed types. If both are void, bullet 3.3.4
is not reached. If either is an abominable function type or void, the COND_RES
type expression above is ill-formed and bullet 3.3.4 does not apply. In all cases in which the
COND_RES expression is well-formed, D1 and D2 denote cv-unqualified
non-array object types. Given that fact, (1) CREF(D1) and CREF(D2)
are equivalent to const D1& and const D2&, respectively, and (2) the
COND_RES expression is equivalent to decltype(false ?
declval<const D1&>() : declval<const D1&>()), i.e., the second and third
operands of the conditional operator are lvalues of type const D1 and const D2, respectively.
D1 and D2 are the same type, [expr.cond]/4 does not apply. If D1 and
D2 are different types, there are a few cases to consider:
If [expr.cond]/4.1 applies, one operand is converted into an lvalue reference to the type of the other,
i.e., both resulting operands are lvalues of type either const D1 or const D2.
[expr.cond]/4.2 cannot apply since neither operand is an xvalue.
[expr.cond]/4.3.1 cannot apply since it would imply that the operands have the same type.
If [expr.cond]/4.3.2 applies — if either D1 or D2 is a base class of the
other — again the resulting operands are lvalues of type either const D1 or const D2.
If [expr.cond]/4.3.3 applies, the either the const D1& operand converts to
const D2 or the const D2& operand converts to const D1.
If none of the sub-bullets in [expr.cond]/4 applies, the operands are left unchanged.
[expr.cond]/5 applies if the operands initially had the same type, or in cases 1 and 4 above. The
conditional expression is an lvalue of type const D1 or const D2, and the
COND_RES expression yields const D1& or const D2&.
[expr.cond]/7.1 applies if the operands now have the same type, which is the type of the conditional expression.
[expr.cond]/7.2 applies if the operands have arithmetic or enumeration type; the conditional expression yields the result of applying the usual arithmetic conversions.
[expr.cond]/7.3 applies if the operands have pointer type; the conditional expression yields their composite pointer type.
[expr.cond]/7.4 applies if the operands have pointer-to-member type; the conditional expression applies some more standard conversions and yields their composite pointer type.
[expr.cond]/7.5 applies if one operand has type nullptr_t and the other is either a null
pointer constant or has type nullptr_t; the conditional expression yields nullptr_t.
In every case above, the conditional expression is either ill-formed, an lvalue of type const D1 or
const D2, or a prvalue of a non-array non-function type. Consequently the COND_RES
type expression always yields a non-array non-function type, for which decay_t and remove_cvref_t
are equivalent. We can therefore replace COND_RES(CREF(D1), CREF(D2)) in
[meta.trans.other]/3.3.4 with decltype(false ? declval<const D1&>() :
declval<const D2&>()), and replace the usage of decay_t with remove_cvref_t.
common_type.
It's not clear that common_type<T...>::type is always a decayed type without in-depth analysis.
We should non-normatively clarify that fact.
[2019-06-12 Priority set to 3 after reflector discussion]
[2020-05-01; Daniel adjusts wording to recent working draft]
[2022-04-25; Daniel adjusts wording to recent working draft]
Proposed resolution:
This wording is relative to N4910.
Modify 21.3.9.7 [meta.trans.other] as indicated:
-2- Let:
(2.1) —CREF(A)beadd_lvalue_reference_t<const remove_reference_t<A>>,(2.2) — […]
[…]
(2.9) — […]
If any of the types computed above is ill-formed, then
-3- Note A: For theCOMMON-REF(A, B)is ill-formed.common_typetrait applied to a template parameter packTof types, the membertypeshall be either defined or not present as follows:
(3.1) — […]
(3.2) — […]
(3.3) — If
sizeof...(T)is two, let the first and second types constitutingTbe denoted byT1andT2, respectively, and letD1andD2denote the same types asdecay_t<T1>anddecay_t<T2>, respectively.
(3.3.1) — […]
(3.3.2) — […]
(3.3.3) — Otherwise, if
decay_t<decltype(false ? declval<D1>() : declval<D2>())>denotes a valid type, let
Cdenote that type.(3.3.4) — Otherwise, if
COND-RES(CREF(D1), CREF(D2))remove_cvref_t<decltype(false ? declval<const D1&>() : declval<const D2&>())>denotes a type, let
Cdenote theat type.decay_t<COND-RES(CREF(D1), CREF(D2))>(3.4) — […]
[Note: Whenever the qualified-id
-4- Note B: […]common_type<T...>::typeis valid, it denotes the same type asdecay_t<common_type<T...>::type>. — end note]
allocate_shared is inconsistent about removing const from the pointer
passed to allocator construct and destroySection: 20.3.2.2.7 [util.smartptr.shared.create] Status: New Submitter: Billy O'Neal III Opened: 2019-05-29 Last modified: 2024-10-02
Priority: 3
View all other issues in [util.smartptr.shared.create].
View all issues with New status.
Discussion:
I implemented the fix for LWG 3008(i) and Stephan pointed out there's an inconsistency here
for allocate_shared<const T>.
destroy
call is done with removed cv qualifiers.
The fallback for allocator_traits::construct rejects const T* (since it static_casts
to void*), so the most likely outcome of attempting to do this today is to fail to compile, which
is a break with C++17.
Our options are:
Fix the allocator model to deal with const elements somehow, which breaks compatibility
with existing allocators unprepared for const elements here. We would need to extend the allocator
requirements to allow const T* to be passed here, and fix our default to const_cast.
Fix allocate_shared to remove const before calling construct, which
changes the experience for C++17 customers because allocate_shared constructs a T
instead of a const T, but not in a way substantially different to edits
P0674 already made here.
Back out allocate_shared's interaction with this part of the allocator model (reverting
this part of P0674 and reopening LWG 3008(i)).
Go around the problem by prohibiting allocate_shared<const T>, which breaks
existing C++17 customers.
Billy O'Neal argues that only (2) preserves the design intent P0674 while maintaining compatibility for most allocators and most C++17 customers.
Peter Dimov argues that (1) isn't likely to break enough to matter.[2019-06-16 Priority set to 3 based on reflector discussion]
Previous resolution [SUPERSEDED]:
This wording is relative to N4810.
[Drafting note: As the issue submitter prefers option (2), this is wording for that.]
Modify 20.3.2.2.7 [util.smartptr.shared.create] as indicated:
template<class T, ...> shared_ptr<T> make_shared(args); template<class T, class A, ...> shared_ptr<T> allocate_shared(const A& a, args); template<class T, ...> shared_ptr<T> make_shared_default_init(args); template<class T, class A, ...> shared_ptr<T> allocate_shared_default_init(const A& a, args);-2- Requires: […]
[…] -7- Remarks:
(7.1) — […]
[…]
(7.5) — When a (sub)object of a non-array type
Uis specified to have an initial value ofv, orU(l...), wherel...is a list of constructor arguments,allocate_sharedshall initialize this (sub)object via the expression
(7.5.1) —
allocator_traits<A2>::construct(a2, pv, v)or(7.5.2) —
allocator_traits<A2>::construct(a2, pv, l...)respectively, where
pvpoints to storage suitable to hold an object of typeremove_cv_t<U>anda2of typeA2is a rebound copy of the allocatorapassed toallocate_sharedsuch that itsvalue_typeisremove_cv_t<U>.
[2024-04-13; Jiang An comments and provides improved wording]
The currently proposed resolution is meaningless, because "(allocated) storage suitable to hold an object of type
remove_cv_t<U>" is always "storage suitable to hold an object of type U", and vice versa.
Also, the current specification doesn't seem to specify the type of pv in the cases of allocator_shared,
because pv is merely specified to point some storage instead of an object.
[2024-10-02; will be resolved by issue 3216(i).]
Proposed resolution:
This wording is relative to N4971.
Modify 20.3.2.2.7 [util.smartptr.shared.create] as indicated:
[Drafting note: As the issue submitter prefers option (2), this is wording for that.]
template<class T, ...> shared_ptr<T> make_shared(args); template<class T, class A, ...> shared_ptr<T> allocate_shared(const A& a, args); template<class T, ...> shared_ptr<T> make_shared_for_overwrite(args); template<class T, class A, ...> shared_ptr<T> allocate_shared_for_overwrite(const A& a, args);-2- Preconditions: […]
[…] -7- Remarks:
(7.1) — […]
[…]
(7.5) — When a (sub)object of a non-array type
Uis specified to have an initial value ofv, orU(l...), wherel...is a list of constructor arguments,allocate_sharedshall initialize this (sub)object via the expression
(7.5.1) —
allocator_traits<A2>::construct(a2, pv, v)or(7.5.2) —
allocator_traits<A2>::construct(a2, pv, l...)respectively, where
pvhas typeremove_cv_t<U>*and points to storage suitable to hold an object of typeUanda2of typeA2is a rebound copy of the allocator a passed toallocate_sharedsuch that itsvalue_typeisremove_cv_t<U>.(7.6) — […]
(7.7) — When a (sub)object of non-array type
Uis specified to have a default initial value,allocate_sharedshall initialize this (sub)object via the expressionallocator_traits<A2>::construct(a2, pv), wherepvhas typeremove_cv_t<U>*and points to storage suitable to hold an object of typeUanda2of typeA2is a rebound copy of the allocator a passed toallocate_sharedsuch that itsvalue_typeisremove_cv_t<U>.
Section: 28.3.4.3.2.3 [facet.num.get.virtuals] Status: New Submitter: Jonathan Wakely Opened: 2019-06-03 Last modified: 2019-08-23
Priority: 4
View other active issues in [facet.num.get.virtuals].
View all other issues in [facet.num.get.virtuals].
View all issues with New status.
Discussion:
28.3.4.3.2.3 [facet.num.get.virtuals] paragraph 4 says:
"Digit grouping is checked. That is, the positions of discarded separators is examined for consistency with
use_facet<numpunct<charT>>(loc).grouping(). If they are not consistent thenios_base::failbitis assigned toerr."
It's unclear what is considered consistent or not.
Obviously if the expected grouping is "1,234,567" then an input of "1,234,567" is consistent. Libstdc++, MSVC and Boost all consider "1234567" to be consistent with an expected grouping "1,234,567" (and it looks like libc++ is going to agree soon). That can be justified by saying that there are no discarded separators to examine, so no inconsistency. But what about "1234,567"? There is only one discarded separator here, and its position is consistent with the expected format. The wording should clarify that if there are no separators at all, that is OK. If there are one or more separators then they must be at the expected positions, and there must not be any missing.[2019-07 Issue Prioritization]
Priority to 4 after discussion on the reflector.
Proposed resolution:
variant default constructor has vague constexpr requirementsSection: 22.6.3.2 [variant.ctor] Status: New Submitter: Louis Dionne Opened: 2019-06-04 Last modified: 2020-09-06
Priority: 2
View other active issues in [variant.ctor].
View all other issues in [variant.ctor].
View all issues with New status.
Discussion:
In 22.6.3.2 [variant.ctor] p5, we say:
Remarks: This function shall be
constexprif and only if the value-initialization of the alternative typeT0would satisfy the requirements for a constexpr function. […]
First of all, I find it confusing that we say "This function shall be constexpr if […]",
when the declaration of the function clearly has the constexpr keyword on it
unconditionally. Instead, I would use the wording "This function shall be usable in a constexpr context
if […]".
constexpr-friendly as an extension. Instead, it seems better to just say "if".
Finally, I think the condition under which the function must be constexpr-friendly is not
something we can test for because it says "value-initialization of the alternative type
T0 would satisfy the requirements for a constexpr function", which doesn't imply
the value initialization can actually be be performed inside a constexpr context (for example the
default constructor could be constexpr friendly but not marked with the
constexpr keyword).
[2017-06-17, Tim Song comments]
This issue is related to LWG 2833(i).
[2019-07 Issue Prioritization]
Priority to 2 after discussion on the reflector.
Previous resolution from Daniel [SUPERSEDED]:This wording is relative to N4810.
Modify 22.6.3.2 [variant.ctor] as indicated:
constexpr variant() noexcept(see below);-2- Effects: […]
-3- Ensures: […] -4- Throws: […] -5- Remarks: This function shall be usable in a context that requires constant evaluation if the alternative typeT0can be value-initialized in a context that requires constant evaluation. […]constexprif and only if the value-initialization of the alternative typeT0would satisfy the requirements for a constexpr function
[2020-06-08 Nina Dinka Ranns comments]
The revised wording provided by LWG 2833(i) should resolve this issue as well.
Proposed resolution:
<memory> and <execution> should define __cpp_lib_parallel_algorithmSection: 17.3.1 [support.limits.general] Status: New Submitter: Jonathan Wakely Opened: 2019-06-12 Last modified: 2020-09-06
Priority: 3
View all other issues in [support.limits.general].
View all issues with New status.
Discussion:
There are parallel overloads of algorithms in <memory>, so it should define the macro.
Also, <execution> defines the exec policies for use with the algos, so that should
define the macro too.
[2019-07 Issue Prioritization]
Priority to 3 after discussion on the reflector.
Proposed resolution:
This wording is relative to N4810.
Modify 17.3.1 [support.limits.general], Table 36 — "Standard library feature-test macros", as indicated:
Table 36 — Standard library feature-test macros Macro name Value Header(s) […]__cpp_lib_parallel_algorithm201603L<algorithm> <execution> <memory> <numeric>[…]
std::array overview container requirements are incorrectSection: 23.3.3.1 [array.overview], 23.2.2 [container.requirements.general] Status: New Submitter: Nevin Liber & Christian Trott Opened: 2019-06-13 Last modified: 2022-04-24
Priority: 3
View other active issues in [array.overview].
View all other issues in [array.overview].
View all issues with New status.
Discussion:
The requirements specified in 23.3.3.1 [array.overview] p3 are incorrect; namely:
A default constructed array<T, N> where 0 < N has linear,
not constant complexity.
A default constructed array<T, 0> is empty and has constant complexity.
[2019-07 Issue Prioritization]
Priority to 3 after discussion on the reflector.
Previous resolution [SUPERSEDED]:
This wording is relative to N4810.
Modify 23.2.2 [container.requirements.general], Table 62 — "Container requirements", as indicated (This table can be identified by the "section" identifier [tab:container.req] in the next working draft):
Table 62 — Container requirements Expression Return type Operational
semanticsAssertion/note
pre/post-conditionComplexity […]X u;Ensures: !u.empty()forarray<T, N>where0 < N, and
Ensures:u.empty()for all other standard containers.constant(Note A)X()Ensures: !X().empty()forarray<T, N>where0 < N, and
Ensures:X().empty()for all other standard containers.constant(Note A)[…]Those entries marked "(Note A)" or "(Note B)" have linear complexity for
array<T, N>where0 < Nand have constant complexity for all other standard containers.Modify 23.3.3.1 [array.overview] as indicated:
-2- An
-3- Anarrayis an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up toNelements whose types are convertible toT.array<T, 0>satisfies all of the requirements of a container and of a reversible container (23.2 [container.requirements]). Anarray<T, N>where0 < Nsatisfies all of the requirements of a container and of a reversible container (23.2 [container.requirements]), except that a default constructedarray<T, N>object is not empty andthatboth default construction andswapdoes nothaveconstantlinear complexity. Anarraysatisfies some of the requirements of a sequence container (23.2.4 [sequence.reqmts]). Descriptions are provided here only for operations onarraythat are not described in one of these tables and for operations where there is additional semantic information.
[2022-04-24; Daniel rebases wording on N4910]
Proposed resolution:
This wording is relative to N4910.
Modify 23.2.2.2 [container.reqmts] as indicated:
X u; X u = X();[…]-10- Postconditions:
-11- Complexity:!u.empty()forarray<T, N>where0 < N, andu.empty()for all other standard containers.ConstantLinear forarray<T, N>where0 < Nand constant for all other standard containers.X u(rv); X u = rv;[…]-15- Postconditions:
-11- Complexity: Linear foruis equal to the value thatrvhad before this construction.array<T, N>where0 < Nand constant for all other standard containers.a.swap(b)-45- Result:
-46- Effects: Exchanges the contents ofvoidaandb. -47- Complexity: Linear forarray<T, N>where0 < Nand constant for all other standard containers.
Modify 23.3.3.1 [array.overview] as indicated:
-2- An
-3- Anarrayis an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up toNelements whose types are convertible toT.array<T, 0>meets all of the requirements of a container (23.2.2.2 [container.reqmts]) and of a reversible container (23.2.2.3 [container.rev.reqmts]). Anarray<T, N>where0 < Nmeets all of the requirements of a container (23.2.2.2 [container.reqmts]) and of a reversible container (23.2.2.3 [container.rev.reqmts]), except that a default constructedarray<T, N>object is not empty ifN > 0and default construction, move construction, andswaphave linear complexity ifN > 0. Anarraymeets some of the requirements of a sequence container (23.2.4 [sequence.reqmts]). Descriptions are provided here only for operations onarraythat are not described in one of these tables and for operations where there is additional semantic information.
shared_ptrSection: 32.5.2 [atomics.syn] Status: New Submitter: Casey Carter Opened: 2019-06-11 Last modified: 2020-09-06
Priority: 3
View other active issues in [atomics.syn].
View all other issues in [atomics.syn].
View all issues with New status.
Discussion:
This well-formed C++14 program:
#include <atomic>
#include <memory>
struct Abstract { virtual void test() = 0; };
struct Concrete : Abstract { virtual void test() override {} };
int main() {
std::shared_ptr<Abstract> ptr;
std::atomic_store<Abstract>(&ptr, std::make_shared<Concrete>());
}
is ill-formed in C++17. P0558 changed the non-member
non-shared_ptr atomic functions to avoid deducing from their second argument,
e.g. C++14 atomic_store:
template<class T> void atomic_store(atomic<T>*, T); // #1
became C++17 atomic_store:
template<class T> void atomic_store(atomic<T>*, typename atomic<T>::value_type); // #2
The program intends to call the "other" atomic_store from
99 [depr.util.smartptr.shared.atomic]:
template<class T> void atomic_store(shared_ptr<T>*, shared_ptr<T>); // #3
In C++14, the call expression in the sample program —
std::atomic_store<Abstract>(&ptr, std::make_shared<Concrete>()) —
selects overload #3; overload #1 fails to be viable due to the
lack of conversions from shared_ptr<Abstract>* to atomic<Abstract>*
and from shared_ptr<Concrete> to Abstract. In C++17, overload
#2 doesn't get to the point of considering argument conversions: when we try to
generate the declaration of the specialization for T = Abstract we must instantiate
atomic<Abstract> in order to substitute typename atomic<Abstract>::value_type,
but doing so violates the requirement in [atomics.types.generic] p1 that "The type of the template
argument T shall be trivially copyable"
atomic<T>::value_type is always an alias
for T: for those non-member atomic functions with overloads defined in
99 [depr.util.smartptr.shared.atomic], use a different form to require that T
in the type of the second parameter is non-deduced.
[2019-07 Issue Prioritization]
Priority to 3 after discussion on the reflector.
Proposed resolution:
This wording is relative to N4810.
Modify 32.5.2 [atomics.syn], header <atomic> synopsis, as indicated:
[…] // 32.5.9 [atomics.nonmembers], non-member functions […] template<class T> void atomic_store(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> void atomic_store(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> void atomic_store_explicit(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order) noexcept; template<class T> void atomic_store_explicit(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order) noexcept; […] template<class T> T atomic_exchange(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> T atomic_exchange(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> T atomic_exchange_explicit(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order) noexcept; template<class T> T atomic_exchange_explicit(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order) noexcept; template<class T> bool atomic_compare_exchange_weak(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> bool atomic_compare_exchange_weak(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> bool atomic_compare_exchange_strong(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> bool atomic_compare_exchange_strong(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>) noexcept; template<class T> bool atomic_compare_exchange_weak_explicit(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_weak_explicit(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_strong_explicit(volatile atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_strong_explicit(atomic<T>*,typename atomic<T>::value_typetype_identity_t<T>*,typename atomic<T>::value_typetype_identity_t<T>, memory_order, memory_order) noexcept;
Modify 32.5.9 [atomics.nonmembers] as indicated:
-1- A non-member function template whose name matches the pattern
atomic_for the patternatomic_f_explicitinvokes the member functionf, with the value of the first parameter as the object expression and the values of the remaining parameters (if any) as the arguments of the member function call, in order. An argument for a parameter of typeatomic<T>::value_type*ortype_identity_t<T>*is dereferenced when passed to the member function call. If no such member function exists, the program is ill-formed.
extract in ordered and unordered associative containersSection: 23.2.7 [associative.reqmts], 23.2.8 [unord.req] Status: New Submitter: Konstantin Boyarinov Opened: 2019-06-25 Last modified: 2022-04-24
Priority: 3
View other active issues in [associative.reqmts].
View all other issues in [associative.reqmts].
View all issues with New status.
Discussion:
Ordered and unordered associative containers in C++14 contained an issue, which caused an ambiguity
while invoking std::map::erase when key_type of the map can be constructed from
the iterator. In this case both overloads erase(const key_type&) and
erase(const_iterator) could be chosen.
erase in ordered and unordered associative containers which accepts iterator
as an argument.
C++17 also introduced new functionality for splicing ordered and unordered maps and sets.
One of the extensions allows to extract a node from the container by passing either
key_type& or const_iterator to the extract() member function:
node_type extract(const key_type& x); node_type extract(const_iterator position);
Providing these two extract overloads causes the same problem as for erase.
Consider the following example:
#include <map>
#include <string>
struct Key
{
template <typename T>
Key(const T&) {}
};
bool operator<(const Key&, const Key&) { return false; }
int main()
{
using map_type = std::map<Key, std::string>;
map_type m({ {Key(1), "a"}, {Key(2), "b"} });
map_type::iterator it = m.begin();
auto nh = m.extract(it);
}
In this case, call to extract() is ambiguous, because the overloads which accept
const_iterator and key_type are equally good matches for the argument
it.
std::map::erase
by adding an overload for extract which accepts iterator as an argument.
[2019-07 Issue Prioritization]
Priority to 3 after discussion on the reflector.
Previous resolution [SUPERSEDED]:
This wording is relative to N4820.
Modify [tab:container.assoc.req], Table 69 — "Associative container requirements", as indicated:
Table 69 — Associative container requirements (in addition to container) [tab:container.assoc.req] Expression Return type Assertion/note pre-/post-condition Complexity …a.extract(q)node_typeEffects: Removes the element
pointed to byq.
Returns: Anode_typeowning
that element.amortized constant a.extract(r)node_typeEffects: Removes the element
pointed to byr.
Returns: Anode_typeowning
that element.amortized constant …Modify [tab:container.assoc.req], Table 70 — "Unordered associative container requirements", as indicated:
Table 70 — Unordered associative container requirements (in addition to container) [tab:container.hash.req] Expression Return type Assertion/note pre-/post-condition Complexity …a.extract(q)node_typeEffects: Removes the element
pointed to byq.
Returns: Anode_typeowning
that element.Average case 𝒪(1), worst case𝒪(a.size()).a.extract(r)node_typeEffects: Removes the element
pointed to byr.
Returns: Anode_typeowning
that element.Average case 𝒪(1), worst case𝒪(a.size()).…Modify 23.4.3.1 [map.overview], class template
mapsynopsis, as indicated:[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]Modify 23.4.4.1 [multimap.overview], class template
multimapsynopsis, as indicated:[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]Modify 23.4.6.1 [set.overview], class template
setsynopsis, as indicated:[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]Modify 23.4.7.1 [multiset.overview], class template
multisetsynopsis, as indicated:[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]Modify 23.5.3.1 [unord.map.overview], class template
unordered_mapsynopsis, as indicated:[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]Modify 23.5.4.1 [unord.multimap.overview], class template
unordered_multimapsynopsis, as indicated:[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]Modify 23.5.6.1 [unord.set.overview], class template
unordered_setsynopsis, as indicated:[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]Modify 23.5.7.1 [unord.multiset.overview], class template
unordered_multisetsynopsis, as indicated:[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]
[2022-04-24; Daniel rebases wording on N4910]
Proposed resolution:
This wording is relative to N4910.
Modify 23.2.7.1 [associative.reqmts.general] as indicated:
a.extract(q)-108- Result:
-109- Effects: Removes the element pointed to bynode_typeq. -110- Returns: Anode_typeowning that element. -111- Complexity: Amortized constant.a.extract(r)-?- Result:
-?- Effects: Removes the element pointed to bynode_typer. -?- Returns: Anode_typeowning that element. -?- Complexity: Amortized constant.
Modify 23.2.8.1 [unord.req.general] as indicated:
a.extract(q)-141- Result:
-142- Effects: Removes the element pointed to bynode_typeq. -143- Returns: Anode_typeowning that element. -144- Complexity: Average case𝒪(1), worst case𝒪(a.size()).a.extract(r)-?- Result:
-?- Effects: Removes the element pointed to bynode_typer. -?- Returns: Anode_typeowning that element. -?- Complexity: Average case𝒪(1), worst case𝒪(a.size()).
Modify 23.4.3.1 [map.overview], class template map synopsis, as indicated:
[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]
Modify 23.4.4.1 [multimap.overview], class template multimap synopsis, as indicated:
[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]
Modify 23.4.6.1 [set.overview], class template set synopsis, as indicated:
[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]
Modify 23.4.7.1 [multiset.overview], class template multiset synopsis, as indicated:
[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]
Modify 23.5.3.1 [unord.map.overview], class template unordered_map synopsis, as indicated:
[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]
Modify 23.5.4.1 [unord.multimap.overview], class template unordered_multimap
synopsis, as indicated:
[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]
Modify 23.5.6.1 [unord.set.overview], class template unordered_set
synopsis, as indicated:
[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]
Modify 23.5.7.1 [unord.multiset.overview], class template unordered_multiset
synopsis, as indicated:
[…] node_type extract(iterator position); node_type extract(const_iterator position); node_type extract(const key_type& x); […]
Section: 16.4.6.14 [res.on.exception.handling] Status: New Submitter: Nevin Liber Opened: 2019-06-28 Last modified: 2023-01-29
Priority: 3
View other active issues in [res.on.exception.handling].
View all other issues in [res.on.exception.handling].
View all issues with New status.
Discussion:
16.4.6.14 [res.on.exception.handling]#3 says:
Destructor operations defined in the C++ standard library shall not throw exceptions. Every destructor in the C++ standard library shall behave as if it had a non-throwing exception specification.
However, types like pair and array have implicitly declared destructors,
where 14.5 [except.spec]#8 applies:
The exception specification for an implicitly-declared destructor, or a destructor without a noexcept-specifier, is potentially-throwing if and only if any of the destructors for any of its potentially constructed subobjects is potentially-throwing or the destructor is virtual and the destructor of any virtual base class is potentially throwing.
We can relax 16.4.6.14 [res.on.exception.handling] to only cover explicitly declared destructors because if they hold a user type where the destructor throws then we get UB from 16.4.5.8 [res.on.functions]#2:
In particular, the effects are undefined in the following cases: […]
[…]
(2.4) — if any replacement function or handler function or destructor operation exits via an exception, unless specifically allowed in the applicable Required behavior: paragraph.
[…]
and the referred to UB happens before [res.on.exception.handling] could apply.
[2019-07 Issue Prioritization]
Priority to 3 after discussion on the reflector.
[2023-01-29; Daniel comments]
This issue has very much overlap with LWG 3854(i).
Proposed resolution:
This wording is relative to N4820.
Modify 16.4.6.14 [res.on.exception.handling] as indicated:
-3- Destructor operations defined in the C++ standard library shall not throw exceptions. Every explicitly declared destructor in the C++ standard library shall behave as if it had a non-throwing exception specification.
Section: 16.4.3.2 [using.headers] Status: New Submitter: Alisdair Meredith Opened: 2019-07-24 Last modified: 2020-04-07
Priority: 3
View all other issues in [using.headers].
View all issues with New status.
Discussion:
Quoting 16.4.3.2 [using.headers] p3:
"[…] and shall include the header lexically before the first reference in that translation unit to any of the entities declared in that header."
This suggests we may be able to use macros and typedefs (like size_t) declared in
standard headers without the corresponding #include. Clearly that is not the intended
behavior!
std::begin etc.) being declared
in multiple headers.
It may be simpler to turn this sentence around, along the lines of:
"No part of the standard library shall be used in a translation unit prior to a including or importing a header that provides that feature."
Even here, 'used' may be a problematic term of art. Perhaps "named"?
[2020-04-07 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
regex components' noexcept annotations appear broken for POCMA or throwing
BidirectionalIteratorSection: 28.6.7 [re.regex], 28.6.9 [re.results] Status: New Submitter: Billy O'Neal III Opened: 2019-08-17 Last modified: 2019-10-07
Priority: 3
View all other issues in [re.regex].
View all issues with New status.
Discussion:
std::basic_regex and std::match_results have noexcept move construction, and
std::basic_regex has noexcept move assignment, but both of them have throwing swaps. We
probably need an Expects: or something to say that BidirectionalIterator doesn't
throw through these operations. We probably also need match_results::operator= to
respect propagate_on_container_move_assignment (and maybe the copy ctor respect
propagate_on_container_copy_assignment).
[2019-09-02; Tim Song comments]
The issue is related to LWG 2490(i).
[2019-10 Priority set to 3 after reflector discussion]
Proposed resolution:
Section: 32.5.6 [atomics.wait] Status: New Submitter: Geoffrey Romer Opened: 2019-08-19 Last modified: 2020-09-06
Priority: 3
View other active issues in [atomics.wait].
View all other issues in [atomics.wait].
View all issues with New status.
Discussion:
It appears that in a conforming implementation, all but one wait() call on a given
atomic object may block forever, regardless of any notify_one() calls, because in
principle every notify_one() call could be considered to unblock the same single
wait() call. Common sense suggests (and David Olsen confirms) that the intent is
for each waiting function call to be (non-spuriously) unblocked by at most one notifying
function call, but as far as I can tell the words never say that.
[2019-09-14 Priority set to 3 based on reflector discussion]
Proposed resolution:
This wording is relative to N4830.
Modify 32.5.6 [atomics.wait] as indicated:
-?- All blocking and unblocking events on a single atomic object occur in a single total order that is consistent with the "happens before" partial order.
-4- A call to an atomic waiting operation on an atomic objectMis eligible to be unblocked by a call to an atomic notifying operation onMif it has not been unblocked, and there exist side effectsXandYonMsuch that:
(4.1) — the atomic waiting operation has blocked after observing the result of
X,(4.2) —
XprecedesYin the modification order ofM, and(4.3) —
Yhappens before the call to the atomic notifying operation.
is_always_equalSection: 16.4.4.6 [allocator.requirements] Status: New Submitter: FrankHB1989 Opened: 2019-08-27 Last modified: 2023-01-14
Priority: 4
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with New status.
Discussion:
[allocator.requirements] does not mention the interaction between is_always_equal
and allocator rebinding. As the result, a rebound allocator may have different
is_always_equal::value to the original allocator.
X satisfying
std::allocator_type<X>::is_always_equal::value == true, rebound allocators
of X with same type are not guaranteed equal.
Consider:
X is used as an allocator for value_type used in a node-based container;
Y is the rebound allocator type for the node type used in the implementation;
b1 and b2 are values of Y from different allocator objects.
Then, std::allocator_type<X>::is_always_equal::value == true does not necessarily
imply b1 == b2.
is_always_equal
of allocators for their value_type (notably, in the exception specification of the move
assignment), this can cause subtle problems.
In general, the implementation of the move assignment operator of such a container can not avoid
allocation for new nodes when !std::allocator_traits<Y>::propagate_on_container_move_assignment::value
&& b1 != b2. This can throw, and it can clash with the required exception specification
based on std::allocator_traits<value_type>::is_always_equal:
#include <utility>
#include <memory>
#include <new>
#include <map>
#include <functional>
#include <type_traits>
using K = int;
using V = int;
using P = std::pair<const K, V>;
bool stop_alloc;
template<typename T>
struct AT
{
using value_type = T;
std::shared_ptr<void> sp = {};
template<typename U>
struct rebind
{
using other = AT<U>;
};
using is_always_equal = std::is_same<T, P>;
AT() : sp(is_always_equal::value ? nullptr : new T*()) {}
AT(const AT& a) = default;
template<typename U>
AT(const AT<U>& a) noexcept : sp(a.sp) {}
T* allocate(std::size_t size)
{
if (stop_alloc)
throw std::bad_alloc();
return static_cast<T*>(::operator new(size * sizeof(T)));
}
void deallocate(T* p, std::size_t)
{
::operator delete(p);
}
friend bool operator==(const AT& x, const AT& y) noexcept
{
return !x.sp.owner_before(y.sp) && !y.sp.owner_before(x.sp);
}
friend bool operator!=(const AT& x, const AT& y) noexcept
{
return !(x == y);
}
};
using A = AT<P>;
int main()
{
// Some sanity checks:
static_assert(std::is_same_v<A::template rebind<A::value_type>::other, A>);
// For any U:
using U = int;
static_assert(std::is_same_v<A::template rebind<U>::other::template rebind<A::value_type>::other, A>);
using C = std::less<>;
using M = std::map<K, V, C, A>;
// As required by the current wording of the container move operator:
using always_equal = std::allocator_traits<A>::is_always_equal;
constexpr bool std_nothrow = always_equal::value && std::is_nothrow_move_assignable_v<C>;
static_assert(std_nothrow);
// For conforming implementations:
// static_assert(!(std_nothrow && !std::is_nothrow_move_assignable<M>::value));
M m{{K(), V()}}, m2;
auto a = m.get_allocator();
a.sp = std::make_shared<int>(42);
stop_alloc = true;
try
{
// Call terminate with conforming implementations. This does not work on libstdc++.
m2 = std::move(m);
// For libstdc++, terminate on allocator-extended move constructor call.
// M m3(std::move(m), a);
}
catch(...)
{}
}
[2019-10 Priority set to 4 after reflector discussion]
Previous resolution [SUPERSEDED]:
This wording is relative to N4830.
[Drafting note: Additional questions: Is it necessary to ensure that
XX::propagate_on_container_copy_assignment::value == YY::propagate_on_container_copy_assignment::valueistrueas well?]
Modify 16.4.4.6 [allocator.requirements], Table [tab:cpp17.allocator] "
Cpp17Allocatorrequirements" as indicated:
Table 34 — Cpp17Allocatorrequirements [tab:cpp17.allocator]Expression Return type Assertion/note
pre-/post-conditionDefault …typename
X::template
rebind<U>::otherYFor all U(includingT),
Y::templateis
rebind<T>::otherX.
XX::is_always_equal::value == YY::is_always_equal::value
istrue.See Note A,
below.…
[2022-04-24; Daniel rebases wording on N4910]
Previous resolution [SUPERSEDED]:
This wording is relative to N4910.
[Drafting note: Additional questions: Is it necessary to ensure that
XX::propagate_on_container_copy_assignment::value == YY::propagate_on_container_copy_assignment::valueistrueas well?]
Modify 16.4.4.6 [allocator.requirements] as indicated:
typename X::template rebind<U>::other-16- Result:
-17- Postconditions: For allYU(includingT),Y::template rebind<T>::otherisX.XX::is_always_equal::value == YY::is_always_equal::valueistrue. -18- Remarks: IfAllocatoris a class template instantiation of the formSomeAllocator<T, Args>, whereArgsis zero or more type arguments, andAllocatordoes not supply arebindmember template, the standardallocator_traitstemplate usesSomeAllocator<U, Args>in place ofAllocator::rebind<U>::otherby default. For allocator types that are not template instantiations of the above form, no default is provided. -19- [Note 1: The member class templaterebindofXis effectively a typedef template. In general, if the nameAllocatoris bound toSomeAllocator<T>, thenAllocator::rebind<U>::otheris the same type asSomeAllocator<U>, whereSomeAllocator<T>::value_typeisTandSomeAllocator<U>::value_typeisU. — end note]
[2023-01-08; Jiang An comments and provides improved wording]
Exception specifications of some container operations (added by N4258 and LWG 3778(i))
are specified with the propagation properties of template parameter Allocator. However, for node-based
containers and std::deque (and common implementations of std::vector<bool, A>),
rebound allocators are needed to be propagated, and common implementations are currently detecting the propagation
properties of rebound allocators.
Proposed resolution:
This wording is relative to N4917.
Modify 16.4.4.6 [allocator.requirements] as indicated:
typename X::template rebind<U>::other-16- Result:
-17- Postconditions: For allYU(includingT),Y::template rebind<T>::otherisX. All ofXX::is_always_equal::value == YY::is_always_equal::value,XX::propagate_on_container_copy_assignment::value == YY::propagate_on_container_copy_assignment::value,XX::propagate_on_container_move_assignment::value == YY::propagate_on_container_move_assignment::value, andXX::propagate_on_container_swap::value == YY::propagate_on_container_swap::valuearetrue. -18- Remarks: IfAllocatoris a class template instantiation of the formSomeAllocator<T, Args>, whereArgsis zero or more type arguments, andAllocatordoes not supply arebindmember template, the standardallocator_traitstemplate usesSomeAllocator<U, Args>in place ofAllocator::rebind<U>::otherby default. For allocator types that are not template instantiations of the above form, no default is provided. -19- [Note 1: The member class templaterebindofXis effectively a typedef template. In general, if the nameAllocatoris bound toSomeAllocator<T>, thenAllocator::rebind<U>::otheris the same type asSomeAllocator<U>, whereSomeAllocator<T>::value_typeisTandSomeAllocator<U>::value_typeisU. — end note]
memory_order::memory_order_foo broken in C++20Section: 32.5.4 [atomics.order] Status: New Submitter: Eric Fiselier Opened: 2019-08-31 Last modified: 2020-09-06
Priority: 4
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with New status.
Discussion:
P0439R0 renamed the std::memory_order enumerators
when making it a scoped enumeration. The paper makes the old unscoped names available in the
global namespace, but not within the scope of the enumeration.
std::memory_order::memory_order_consume is no longer well-formed but
std::memory_order::consume and std::memory_order_consume are.
In order to prevent unnecessary breakage, we should re-add the memory_order_foo names
to the enumeration.
[2019-10 Priority set to 4 after reflector discussion]
Proposed resolution:
This wording is relative to N4830.
Add a new sub-clause at the end of Clause D [depr] as indicated:
D.?? Deprecated
-?- The following enumerators are declared in addition to those specified in 32.5.4 [atomics.order]:memory_orderenumeratorsnamespace std { enum class memory_order : unspecified { memory_order_relaxed = relaxed, memory_order_consume = consume, memory_order_acquire = acquire, memory_order_release = release, memory_order_acq_rel = acq_rel, memory_order_seq_cst = seq_cst }; }
time_get::do_get require a valid pointer when none of the others do?Section: 28.3.4.6.2.3 [locale.time.get.virtuals] Status: New Submitter: Marshall Clow Opened: 2019-09-09 Last modified: 2020-09-06
Priority: 3
View other active issues in [locale.time.get.virtuals].
View all other issues in [locale.time.get.virtuals].
View all issues with New status.
Discussion:
According to 28.3.4.6.2.3 [locale.time.get.virtuals] p11:
Requires:
tshall point to an object
[Note: In my "Mandates" paper, I changed this to "Expects: t points to an object"]
time_get::get does not have any such stated requirement,
and it calls do_get. None of the other "time" calls in time_get have such a (stated)
requirement.
I believe that this requirement is redundant, that it is implied by the wording in P12 and P14.
P12: "or until it has extracted and assigned those struct tm members"
P14: "It is unspecified whether multiple calls to do_get() with the address of the same
struct tm object will update the current contents of the object or simply overwrite its members."
If the pointer is invalid (null, or points to unmapped memory, say), you've got UB anyway.
All the other calls in [locale.time.get.virtuals] were from C++98. do_get_time was added in C++11,
and p11 originally said "t shall be dereferenceable".
This was changed to "t shall point to an object" as part of the resolution of CWG issue
342
[2019-10 Priority set to 3 after reflector discussion]
Proposed resolution:
This wording is relative to N4830.
Modify 28.3.4.6.2.3 [locale.time.get.virtuals] as indicated:
iter_type do_get(iter_type s, iter_type end, ios_base& f, ios_base::iostate& err, tm* t, char format, char modifier) const;-12- Effects: […]
-11- Requires:tshall point to an object.
cpp17-input-iterator concept is needlessly complexSection: 24.3.2.3 [iterator.traits] Status: New Submitter: Eric Niebler Opened: 2019-09-10 Last modified: 2020-09-06
Priority: 3
View all other issues in [iterator.traits].
View all issues with New status.
Discussion:
The new C++20 iterator concepts use common_reference to constrain the value, reference, and
rvalue_reference associated types in order to support proxy references (see
24.3.4.2 [iterator.concept.readable]).
common_reference in
24.3.2.3 [iterator.traits]/p2 is needlessly complex. The common_reference constraints can be
replaced with simple convertibility requirements to a const lvalue reference to the value type.
This fix has been implemented in range-v3.
[2019-10-14 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
This wording is relative to N4830.
Modify 24.3.2.3 [iterator.traits] as indicated:
-2- The definitions in this subclause make use of the following exposition-only concepts:
template<class I> concept cpp17-iterator = copyable<I> && requires(I i) { { *i } -> can-reference; { ++i } -> same_as<I&>; { *i++ } -> can-reference; }; template<class I> concept cpp17-input-iterator = cpp17-iterator<I> && equality_comparable<I> && requires(I i) { typename incrementable_traits<I>::difference_type; typename readable_traits<I>::value_type;typename common_reference_t<iter_reference_t<I>&&, typename readable_traits<I>::value_type&>; typename common_reference_t<decltype(*i++)&&, typename readable_traits<I>::value_type&>;{ *i } -> convertible_to<const typename readable_traits<I>::value_type&>; { *i++ } -> convertible_to<const typename readable_traits<I>::value_type&>; requires signed_integral<typename incrementable_traits<I>::difference_type>; }; […]
atomic<T>::notify_one is unimplementableSection: 32.5.6 [atomics.wait] Status: New Submitter: Anthony Williams Opened: 2019-09-11 Last modified: 2020-09-06
Priority: 2
View other active issues in [atomics.wait].
View all other issues in [atomics.wait].
View all issues with New status.
Discussion:
I am concerned by the wording around atomic<T>::wait()/atomic<T>::notify_one().
wait() observed a
value X prior to the value Y which results from a store that happens-before
the notify in order to be eligible to be unlocked.
I am not sure how to implement that.
atomic<int> a = 0; T1: int ra=a, read 0 T1: a.wait(0) T2: a=42 T3: int ra=a, read 42 T3: a.wait(42) T2: a.notify_one()
The wording requires that T1 is eligible to be unlocked, but not T3, as
there is not a write after the value read by T3 that happens-before
the notify.
T1 and T3 are waiting, so T3 may be woken by the OS.
Waking T3 is allowed (wait() says it may wake spuriously), but waking T1
is currently required as it is the only thread "eligible to be unblocked".
This requires notify_one() to wake all waiters, which defeats the purpose.
I suspect we need to change 32.5.6 [atomics.wait] p4.
How about:
"A call to an atomic waiting operation
Won an atomic objectMis eligible to be unlocked by a call to an atomic notifying operationNonMif"
Ndoes not happen-beforeWThere are no side effects
XandYin the modification order ofMsuch thatNhappens-beforeX,XprecedesYin the modification order ofMand an atomic operation that observes the effects ofYhappens-beforeW.
This would allow T3 to be woken in the preceding example, but prevent it
being woken in the following case:
T1: int ra=a, read 0 T1: a.wait(0) T2: a=42 T2: a.notify_one() T2: a=69 T3: int ra=a, read 69 T3: a.wait(69)
[2020-07-17; Priority set to 2 in telecon]
Proposed resolution:
This wording is relative to N4830.
Modify 32.5.6 [atomics.wait] as indicated:
-4- A call to an atomic waiting operation
Won an atomic objectMis eligible to be unblocked by a call to an atomic notifying operationNonMifthere exist side effectsXandYonMsuch that:
(4.1) —
Ndoes not happen beforeWthe atomic waiting operation has blocked after observing the result of,X(4.2) — There are no side effects
XandprecedesYin the modification order ofM, andsuch thatNhappens beforeX,XprecedesYin the modification order ofMand an atomic operation that observes the effects ofYhappens beforeW.
(4.3) —Yhappens before the call to the atomic notifying operation.
Section: 23.2.4 [sequence.reqmts] Status: New Submitter: Casey Carter Opened: 2019-09-17 Last modified: 2022-04-24
Priority: 3
View other active issues in [sequence.reqmts].
View all other issues in [sequence.reqmts].
View all issues with New status.
Discussion:
23.2.4 [sequence.reqmts] paragraph 3 says that the names i and j denote
"iterators that meet the Cpp17InputIterator requirements and refer to elements implicitly
convertible to value_type". Ignoring for the moment that this is an occurrence of LWG
3105(i) — we really mean that *i and *j must be implicitly
convertible to value_type — this requirement seems to be completely extraneous.
i and j are used in three places in the requirements table:
The range constructors X(i, j) and X u(i, j), which require that the
container's value type is Cpp17EmplaceConstructible into the container from *i;
implicit conversion is neither necessary nor sufficient.
The range insert overload a.insert(p, i, j) which also requires Cpp17EmplaceConstructible,
as well as the capability to move elements around for vector and deque; again,
implicit conversion is neither necessary nor sufficient. It would be useful / performant
here to require that the container's value type is assignable from *i, which may have
been the intent of the implicit conversion requirement — would doing so be too breaking?
The range assign overload a.assign(i, j) which requires both
Cpp17EmplaceConstructible as above and that it can assign the result of dereferencing an
iterator directly to the container's value type; again, implicit conversion is not useful here.
We should strike the implicit conversion requirement since it is not useful and only serves to confuse readers of the Standard (see e.g. here).
[2019-10-31 Issue Prioritization]
Priority to 3 after reflector discussion.
Previous resolution [SUPERSEDED]:
This wording is relative to N4830.
Modify 23.2.4 [sequence.reqmts] as indicated:
-3- In Tables 76 and 77,
Xdenotes a sequence container class,adenotes a value of typeXcontaining elements of typeT,udenotes the name of a variable being declared,AdenotesX::allocator_typeif the qualified-idX::allocator_typeis valid and denotes a type (13.10.3 [temp.deduct]) andallocator<T>if it doesn't,iandjdenote iterators that meet the Cpp17InputIterator requirementsand refer to elements implicitly convertible to,value_type[i, j)denotes a valid range, […]
[2022-04-24; Daniel rebases wording on N4910]
Proposed resolution:
This wording is relative to N4910.
Modify 23.2.4 [sequence.reqmts] as indicated:
-3- In this subclause,
(3.1) — […]
[…]
(3.5) —
iandjdenote iterators that meet the Cpp17InputIterator requirementsand refer to elements implicitly convertible to,value_type[…]
vector and deque iterator erase invalidates elements even when no change occursSection: 23.3.5.4 [deque.modifiers], 23.3.13.5 [vector.modifiers] Status: New Submitter: Billy O'Neal III Opened: 2019-10-29 Last modified: 2019-11-04
Priority: 3
View other active issues in [deque.modifiers].
View all other issues in [deque.modifiers].
View all issues with New status.
Discussion:
It seems incorrect that a container would invalidate anything as a result of being asked to erase 0 elements.
This came up in a recent customer bug report against Visual Studio, where given a vector v,
v.erase(v.begin(), v.begin()) triggered a self-assignment of all the elements in the vector.
deque has language enumerating erasures of the first and last element which invalidate fewer
iterators, and a fallback that says all iterators are invalidated, which seems to intend to be talking about middle-of-container erasures. However, erasing 0 elements isn't really a middle of container erasure.
vector says that iterators and references are invalidated after the 'point of the erase', but when 0
elements are erased it's unclear what that even means.
We should say that erasures that erase 0 elements are no ops and be clearer about which elements are
invalidated for vector.
[2019-11 Priority to 3 during Monday issue prioritization in Belfast]
Proposed resolution:
This wording is relative to N4835.
Modify 23.3.5.4 [deque.modifiers] as indicated:
iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last); void pop_front(); void pop_back();-4- Effects: Erases elements as indicated in Table 75 [tab:container.seq.req]. An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements. An erase operation that erases the first element of a deque but not the last element invalidates only iterators and references to the erased elements. An erase operation that erases any elements, but neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque.
[…][Note:pop_frontandpop_backare erase operations. — end note]
Modify 23.3.13.5 [vector.modifiers] as indicated:
constexpr iterator erase(const_iterator position); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void pop_back();-3- Effects: Erases elements as indicated in Table 75 [tab:container.seq.req]. Invalidates iterators and references at or after the
[…]point of the erase.first erased element. [Note: For the second overload oferase, iffirst == last, no elements are erased, and no iterators or references are invalidated. — end note]
<ios> implicitly #included by <sstream>, <fstream> etc.?Section: 31.8 [string.streams], 31.10 [file.streams] Status: New Submitter: Jens Maurer Opened: 2019-11-01 Last modified: 2019-11-04
Priority: 3
View all other issues in [string.streams].
View all issues with New status.
Discussion:
It is unclear whether the streams headers implicitly #include <ios> and make (for example)
the name std::basic_ios available after including <sstream>.
[2019-11 Priority to 3 during Monday issue prioritization in Belfast]
Proposed resolution:
Section: 28.3.4.2.5.3 [locale.codecvt.virtuals] Status: New Submitter: Richard Smith Opened: 2019-11-15 Last modified: 2019-11-30
Priority: 3
View other active issues in [locale.codecvt.virtuals].
View all other issues in [locale.codecvt.virtuals].
View all issues with New status.
Discussion:
In 28.3.4.2.5.3 [locale.codecvt.virtuals] paragraphs 6 and 11, we find:
Preconditions:
(to <= to_end)is well-defined andtrue;stateis initialized, if at the beginning of a sequence, or else is equal to the result of converting the preceding characters in the sequence."
This doesn't make sense. What is the value of state if we're at the beginning of a sequence?
Is the fact that we say that it's initialized in that case supposed to imply that it need not be
initialized otherwise?
[2019-11-30 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
Section: 27.4.3 [basic.string], 23.3.13 [vector], 23.5.3 [unord.map], 23.5.6 [unord.set], 23.2.2 [container.requirements.general] Status: New Submitter: Nathan Myers Opened: 2019-11-17 Last modified: 2020-09-06
Priority: 3
View other active issues in [basic.string].
View all other issues in [basic.string].
View all issues with New status.
Discussion:
The Standard leaves unspecified the capacity() of a string or vector,
and bucket_count() of an unordered_(multi)set or unordered_(multi)map,
constructed by move from an empty other.
other, normative language in the Standard constrains the new object to
use (mostly) the same storage as the other, by way of lifetime of iterators and pointers
to elements.
For an empty other, there can be no such pointers or iterators. However, the empty
container may have a non-zero capacity() or bucket_count(), and having reserved
storage there, one naturally expects that storage to be delivered to the new object in the same
way as if it had elements.
Existing implementations, in fact, do move storage to the new container, provided it can be
deallocated using the new object's allocator. It is likely that existing programs have come to
depend on this behavior.
The resolution proposed is to add language to the Standard specifying that, if the allocators
of the existing and new container objects are compatible, the storage of the new object is the
same as of the old, so that no allocations or deallocations are performed in the process, as
existing implementations in fact do.
This appears to affect only string, vector, unordered_set,
unordered_multiset, unordered_map, and unordered_multimap, but any
new container types may also need similar attention.
Note that in the case of the hashed containers, the array of buckets appears not to be required
to be moved, even when elements contained are. This seems to be a similar oversight; extant
implementations do move the bucket array. The resolution should cover this case as well.
It is expected and intended that the proposed resolution does not require changes to the behavior
of implementations.
See also LWG 2321(i) and P0966R1.
[2019-11-30 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
basic_regex range constructor: Missing requirements for iterator typesSection: 28.6.7.2 [re.regex.construct] Status: New Submitter: Денис Захаров Opened: 2019-11-17 Last modified: 2022-04-22
Priority: 3
View other active issues in [re.regex.construct].
View all other issues in [re.regex.construct].
View all issues with New status.
Discussion:
This is description of a basic_regex range constructor from N4835,
28.6.7.2 [re.regex.construct]:
template<class ForwardIterator> basic_regex(ForwardIterator first, ForwardIterator last, flag_type f = regex_constants::ECMAScript);-17- Throws:
-18- Effects: Constructs an object of classregex_errorif the sequence[first, last)is not a valid regular expression.basic_regex; the object's internal finite state machine is constructed from the regular expression contained in the sequence of characters[first, last), and interpreted according to the flags specified inf. -19- Ensures:flags()returnsf. mark_count()returns the number of marked sub-expressions within the expression.
It seems that there are no requirements about dereferenced iterator's element type, that, apparently,
must be implicitly convertible to the basic_regex::value_type. For example, containers having
range constructor satisfy a SequenceContainer requirements, where implicit converting to its elements
is specified.
[2019-11-30 Issue Prioritization]
Priority to 3 after reflector discussion.
[2021-10-04; Jonathan adds a comment]
There isn't even a requirement that the arguments are iterators.
And assign(InputIterator, InputIterator, flag_type) is not
constrained to only accept iterators either, meaning you can call it with
two integers and call the
basic_string(size_type, char_type) constructor.
[2022-04-22; Jonathan adds a comment]
As well as requiring the reference type to be convertible to charT,
we might want to consider constraining these with forward_iterator
as per P2408.
Proposed resolution:
x with y", which is underspecifiedSection: 22.3.2 [pairs.pair], 22.4.4.2 [tuple.cnstr], 22.6.3.2 [variant.ctor], 32.5.8.7.2 [util.smartptr.atomic.shared], 20.6.3 [allocator.adaptor.cnstr], 28.5.6.6 [format.parse.ctx], 28.5.8.1 [format.arg], 23.6 [container.adaptors], 24.5 [predef.iterators], 25.5.4.2 [range.subrange.ctor], 25.6 [range.factories], 25.7 [range.adaptors], 26.10 [numeric.ops], 30.9 [time.hms], 28.6.11 [re.iter], 32.5.8 [atomics.types.generic], 32.5.9 [atomics.nonmembers], 32.3 [thread.stoptoken], 32.4 [thread.threads], 32.6 [thread.mutex], 32.8 [thread.sema], 32.9 [thread.coord], 32.10 [futures] Status: New Submitter: Richard Smith Opened: 2019-11-21 Last modified: 2019-12-08
Priority: 3
View other active issues in [pairs.pair].
View all other issues in [pairs.pair].
View all issues with New status.
Discussion:
The problem was discussed here:
It seems to me that this is just one instance of a systemic problem in the library wording. This phrasing "initializes
Looking at random through the library wording, the first case I found: 23.6.3.2 [queue.cons]/1:xwithy" is common, but underspecified (and formally meaningless) — the library wording either needs to say what kind of initialization is performed, or specify an initializer (not an expression) with which to initialize. We should ask LWG to think about this; for each "initializesxwithy" utterance, the reader should know what kind of initialization we mean.Effects: Initializes
cwithcont.The meaning of this depends on whether this is direct- or copy-initialization. (It's obscure, but if
Another random sample: 26.10.7 [partial.sum]/2:Tis not Cpp17CopyInsertable into the container, it could be the case that one form of initialization works and the other does not, or that they both work and do different things.)Effects: For a non-empty range, the function creates an accumulator
accwhose type isInputIterator's value type, initializes it with*first, and assigns the result to*result.Again the difference between direct- and copy-initialization is observable here.
Perhaps the library should have blanket wording that when it says "initializes", it means by direct- or copy-initialization, and that it's unspecified which one you get (or something like that) — and someone should go through all the instances and check if any of them mean something else (I doubt this is the only case that does).
Suggestion: either
add blanket wording defining what you mean when you say "initializes x with y"
(e.g., it's unspecified whether copy-initialization or direct-initialization is performed) and make sure
that that's what's intended for all uses, or
stop using the "initializes x with y" formulation entirely, and specify the kind
of initialization on each use, or
for each such use, ensure that y is an initializer (that is, of the form "= expr"
or "(expr, expr, …)" or "= { … }" or "{ … }", and not
merely an expression)
[2019-12-08 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
unlock() and notify_all() in Effects element of notify_all_at_thread_exit() should be reversedSection: 32.7.3 [thread.condition.nonmember] Status: Open Submitter: Lewis Baker Opened: 2019-11-21 Last modified: 2023-06-13
Priority: 3
View all issues with Open status.
Discussion:
32.7.3 [thread.condition.nonmember] p2 states:
Effects: Transfers ownership of the lock associated with
lkinto internal storage and schedulescondto be notified when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed. This notification shall be as if:lk.unlock(); cond.notify_all();
One common use-cases for the notify_all_at_thread_exit() is in conjunction with
thread::detach() to allow detached threads to signal when they complete and to allow
another thread to wait for them to complete using the condition_variable/mutex pair.
notify_all_at_thread_exit(condition_variable& cond,
unique_lock<mutex> lk) makes it impossible to know when it is safe to destroy the
condition_variable in the presence of spurious wake-ups and detached threads.
For example: Consider the following code-snippet:
#include <condition_variable>
#include <mutex>
#include <thread>
int main() {
std::condition_variable cv;
std::mutex mut;
bool complete = false;
std::thread{[&] {
// do work here
// Signal thread completion
std::unique_lock lk{mut};
complete = true;
std::notify_all_at_thread_exit(cv, std::move(lk));
}}.detach();
// Wait until thread completes
std::unique_lock lk{mut};
cv.wait(lk, [&] { return complete; });
// condition_variable destroyed on scope exit
return 0;
}
This seems to an intended usage of thread::detach() and std::notify_all_at_thread_exit()
and yet this code contains a race involving the call to cv.notify_all() on the created thread,
and the destructor of the condition_variable.
T0 be the thread that executes main() and T1 be the thread created
by the std::thread construction.
T0: creates threadT1
T0: context-switched out by OS
T1: starts running
T1: acquires mutex lock
T1: setscomplete = true
T1: callsnotify_all_at_thread_exit()
T1: returns from thread-main function and runs all thread-local destructors
T1: callslk.unlock()
T1: context-switched out by OS
T0: resumes execution
T0: acquires mutex lock
T0: callscv.wait()which returns immediately ascompleteistrue
T0: returns frommain(), destroyingcondition_variable
T1: resumes execution
T1: callscv.notify_all()on a danglingcvreference (undefined behaviour)
Other sequencings are possible involving spurious wake-ups of the cv.wait() call.
cv.notify_all(). In the
presence of spurious wake-ups of a condition_variable::wait(), there is no way to know whether
or not a detached thread that called std::notify_all_at_thread_exit() has finished calling
cv.notify_all(). This means there is no portable way to know when it will be safe for the
waiting thread to destroy that condition_variable.
However, if we were to reverse the order of the calls to lk.unlock() and cond.notify_all()
then the thread waiting for the detached thread to exit would not be able to observe the completion of the
thread (in the above case, this would be observing the assignment of true to the complete
variable) until the mutex lock was released by that thread and subsequently acquired by the waiting thread
which would only happen after the completion of the call to cv.notify_all(). This would allow the
above code example to eliminate the race between a subsequent destruction of the condition-variable and
the call to cv.notify_all().
[2019-12-08 Issue Prioritization]
Priority to 3 after reflector discussion.
[2019-12-15; Daniel synchronizes wording with N4842]
[2020-02, Prague]
Response from SG1: "We discussed it in Prague. We agree it’s an error and SG1 agreed with the PR."
Previous resolution [SUPERSEDED]:
This wording is relative to N4842.
Change 32.7.3 [thread.condition.nonmember] as indicated:
void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);[…]
-2- Effects: Transfers ownership of the lock associated withlkinto internal storage and schedulescondto be notified when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed. This notification is equivalent to:lk.unlock();cond.notify_all(); lk.unlock();
[2023-06-13, Varna; Tim provides improved wording]
Addressed mailing list comments. Ask SG1 to check.
Proposed resolution:
This wording is relative to N4950.
Change 32.7.3 [thread.condition.nonmember] as indicated:
void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);[…]
-2- Effects: Transfers ownership of the lock associated withlkinto internal storage and schedulescondto be notified when the current thread exits,. This notification is sequenced after all objects of thread storage duration associated with the current thread have been destroyed. This notificationand is equivalent to:lk.unlock();cond.notify_all(); lk.unlock();
advance(i, most-negative) and prev(i, most-negative)Section: 24.4.3 [iterator.operations], 24.4.4.2 [range.iter.op.advance] Status: New Submitter: Casey Carter Opened: 2019-11-22 Last modified: 2019-12-07
Priority: 3
View other active issues in [iterator.operations].
View all other issues in [iterator.operations].
View all issues with New status.
Discussion:
ranges::advance (24.4.4.2 [range.iter.op.advance]) and std::advance
(24.4.3 [iterator.operations]) can be called with a negative count n when the
iterator argument i models bidirectional_iterator (respectively, meets the
Cpp17BidirectionalIterator requirements). In this case, they are specified to "decrement i
by -n". If n is the most-negative value of a signed integral type, the expression -n
has undefined behavior. This UB is unfortunate given that typical implementations never actually
form the expression -n. It's nonsensical to describe the effects of a function in terms
of an expression with undefined behavior, so we should either define the behavior or exclude
this case via precondition.
ranges::prev() and std::prev (24.4.3 [iterator.operations]) have a similar problem:
prev(i, n) is equivalent to:
advance(i, -n); return i;
which has undefined behavior when n is numeric_limits<T>::min() where T
is iter_difference_t<decltype(i)> (for ranges::prev) or some signed integral type
(for std::prev). There is an implicit precondition here thanks to "Effects: Equivalent
to" since the equivalent code has a precondition that n is not a most-negative value, so
this wording is not defective. We could, however, define behavior for prev regardless of the
value of n by duplicating the specification of advance and inverting the "direction" of the
operations. We should consider doing so.
[2019-12-07 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
This wording is relative to N4835.
[Drafting note: I've chosen to provide wording for the conservative "define behavior for
The occurrences of "|" in the below are math-font vertical bars (indicating absolute value). I've changed both positive and negative cases for consistency of presentation. ]advanceand leaveprevas status quo" middle ground.
Modify 24.4.3 [iterator.operations] as indicated:
template<class InputIterator, class Distance> constexpr void advance(InputIterator& i, Distance n);-2- Expects:
-3- Effects: Incrementsnis negative only for bidirectional iterators.iby|n|ifnis non-negative, and decrementsibyotherwise.-|n|
Modify 24.4.4.2 [range.iter.op.advance] as indicated:
template<input_or_output_iterator I> constexpr void ranges::advance(I& i, iter_difference_t<I> n);[…]-1- Expects: If
-2- Effects:Idoes not modelbidirectional_iterator,nis not negative.
(2.1) — If
Imodelsrandom_access_iterator, equivalent toi += n.(2.2) — Otherwise, if
nis non-negative, incrementsiby|n|.(2.3) — Otherwise, decrements
iby.-|n|template<input_or_output_iterator I, sentinel_for<I> S> constexpr iter_difference_t<I> ranges::advance(I& i, iter_difference_t<I> n, S bound);-5- Expects: […]
-6- Effects:
(6.1) — If
SandImodelsized_sentinel_for<S, I>:
(6.1.1) — If
|n| ≥ |bound - i|, equivalent toranges::advance(i, bound).:(6.1.2) — Otherwise, equivalent to
ranges::advance(i, n).(6.2) — Otherwise,
(6.2.1) — if
nis non-negative, whilebool(i != bound)istrue, incrementsibut at most|n|times.:(6.2.2) — Otherwise, while
bool(i != bound)istrue, decrementsibut at mosttimes.-|n|
locale's copy assignment operator should return locale&Section: 28.3.3.1 [locale] Status: New Submitter: Stephan T. Lavavej Opened: 2019-12-06 Last modified: 2019-12-21
Priority: 3
View all other issues in [locale].
View all issues with New status.
Discussion:
Curiously, locale's copy assignment operator currently returns const locale&. As Casey
Carter noted in microsoft/STL#268, this is:
Weird!
The only occurrence in the entire Standard Library.
Preventing locale from satisfying std::copyable.
We aren't aware of any reason for this to be const. (I observe that this hasn't changed since
N1804 on 2005-04-27 and probably goes back to C++98; I suspect that
when this was originally specified, copy assignment operators were relatively new, and conventions for
them weren't rigorously followed.)
[2019-12-21 Issue Prioritization]
Priority to 3 after reflector discussion based on the observation that we have implementation divergence.
Proposed resolution:
This wording is relative to N4842.
Modify 28.3.3.1 [locale] as indicated:
[…] ~locale(); // not virtualconstlocale& operator=(const locale& other) noexcept; template<class Facet> locale combine(const locale& other) const; […]
Modify 28.3.3.1.3 [locale.cons] as indicated:
constlocale& operator=(const locale& other) noexcept;-14- Effects: Creates a copy of
-15- Returns:other, replacing the current value.*this.
default_random_engine is overspecified for per-thread engineSection: 99 [fund.ts.v3::rand.util.randint] Status: Open Submitter: Zhihao Yuan Opened: 2019-12-10 Last modified: 2022-11-30
Priority: 3
View all issues with Open status.
Discussion:
Addresses: fund.ts.v3
Although "implementation may select this type on the basis of performance, size, quality, or any combination of such factors," but changing this typedef is an ABI-break for implementations. Specifying per-thread engine to use this typedef results in losses of performance, size, and/or quality.
Since this type is not involved inrandint facilities' interface (other than its member
typedef), the current specification should be relaxed.
[2020-01 Priority set to 3 and assigned to LEWG after review on the reflector.]
[2020-05-28; LEWG issue reviewing]
LEWG issue processing voted to reject 3357 as NAD. Status change to Open.
Reject LWG3357 as NAD SF F N A SA 1 10 4 2 1
[2022-10-19; Reflector poll]
Set status to "Tentatively NAD" based on LEWG recommendation and reflector poll.
[2022-11-30; LWG telecon]
Prefer to keep an open issue for the TS than to possibly forget to address it if this feature is proposed for the IS some day.
Proposed resolution:
This wording is relative to N4840.
Modify 11.1.1 [fund.ts.v3::rand.syn], header <experimental/random> synopsis, as indicated:
#include <random>
namespace std::experimental {
inline namespace fundamentals_v3 {
// 10.1.2.1, Function template randint
template <class IntType>
IntType randint(IntType a, IntType b);
void reseed();
void reseed(default_random_engine::result_typeuint_fast32_t value);
} // inline namespace fundamentals_v3
} // namespace std::experimental
Modify 99 [fund.ts.v3::rand.util.randint] as indicated:
-1- A separate per-thread engine of
[…]typeunspecified type that meets the requirements of random number engine (C++17 [rand.req.eng]), initialized to an unpredictable state, shall be maintained for each thread. [Note: The implementation may choose the engine type on the basis of performance, size, quality, or any combination of such factors, so as to provide at least acceptable engine behavior for relatively casual, inexpert, and/or lightweight use. — end note]default_random_engine(C++17 §29.6.5)void reseed(); void reseed(default_random_engine::result_typeuint_fast32_t value);-7- Effects: Let
-8- Postconditions: Subsequent calls togbe the per-thread engine. The first form setsgto an unpredictable state. The second form invokesg.seed(value).randintdo not depend on values produced bygbefore calling reseed. [Note:reseedalso resets any instances ofuniform_int_distributionused byrandint. — end note]
Section: 17.4.1 [cstdint.syn], 16.4.2.3 [headers] Status: New Submitter: Dawid Pilarski Opened: 2020-01-14 Last modified: 2020-01-25
Priority: 3
View all other issues in [cstdint.syn].
View all issues with New status.
Discussion:
This issue has been submitted, because the editorial change requests c++-draft-issue 3521 and c++-draft-pull request 3528 has been rejected as not being editorial changes:
Currently given wording of 17.4.1 [cstdint.syn]p2:
The header defines all types and macros the same as the C standard library header
<stdint.h>.
might be understood as intended: typedefs inside stdint.h and inside cstdint in namespace
std:: refer to the same types, but another interpretation could be, that it's understood as:
cstdint provides typedefs not in namespace
std, because it would be a different definition than one in stdint.h).
Also 16.4.2.3 [headers]p5 is non sufficiently clear:
[…] the contents of each header
cnameis the same as that of the corresponding headername.h[…]
As it doesn't say what does "same content" mean. For example is an implementation allowed to do following:
// __impl.h
typedef int __my_int;
namespace std { typedef long __my_int; }
// cname header
#include "__impl.h"
namespace std {
typedef __my_int uint32_t;
}
// name.h header
#include "__impl.h"
typedef __my_int uint32_t;
?
In this case typedef from namespacestd and from global namespace refer to different types?
Proposed change:
Apply wording, that will unambiguously make typedefs from namespace std refer to the same types
as typedefs from global namespace for all headers name.h and their corresponding headers
cname.
[2020-01-25 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
tuple_size_v/tuple_element_t should be available when tuple_size/tuple_element areSection: 22.4.2 [tuple.syn], 22.4.7 [tuple.helper] Status: New Submitter: Casey Carter Opened: 2020-01-17 Last modified: 2021-11-04
Priority: 3
View all issues with New status.
Discussion:
22.4.7 [tuple.helper]/6 makes the const/volatile/const volatile partial
specializations of tuple_size available when any of <array>, <ranges>,
<span>, or <utility> is included. 22.4.7 [tuple.helper]/8 makes the
const/volatile/const volatile partial specializations of tuple_element
available when any of those same headers is included. This leads to a couple of problems:
For users of the Standard Library, it's not helpful to have these partial specializations of class
templates available when the preferred interface — the variable template tuple_size_v and
alias template tuple_element_t — are not.
For specifiers of the Standard Library, we must update two distinct yet identical lists of headers that make this same set of templates available when adding another header.
We could solve both of these problems by coalescing the two paragraphs into one and including the variable and alias template in the set of declarations made available by the pertinent (now single) list of headers.
[2020-02-08 Issue Prioritization]
Priority to 3 after reflector discussion. Tim Song said: It’s not clear that entities only mentioned in the synopsis are "defined in this subclause".
[2021-11-04; Jonathan Wakely adds note]
The __cpp_lib_tuple_element_t macro in
17.3.2 [version.syn] should be updated too.
Proposed resolution:
This wording is relative to N4842.
Modify 22.4.2 [tuple.syn], header <tuple> synopsis, as indicated:
namespace std {
[…]
// 22.4.7 [tuple.helper], tuple helper classes
template<class T> struct tuple_size; // not defined
template<class T> struct tuple_size<const T>;
template<class T> struct tuple_size<volatile T>;
template<class T> struct tuple_size<const volatile T>;
template<class T>
inline constexpr size_t tuple_size_v = tuple_size<T>::value;
template<class... Types> struct tuple_size<tuple<Types...>>;
template<size_t I, class T> struct tuple_element; // not defined
template<size_t I, class T> struct tuple_element<I, const T>;
template<size_t I, class T> struct tuple_element<I, volatile T>;
template<size_t I, class T> struct tuple_element<I, const volatile T>;
template<size_t I, class... Types>
struct tuple_element<I, tuple<Types...>>;
template<size_t I, class T>
using tuple_element_t = typename tuple_element<I, T>::type;
// 22.4.8 [tuple.elem], element access
template<class... Types> struct tuple_size<tuple<Types...>>;
template<size_t I, class... Types>
struct tuple_element<I, tuple<Types...>>;
template<size_t I, class... Types>
constexpr tuple_element_t<I, tuple<Types...>>& get(tuple<Types...>&) noexcept;
template<size_t I, class... Types>
constexpr tuple_element_t<I, tuple<Types...>>&& get(tuple<Types...>&&) noexcept;
[…]
// 22.4.7 [tuple.helper], tuple helper classes
template<class T>
inline constexpr size_t tuple_size_v = tuple_size<T>::value;
}
Modify 22.4.7 [tuple.helper] as indicated:
20.5.6 Tuple helper classes [tuple.helper]
-?- In addition to being available via inclusion of the<tuple>header, the entities defined in this subclause [tuple.helper] are available when any of the headers<array>(23.3.2 [array.syn]),<ranges>(25.2 [ranges.syn]),<span>(23.7.2.1 [span.syn]), or<utility>(22.2.1 [utility.syn]) are included.template<class T> struct tuple_size;-1- Remarks: All specializations of
tuple_sizeshall meet the Cpp17UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a base characteristic ofintegral_constant<size_t, N>for someN.template<class... Types> struct tuple_size<tuple<Types...>> : public integral_constant<size_t, sizeof...(Types)> { }; template<size_t I, class... Types> struct tuple_element<I, tuple<Types...>> { using type = TI; };
-2- Requires:I < sizeof...(Types). The program is ill-formed ifIis out of bounds.-3- Type:TIis the type of theIthelement ofTypes, where indexing is zero-based.template<class T> struct tuple_size<const T>; template<class T> struct tuple_size<volatile T>; template<class T> struct tuple_size<const volatile T>;-4- Let
TSdenotetuple_size<T>of the cv-unqualified typeT. If the expressionTS::valueis well-formed when treated as an unevaluated operand, then each of the three templates shall meet the Cpp17UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a base characteristic ofOtherwise, they shall have no member value. -5- Access checking is performed as if in a context unrelated tointegral_constant<size_t, TS::value>TSandT. Only the validity of the immediate context of the expression is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]-6- In addition to being available via inclusion of the<tuple>header, the three templates are available when any of the headers<array>(23.3.2 [array.syn]),<ranges>(25.2 [ranges.syn]),<span>(23.7.2.1 [span.syn]), or<utility>(22.2.1 [utility.syn]) are included.template<size_t I, class T> struct tuple_element<I, const T>; template<size_t I, class T> struct tuple_element<I, volatile T>; template<size_t I, class T> struct tuple_element<I, const volatile T>;-7- Let
TEdenotetuple_element_t<I, T>of the cv-unqualified typeT. Then each of the three templates shall meet the Cpp17TransformationTrait requirements (21.3.2 [meta.rqmts]) with a member typedef type that names the following type:
(7.1) — for the first specialization,
add_const_t<TE>,(7.2) — for the second specialization,
add_volatile_t<TE>, and(7.3) — for the third specialization,
add_cv_t<TE>.-8- In addition to being available via inclusion of the<tuple>header, the three templates are available when any of the headers<array>(23.3.2 [array.syn]),<ranges>(25.2 [ranges.syn]),<span>(23.7.2.1 [span.syn]), or<utility>(22.2.1 [utility.syn]) are included.
Modify 22.4.8 [tuple.elem] as indicated:
[Drafting note: Since this issue performs colliding text changes with P1460R0, we perform similar wording changes as suggested on page 19 [tuple.helper] p2.]
20.5.7 Element access [tuple.elem]
template<class... Types> struct tuple_size<tuple<Types...>> : public integral_constant<size_t, sizeof...(Types)> { }; template<size_t I, class... Types> struct tuple_element<I, tuple<Types...>> { using type = TI; };-?- Mandates:
-?- Type:I < sizeof...(Types).TIis the type of theIthelement ofTypes, where indexing is zero-based.template<size_t I, class... Types> constexpr tuple_element_t<I, tuple<Types...>>& get(tuple<Types...>& t) noexcept; […][…]
is_nothrow_convertible consider destruction of the destination type?Section: 21.3.8 [meta.rel] Status: New Submitter: Jiang An Opened: 2020-02-10 Last modified: 2023-12-22
Priority: 3
View other active issues in [meta.rel].
View all other issues in [meta.rel].
View all issues with New status.
Discussion:
This issue was submitted after a previous editorial change request had been rejected by the project editors.
I find that all known implementations (at least msvcstl, libc++, libstdc++ and the sample in P0758R1) ofstd::is_nothrow_convertible
may be not clear enough to indicate that whether destruction of the destination type is considered (or not).
For example, given a type Weird defined as
struct Weird
{
Weird(int) noexcept {}
~Weird() noexcept(false) {}
};
Then std::is_nothrow_convertible_v<int, Weird> is false in every known implementation.
However, it seems that the conversion itself is noexcept.
[2020-02-22, Daniel comments]
This seems to be quite related to the existing issue LWG 2116(i).
[2020-03-11 Issue Prioritization]
Priority to 3 after reflector discussion.
[2023-12-22; Daniel comments]
This issue should be resolved by LWG 4028(i).
Proposed resolution:
Section: 16.3.2.4 [structure.specifications] Status: New Submitter: Casey Carter Opened: 2020-02-14 Last modified: 2020-03-11
Priority: 3
View other active issues in [structure.specifications].
View all other issues in [structure.specifications].
View all issues with New status.
Discussion:
We have quite a few occurrences of the phrase "as if by" in Effects: elements in the library specification. Is the meaning of this phrase the same as "equivalent to"? If so, we should replace occurrences of "as if by" with "equivalent to" to make it clear that the magic "Effects: Equivalent to" wording in 16.3.2.4 [structure.specifications] para 4 is intended to apply.
[2020-03-11 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
negative_binomial_distribution is unclear as a consequence of LWG 2406 resolutionSection: 29.5.9.3.4 [rand.dist.bern.negbin] Status: New Submitter: Ahti Leppänen Opened: 2020-02-17 Last modified: 2020-03-11
Priority: 3
View all other issues in [rand.dist.bern.negbin].
View all issues with New status.
Discussion:
This issue has been created because a corresponding editorial change request had been rejected.
The resolution of LWG 2406(i) added a note to the definition ofnegative_binomial_distribution:
[Note: This implies that is undefined when
p == 1. — end note]
This issue argues that the note is invalid as are the premises on which LWG 2406 was based
on. It's also argued that current normative standard text allowing p == 1 is valid
both conceptually and mathematically, and that it follows existing conventions in other software.
Why does p == 1 imply that is undefined? The only questionable factor in the definition of
seems to be that in case of
p == 1, the factor (1 - p)i leads to 00
when i == 0. While it is true that there's
no generally accepted convention
what this means, std::binomial_distribution already uses the common convention
00 == 1 (e.g. with p == 1 && t == i,
leads to 00)
Even if the term was undefined mathematically, does a non-normative note of mathematical term being undefined mean that the behaviour of the program is undefined (instead of e.g. resulting to NaN) even when no preconditions are violated?
The note has lead to unclear situation of being able to construct a
distribution object, but calling operator() might lead to undefined behaviour
even though no preconditions are violated: for example the
cppreference.com notes that
If
p == 1, subsequent calls to theoperator()overload that does not accept aparam_typeobject will cause undefined behavior.
Invalidity of premises of LWG 2406:
For
p == 1, this is "* 1^k * 0^i", so every integeri >= 0is produced with zero probability. (Let's avoid thinking about0^0.)
This is contradictory: first assuming that 0^i == 0 for all
i >= 0 (implying that 0^0 == 0), but then comments not to
think about 0^0. The very essence of the issue is interpretation of
0^0 and given the definition of binomial_distribution,
where 0^0 == 1, the claim "so every integer i >= 0 is
produced with zero probability" can be considered faulty.
Wikipedia states that
pmust be within(0, 1), exclusive on both sides.
I cannot find any mention of this in the
Wikipedia's
version as of 2014-06-02 (i.e. around the time when LWG 2406 was opened). Note that
the Wikipedia's version is not the same as in C++ standard; in Wikipedia, p
parameter is the same — i.e. the probability of success — but the integer parameter
(> 0) is number of failures, while in C++ it is the number of successes.
In the failure formulation p == 1 is indeed invalid for essentially the same reason why
p == 0 is invalid for the C++ definition (i.e. leads to
== 0 for all i).
Validity of p == 1:
[…] distribution of the number of failures in a sequence of trials with success probability
pbeforensuccesses occur.
(from
Wolfram documentation). When p == 1, this means that trial always succeeds,
so it's obvious that the probability to get 0 failures is 1, and the probability for
i > 0 failures is 0. This is exactly what the mathematical definition in
29.5.9.3.4 [rand.dist.bern.negbin] gives with convention 00 = 1
when p == 1.
Software such as
Mathematica,
Matlab
and R all
accept p == 1 for negative binomial distribution and they use the integer parameter
as number of successes like the C++ standard.
What comes to the reasons why p == 1 could have been considered invalid, it
seems that major implementations — namely
libstd++,
libc++ and
MSVC
standard library — are using std::gamma_distribution in
std::negative_binomial_distribution and passing (1 - p)/p as the second argument
of std::gamma_distribution. Case p == 1 is not checked leading to violation of
precondition of std::gamma_distribution, which requires argument to be > 0.
p == 1,
removing the note might not be the only option to consider.
[2020-03-11 Issue Prioritization]
Priority to 3 and hand over to SG6 after reflector discussion.
Proposed resolution:
atomic_ref<T>::required_alignmentSection: 32.5.7.2 [atomics.ref.ops] Status: New Submitter: Andrey Semashev Opened: 2020-02-27 Last modified: 2020-09-06
Priority: 3
View other active issues in [atomics.ref.ops].
View all other issues in [atomics.ref.ops].
View all issues with New status.
Discussion:
N4849 32.5.7.2 [atomics.ref.ops]/1
describes atomic_ref<T>::required_alignment constant as follows:
The alignment required for an object to be referenced by an atomic reference, which is at least
alignof(T).
This wording allows for an implementation to always define required_alignment
to be equal to alignof(T) and implement atomic operations using locking,
even if a lock-free implementation is possible at a higher alignment. For example,
on x86-64, atomic_ref<complex<double>> could be lock-free
only when the referred object is aligned to 16 bytes, but the above definition
allows an implementation to define required_alignment to 8 and use locking.
required_alignment reflects alignment required for lock-free
operations, if possible, and not just minimum alignment required for any kind of
implementation.
The suggested resolution is to change the wording so that it is clear
that required_alignment indicates the alignment required for lock-free
implementation, if one is possible, or alignof(T) otherwise.
Further, the note in 32.5.7.2 [atomics.ref.ops]/2 contains this sentence:
Further, whether operations on an
atomic_refare lock-free could depend on the alignment of the referenced object.
This sentence is misleading, because according to is_lock_free()
definition in 32.5.7.2 [atomics.ref.ops]/4, the lock-free property is not
allowed to depend on the alignment of a particular referenced object
(is_lock_free() must return true or false if
operations on all objects of the given type T are lock-free or
not). In other words, atomic_ref can only refer to an object aligned at
least to required_alignment and its lock-free capability cannot depend
on the actual runtime alignment of the object.
[2020-04-04 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
This wording is relative to N4849.
Modify 32.5.7.2 [atomics.ref.ops] as indicated:
static constexpr size_t required_alignment;-1- Let
-2- [Note: Hardware could require an object referenced by anAbe tThe alignment required for an object to be referenced by an atomic reference,which is at leastso thatalignof(T)is_always_lock_freeistrue. If there is no such alignment orAis less thanalignof(T),required_alignmentequalsalignof(T). Otherwise,required_alignmentequalsA.atomic_refto have stricter alignment (6.8.3 [basic.align]) than other objects of typeT.Further, whether operations on anFor example, lock-free operations onatomic_refare lock-free could depend on the alignment of the referenced object.std::complex<double>could be supported only if aligned to2*alignof(double). — end note]
std::any does not mention allocationSection: 22.7.4 [any.class] Status: New Submitter: Thomas Köppe Opened: 2020-03-04 Last modified: 2020-04-04
Priority: 3
View all other issues in [any.class].
View all issues with New status.
Discussion:
Several of the function specifications in 22.7.4 [any.class] have Throws: elements,
but those only mention "exceptions thrown by a constructor". It seems necessary for std::any
to perform dynamic allocation in general, and so in general there should be a possibility of an
exception raised by such dynamic allocation. (This may come from a user-provided
T::operator new, as far as I can tell.)
any(const any& other)
template<class T> any(T&& value)
both any(in_place_t<T>, …) overloads
any& operator=(const any& rhs)
template<class T> any& operator=(T&& rhs)
all emplace overloads
[2020-04-04 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
volatile atomic deprecationsSection: 32.5.8.2 [atomics.types.operations] Status: SG1 Submitter: Alisdair Meredith Opened: 2020-03-19 Last modified: 2020-09-06
Priority: 3
View other active issues in [atomics.types.operations].
View all other issues in [atomics.types.operations].
View all issues with SG1 status.
Discussion:
Paper P1831R1 aimed to deprecate all
the atomic overloads for volatile qualified member function of
std::atomic unless is_always_lock_free is true.
There are a few omissions in the wording.
operator++ and operator-- are correctly constrained,
but the deprecated overloads are not restored in Annex D, unlike the other
member functions. I confirmed with the paper author this is an accidental
oversight, and not an intended change of behavior for C++20.
Secondly, the wait/notify APIs were added after the initial wording
For this paper was drafted, and the paper did not catch up. Again, I
confirmed with the paper author that these functions should be
similarly constrained and deprecated.
[2020-04-04 Issue Prioritization]
Priority to 3 after reflector discussion. The suggested wording was generally accepted, but there
were considerable doubts that it is necessary to add deprecated functions of the new wait/notify
functions instead of declaring only the non-volatile overloads. The wish was expressed that both SG1 and
LEWG should express their opinion here.
Proposed resolution:
This wording is relative to N4861.
Modify 32.5.8.2 [atomics.types.operations] as indicated:
void wait(T old, memory_order order = memory_order::seq_cst) const volatile noexcept; void wait(T old, memory_order order = memory_order::seq_cst) const noexcept;Constraints: For the
-29- Preconditions: […] […]volatileoverload of this function,is_always_lock_freeistrue.void notify_one() volatile noexcept; void notify_one() noexcept;Constraints: For the
-32- Effects: […] […]volatileoverload of this function,is_always_lock_freeistrue.void notify_all() volatile noexcept; void notify_all() noexcept;Constraints: For the
-34- Effects: […] […]volatileoverload of this function,is_always_lock_freeistrue.
Modify D.23.2 [depr.atomics.volatile], annex D, as indicated:
If an atomic specialization has one of the following overloads, then that overload participates in overload resolution even if
atomic<T>::is_always_lock_freeisfalse:void store(T desired, memory_order order = memory_order::seq_cst) volatile noexcept; […] T* fetch_key(ptrdiff_t operand, memory_order order = memory_order::seq_cst) volatile noexcept; value_type operator++(int) volatile noexcept; value_type operator--(int) volatile noexcept; value_type operator++() volatile noexcept; value_type operator--() volatile noexcept; void wait(T old, memory_order order = memory_order::seq_cst) const volatile noexcept; void notify_one() volatile noexcept; void notify_all() volatile noexcept;
<atomic>Section: 32.5.9 [atomics.nonmembers] Status: New Submitter: Alisdair Meredith Opened: 2020-03-19 Last modified: 2020-09-06
Priority: 3
View all issues with New status.
Discussion:
Paper P1831R1 deprecated the volatile-qualified
member functions of std::atomic unless is_always_lock_free is true.
32.5.9 [atomics.nonmembers] maps free functions calls, declared in the <atomic>
header, to those member functions, but does not deprecate them under the same circumstances.
[2020-03-29; Daniel provides wording]
The suggested wording changes for 32.5.9 [atomics.nonmembers] attempts to make clear that any of the specification elements of the member function (including but not restricted to Constraints: elements) are also imposed on the corresponding non-member function template. According to 16.3.2.4 [structure.specifications], the wording "the semantics of the code sequence are determined by the Constraints,[…], and Error conditions specified for the function invocations contained in the code sequence." should realize the wanted effect. The advantage of this more general wording form is that we don't need to to worry in case that in the future Constraints: elements of the member functions are modified.
[2020-03-30; Tim improves wording]
[2020-04-25 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
This wording is relative to N4861.
Modify 32.5.9 [atomics.nonmembers] as indicated:
-1- A non-member function template whose name matches the pattern
atomic_for the patternatomic_f_explicitinvokes the member functionf, with the value of the first parameter as the object expression and the values of the remaining parameters (if any) as the arguments of the member function call, in order. An argument for a parameter of typeatomic<T>::value_type*is dereferenced when passed to the member function call. If no such member function exists, the program is ill-formed. Otherwise, a call to such a function template has effects equivalent to (16.3.2.4 [structure.specifications]) the effective code sequence containing thefinvocation specified in this subclause.
Modify D.23.2 [depr.atomics.volatile], annex D, as indicated:
If an atomic specialization has one of the following overloads, then that overload participates in overload resolution even if
atomic<T>::is_always_lock_freeisfalse:void store(T desired, memory_order order = memory_order::seq_cst) volatile noexcept; […] T* fetch_key(ptrdiff_t operand, memory_order order = memory_order::seq_cst) volatile noexcept;In addition, the following non-member function templates participate in overload resolution even if
atomic<T>::is_always_lock_freeisfalse:template<class T> void atomic_store(volatile atomic<T>*, typename atomic<T>::value_type) noexcept; template<class T> T atomic_load(const volatile atomic<T>*) noexcept; template<class T> T atomic_load_explicit(const volatile atomic<T>*, memory_order) noexcept; template<class T> T atomic_exchange(volatile atomic<T>*, typename atomic<T>::value_type) noexcept; template<class T> T atomic_exchange_explicit(volatile atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template<class T> bool atomic_compare_exchange_weak(volatile atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type) noexcept; template<class T> bool atomic_compare_exchange_strong(volatile atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type) noexcept; template<class T> bool atomic_compare_exchange_weak_explicit(volatile atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type, memory_order, memory_order) noexcept; template<class T> bool atomic_compare_exchange_strong_explicit(volatile atomic<T>*, typename atomic<T>::value_type*, typename atomic<T>::value_type, memory_order, memory_order) noexcept; template<class T> T atomic_fetch_key(volatile atomic<T>*, typename atomic<T>::difference_type) noexcept; template<class T> T atomic_fetch_key_explicit(volatile atomic<T>*, typename atomic<T>::difference_type, memory_order) noexcept; template<class T> T atomic_fetch_key(volatile atomic<T>*, typename atomic<T>::value_type) noexcept; template<class T> T atomic_fetch_key_explicit(volatile atomic<T>*, typename atomic<T>::value_type, memory_order) noexcept; template<class T> void atomic_wait(const volatile atomic<T>*, typename atomic<T>::value_type); template<class T> void atomic_wait_explicit(const volatile atomic<T>*, typename atomic<T>::value_type, memory_order); template<class T> void atomic_notify_one(volatile atomic<T>*); template<class T> void atomic_notify_all(volatile atomic<T>*);
std::any_cast should never return a cv-qualified typeSection: 22.7.5 [any.nonmembers] Status: New Submitter: Casey Carter Opened: 2020-04-02 Last modified: 2020-09-06
Priority: 3
View all other issues in [any.nonmembers].
View all issues with New status.
Discussion:
The return type of the non-pointer overloads of std::any_cast<T> is T. This is silly
when T is cv-qualified (and, since merging P1152
"Deprecating volatile" into the working draft, deprecated when T is volatile-qualified).
We should strip cv-qualifiers to determine the type returned.
[2020-04-18 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
This wording is relative to N4861.
Modify 22.7.2 [any.synop] as indicated:
[…] template<class T> remove_cv_t<T> any_cast(const any& operand); template<class T> remove_cv_t<T> any_cast(any& operand); template<class T> remove_cv_t<T> any_cast(any&& operand); […]
Modify 22.7.5 [any.nonmembers] as indicated:
template<class T> remove_cv_t<T> any_cast(const any& operand); template<class T> remove_cv_t<T> any_cast(any& operand); template<class T> remove_cv_t<T> any_cast(any&& operand);-4- Let
-5- Mandates: For the first overload,Rbe the typeremove_cv_t<T>, andUbe the typeremove_cvref_t<T>.is_constructible_v<isTR, const U&>true. For the second overload,is_constructible_v<isTR, U&>true. For the third overload,is_constructible_v<isTR, U>true. -6- Returns: For the first and second overload,static_cast<. For the third overload,TR>(*any_cast<U>(&operand))static_cast<. -7- Throws:TR>(std::move(*any_cast<U>(&operand)))bad_any_castifoperand.type() != typeid(remove_reference_t<T>).
optional::value_or should never return a cv-qualified typeSection: 22.5.3.7 [optional.observe] Status: New Submitter: Casey Carter Opened: 2020-04-02 Last modified: 2023-02-10
Priority: 3
View other active issues in [optional.observe].
View all other issues in [optional.observe].
View all issues with New status.
Discussion:
The optional<T>::value_or overloads are specified to return T. This seems silly when
T is const or volatile qualified — return types should never be cv-qualified.
(In the volatile case, it is even deprecated since merging P1152R4
"Deprecating volatile" into the working draft.) We should strip cv-qualifiers from these return types.
[2020-04-18 Issue Prioritization]
Priority to 3 after reflector discussion.
Previous resolution [SUPERSEDED]:
This wording is relative to N4861.
Modify 22.5.3 [optional.optional] as indicated:
[…] template<class U> constexpr remove_cv_t<T> value_or(U&&) const&; template<class U> constexpr remove_cv_t<T> value_or(U&&) &&; […]Modify 22.5.3.7 [optional.observe] as indicated:
template<class U> constexpr remove_cv_t<T> value_or(U&& v) const&;-?- Let
-17- Mandates:Rberemove_cv_t<T>.isis_copy_constructible_v<T>is_convertible_v<const T&, R> && is_convertible_v<U&&, T>true. -18- Effects: Equivalent to:return bool(*this) ? **this : static_cast<TR>(std::forward<U>(v));template<class U> constexpr remove_cv_t<T> value_or(U&& v) &&;-?- Let
-19- Mandates:Rberemove_cv_t<T>.isis_move_constructible_v<T>is_convertible_v<T, R> && is_convertible_v<U&&, T>true. -20- Effects: Equivalent to:return bool(*this) ? std::move(**this) : static_cast<TR>(std::forward<U>(v));
[2023-02-09 Casey improves wording and expands to cover expected::value_or]
Since expected was modeled on optional, it has the same issue.
Proposed resolution:
This wording is relative to N4928.
Modify 22.5.3.1 [optional.optional.general] as indicated:
[…] template<class U> constexpr remove_cv_t<T> value_or(U&&) const&; template<class U> constexpr remove_cv_t<T> value_or(U&&) &&; […]
Modify 22.5.3.7 [optional.observe] as indicated:
[Drafting note: The two removals of the
&&inis_convertible_v<U&&, T>below is a simplification to restore consistency with the wording forexpected::value_or.]
template<class U> constexpr remove_cv_t<T> value_or(U&& v) const &;-?- Let
-15- Mandates:Rberemove_cv_t<T>.isis_copy_constructible_v<T>is_convertible_v<const T&, R> && is_convertible_v<U&&,TR>true. -16- Effects: Equivalent to:return bool(*this) ? **this : static_cast<TR>(std::forward<U>(v));template<class U> constexpr remove_cv_t<T> value_or(U&& v) &&;-?- Let
-17- Mandates:Rberemove_cv_t<T>.isis_move_constructible_v<T>is_convertible_v<T, R> && is_convertible_v<U&&,TR>true. -18- Effects: Equivalent to:return bool(*this) ? std::move(**this) : static_cast<TR>(std::forward<U>(v));
Modify 22.8.6.1 [expected.object.general] as indicated:
[…] template<class U> constexpr remove_cv_t<T> value_or(U&&) const &; template<class U> constexpr remove_cv_t<T> value_or(U&&) &&; […]
Modify 22.8.6.6 [expected.object.obs] as indicated:
template<class U> constexpr remove_cv_t<T> value_or(U&& v) const &;-?- Let
-16- Mandates:Rberemove_cv_t<T>.isis_copy_constructible_v<T>is_convertible_v<const T&, R> && is_convertible_v<U,TR>true. -17- Returns:has_value() ? **this : static_cast<.TR>(std::forward<U>(v))template<class U> constexpr remove_cv_t<T> value_or(U&& v) &&;-?- Let
-18- Mandates:Rberemove_cv_t<T>.isis_move_constructible_v<T>is_convertible_v<T, R> && is_convertible_v<U,TR>true. -19- Returns:has_value() ? std::move(**this) : static_cast<.TR>(std::forward<U>(v))
Section: 16.4.5.11 [res.on.requirements] Status: New Submitter: Tim Song Opened: 2020-04-07 Last modified: 2020-09-06
Priority: 3
View other active issues in [res.on.requirements].
View all other issues in [res.on.requirements].
View all issues with New status.
Discussion:
It has been pointed out both on the LWG reflector and as editorial issue 3912 that the definition of "models" added in P2101R0 is not a model of clarity when a concept is defined in terms of other concepts.
[2020-04-25 Issue Prioritization]
Priority to 3 after reflector discussion. There was a temptation to resolve this as P0, but concerns were expressed that the "satisfied as part of the satisfaction" part was a bit confusing.
Proposed resolution:
This wording is relative to N4861.
Modify 16.4.5.11 [res.on.requirements] as indicated:
-1- A sequence
Argsof template arguments is said to model a conceptCif:
(1.1) —
ArgssatisfiesC(13.5.3 [temp.constr.decl]);(1.2) —
andArgsmeets all semantic requirements (if any) given in the specification ofC; and(1.3) — every concept found to be satisfied as part of the satisfaction determination above is also modeled.
multiset/map casesSection: 23.2.5.1 [container.node.overview] Status: New Submitter: Jens Maurer Opened: 2020-04-30 Last modified: 2020-05-09
Priority: 3
View all other issues in [container.node.overview].
View all issues with New status.
Discussion:
This issue resulted out of this editorial change request.
In 23.2.5.1 [container.node.overview], there is Table 79 [tab:container.node.compat] which indicates which containers have compatible nodes. It appears that rows alongmultimap<K, T, C1, A> multimap<K, T, C2, A>
(i.e. multimaps with differing Compare functions) are missing from the table.
multiset and unordered_multiset/map.)
However, the introductory sentences in 23.2.5.1 [container.node.overview] do not relate
"compatible nodes" with Table 79 and do not clearly state transitivity:
A node handle is an object that accepts ownership of a single element from an associative container (23.2.7 [associative.reqmts]) or an unordered associative container (23.2.8 [unord.req]). It may be used to transfer that ownership to another container with compatible nodes. Containers with compatible nodes have the same node handle type. Elements may be transferred in either direction between container types in the same row of Table 79 [tab:container.node.compat].
[2020-05-09; Reflector prioritization]
Set priority to 3 after reflector discussions.
Proposed resolution:
Distance" template parameter is underspecifiedSection: 24.4.3 [iterator.operations], 26.7.12 [alg.random.sample] Status: New Submitter: Jens Maurer Opened: 2020-05-01 Last modified: 2025-10-10
Priority: 3
View other active issues in [iterator.operations].
View all other issues in [iterator.operations].
View all issues with New status.
Discussion:
The library specification conveys pre-concept requirements on template
parameters by naming, e.g. "InputIterator".
Distance",
as used by std::advance (see 24.4.3 [iterator.operations]).
When addressing this omission, the local restriction in 26.7.12 [alg.random.sample]
could possibly be removed.
See the related issue 3213(i) for the Size template parameter.
[2025-10-10; Jonathan comments]
The SGI STL has the requirement that
"Distance is an integral type that is convertible to InputIterator's
distance type." But the SGI STL also required that Size arguments to
fill_n etc. have integral types, and the standard only requires them to
be convertible to integral types. Presumably that relaxation was intentional.
[2020-05-09; Reflector prioritization]
Set priority to 3 after reflector discussions.
Proposed resolution:
net::basic_socket_streambuf::connect(Args&&...) effects are wrongSection: 19.1.2 [networking.ts::socket.streambuf.members] Status: New Submitter: Jonathan Wakely Opened: 2020-05-14 Last modified: 2020-07-17
Priority: 2
View all issues with New status.
Discussion:
Addresses: networking.ts
The effects in 19.1.2 [networking.ts::socket.streambuf.members] p3 say that the function loops through every endpoint in the sequence, attempting to establish a connection. It needs to say that as soon as a connection is successfully established it returns. Otherwise even if a connection is made, it closes the socket and tries the next endpoint in the sequence. That means it will always be left in whatever state resulted from trying the last endpoint in the sequence (or from timing out if the expiry time was reached before iterating through all endpoints).
[2020-07-17; Priority set to 2 in telecon]
Jonathan to provide wording.
Proposed resolution:
net::basic_socket_istream::connect should be constrainedSection: 19.2.1 [networking.ts::socket.iostream.cons], 19.2.2 [networking.ts::socket.iostream.members] Status: LEWG Submitter: Jonathan Wakely Opened: 2020-05-14 Last modified: 2025-10-16
Priority: 3
View all other issues in [networking.ts::socket.iostream.cons].
View all issues with LEWG status.
Discussion:
Addresses: networking.ts
basic_socket_streambuf<P, C, W>::connect(Args&&...) is constrained to only exist when
P meets the InternetProtocol requirements, but basic_socket_iostream<P, C, W>::connect(Args&&...)
is not constrained. Since it just passes those arguments straight to the streambuf, the outer
connect(Args&&...) should be constrained too.
basic_socket_iostream(Args&&...) constructor should be constrained, so that
is_constructible gives the right answer.
[2020-07-17; Priority set to 3 in telecon]
[2025-10-16 Status changed: LEWG → SG4.]
Discussed by LEWG in Wrocław, recommended to send to SG4.
Proposed resolution:
This wording is relative to N4771.
Modify 19.2.1 [networking.ts::socket.iostream.cons] as indicated:
[Drafting note: As a drive-by fix, a missing
std::qualification in front offorwardhas been added]
template<class... Args> explicit basic_socket_iostream(Args&&... args);-4- Effects: Initializes the base class as
-?- Remarks: This function shall not participate in overload resolution unless the expressionbasic_iostream<char>(&sb_)), value-initializessb_, and performssetf(std::ios_base::unitbuf). Then callsrdbuf()->connect(std::forward<Args>(args)...). If that function returns a null pointer, callssetstate(failbit).rdbuf()->connect(std::forward<Args>(args)...)is well-formed.
Modify 19.2.2 [networking.ts::socket.iostream.members] as indicated:
[Drafting note: As a drive-by fix, a missing
std::qualification in front offorwardhas been added]
template<class... Args> void connect(Args&&... args);-1- Effects: Calls
-?- Remarks: This function shall not participate in overload resolution unless the expressionrdbuf()->connect(std::forward<Args>(args)...). If that function returns a null pointer, callssetstate(failbit)(which may throwios_base::failure).rdbuf()->connect(std::forward<Args>(args)...)is well-formed.
Section: 27.4.3 [basic.string], 25.7.14.2 [range.join.view] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2020-06-11 Last modified: 2020-07-17
Priority: 3
View other active issues in [basic.string].
View all other issues in [basic.string].
View all issues with New status.
Discussion:
The library inconsistently marks deduction guides as explicit. join_view and basic_string
account for the only two occurrences of unconditionally explicit deduction guides. All other deduction
guides have no explicit-specifier. Following is a list of unconditionally explicit constructors
with their deduction guides.
template<class Y>
explicit shared_ptr(const weak_ptr<Y>& r);
template<class T>
shared_ptr(weak_ptr<T>) -> shared_ptr<T>;
template<class T>
constexpr explicit basic_string(const T& t, const Allocator& a = Allocator());
template<class charT,
class traits,
class Allocator = allocator<charT>>
explicit basic_string(basic_string_view<charT, traits>, const Allocator& = Allocator())
-> basic_string<charT, traits, Allocator>;
explicit queue(const Container&);
explicit queue(Container&&);
template<class Container>
queue(Container) -> queue<typename Container::value_type, Container>;
explicit stack(const Container&);
explicit stack(Container&&);
template<class Container>
stack(Container) -> stack<typename Container::value_type, Container>;
constexpr explicit join_view(V base);
template<class R>
explicit join_view(R&&) -> join_view<views::all_t<R>>;
constexpr explicit common_view(V r);
template<class R>
common_view(R&&) -> common_view<views::all_t<R>>;
constexpr explicit reverse_view(V r);
template<class R>
reverse_view(R&&) -> reverse_view<views::all_t<R>>;
explicit zoned_time(TimeZonePtr z);
explicit zoned_time(string_view name);
template<class TimeZonePtrOrName>
zoned_time(TimeZonePtrOrName&&)
-> zoned_time<seconds, time-zone-representation<TimeZonePtrOrName>>;
template<class C>
explicit stop_callback(const stop_token& st, C&& cb)
noexcept(is_nothrow_constructible_v<Callback, C>);
template<class C>
explicit stop_callback(stop_token&& st, C&& cb)
noexcept(is_nothrow_constructible_v<Callback, C>);
template<class Callback>
stop_callback(stop_token, Callback) -> stop_callback<Callback>;
[2020-07-17; Priority set to 3 in telecon]
Proposed resolution:
This wording is relative to N4861.
Modify 27.4.3 [basic.string], class template basic_string synopsis, as indicated:
[…]
template<class charT,
class traits,
class Allocator = allocator<charT>>
explicit basic_string(basic_string_view<charT, traits>, const Allocator& = Allocator())
-> basic_string<charT, traits, Allocator>;
[…]
Modify 27.4.3.3 [string.cons] as indicated:
[…] template<class charT, class traits, class Allocator = allocator<charT>>explicitbasic_string(basic_string_view<charT, traits>, const Allocator& = Allocator()) -> basic_string<charT, traits, Allocator>; […]-22- Constraints:
Allocatoris a type that qualifies as an allocator (23.2.2 [container.requirements.general]).
Modify 25.7.14.2 [range.join.view], class template join_view synopsis, as indicated:
[…] template<class R>explicitjoin_view(R&&) -> join_view<views::all_t<R>>; […]
pointer_traits::pointer_to should be constexprSection: 20.2.3 [pointer.traits] Status: Open Submitter: Alisdair Meredith Opened: 2020-06-21 Last modified: 2025-10-20
Priority: 4
View all other issues in [pointer.traits].
View all issues with Open status.
Discussion:
Trying to implement a constexpr std::list (inspired by Tim Song's
note on using variant members in the node) as part of evaluating
the constexpr container and adapters proposals, I hit problems
I could not code around in pointer_traits, as only the specialization
for native pointers has a constexpr pointer_to function.
std::allocator<T> but adds extra
telemetry and uses a fancy pointer, does not work with the approach
I tried for implementing list (common link type, shared between
nodes, and stored as end sentinel directly in the list object).
[2020-07-17; Forwarded to LEWG after review in telecon]
[2022-07-19; Casey Carter comments]
This is no longer simply a theoretical problem that impedes implementing
constexpr std::list, but an actual defect affecting current
implementations of constexpr std::string. More specifically, it
makes it impossible to support so-called "fancy pointers" in a constexpr
basic_string that performs the small string optimization (SSO).
(pointer_traits::pointer_to is critically necessary to get a
pointer that designates the SSO buffer.) As things currently stand,
constexpr basic_string can support fancy pointers or SSO,
but not both.
[Wrocław 2024-11-18; LEWG approves the direction]
Should there be an Annex C entry noting that program-defined specializations
need to add constexpr to be conforming?
[2025-10-20; Set priority to 4 based on age of issue and lack of recent interest.]
Proposed resolution:
This wording is relative to N4861.
Modify 20.2.3 [pointer.traits] as indicated:
-1- The class template
pointer_traitssupplies a uniform interface to certain attributes of pointer-like types.namespace std { template<class Ptr> struct pointer_traits { using pointer = Ptr; using element_type = see below; using difference_type = see below; template<class U> using rebind = see below; static constexpr pointer pointer_to(see below r); }; […] }
Modify 20.2.3.3 [pointer.traits.functions] as indicated:
static constexpr pointer pointer_traits::pointer_to(see below r); static constexpr pointer pointer_traits<T*>::pointer_to(see below r) noexcept;-1- Mandates: For the first member function,
-2- Preconditions: For the first member function,Ptr::pointer_to(r)is well-formed.Ptr::pointer_to(r)returns a pointer torthrough which indirection is valid. -3- Returns: The first member function returnsPtr::pointer_to(r). The second member function returnsaddressof(r). -4- Remarks: Ifelement_typeis cvvoid, the type ofris unspecified; otherwise, it iselement_type&.
std::from_chars is underspecifiedSection: 28.2.3 [charconv.from.chars] Status: New Submitter: Jonathan Wakely Opened: 2020-06-23 Last modified: 2020-09-06
Priority: 3
View other active issues in [charconv.from.chars].
View all other issues in [charconv.from.chars].
View all issues with New status.
Discussion:
The intention of 28.2.3 [charconv.from.chars] p7 is that the fmt argument modifies
the expected pattern, so that only a specific subset of valid strtod patterns are recognized
for each format. This is not clear from the wording.
fmt == chars_format::fixed no exponent is to be used, so any trailing characters that match
the form of a strtod exponent are ignored. For example, "1.23e4" should produce the result
1.23 for the fixed format. The current wording says "the optional exponent part shall not appear"
which can be interpreted to mean that "1.23e4" violates a precondition and so has undefined behaviour!
When fmt != chars_format::hex only decimal numbers should be recognized. This means that for any
format except scientific, "0x123" produces 0.0 (it's invalid when
fmt == chars_format::scientific because there's no exponent). The current wording only says that
when hex is used the string has an assumed "0x" prefix, so is interpreted as a hexadecimal
float, it doesn't say that when fmt != hex that the string is not interpreted as a
hexadecimal float.
Two alternative resolutions are provided, one is a minimal fix and the other attempts to make it clearer by
not referring to a modified version of the C rules.
[2020-07-14; Jonathan fixes the strtod call in Option B]
[2020-07-17; Priority set to 3 in telecon]
Proposed resolution:
This wording is relative to N4861.
Option A:Modify 28.2.3 [charconv.from.chars] as indicated:
from_chars_result from_chars(const char* first, const char* last, float& value, chars_format fmt = chars_format::general); from_chars_result from_chars(const char* first, const char* last, double& value, chars_format fmt = chars_format::general); from_chars_result from_chars(const char* first, const char* last, long double& value, chars_format fmt = chars_format::general);-6- Preconditions:
-7- Effects: The pattern is the expected form of the subject sequence in the "C" locale, as described forfmthas the value of one of the enumerators ofchars_format.strtod, except that
(7.1) — the sign
'+'may only appear in the exponent part;(7.2) — if
fmthaschars_format::scientificset but notchars_format::fixed, theotherwise optional exponent part shall appearexponent part is not optional;(7.3) — if
fmthaschars_format::fixedset but notchars_format::scientific,the optional exponent part shall not appear; andthere is no exponent part;(?.?) — if
fmtis notchars_format::hex, only decimal digits and an optional'.'appear before the exponent part (if any); and(7.4) — if
fmtischars_format::hex, the prefix"0x"or"0X"is assumed. [Example: The string0x123is parsed to have the value0with remaining charactersx123. — end example]In any case, the resulting
valueis one of at most two floating-point values closest to the value of the string matching the pattern.
Modify 28.2.3 [charconv.from.chars] as indicated:
from_chars_result from_chars(const char* first, const char* last, float& value, chars_format fmt = chars_format::general); from_chars_result from_chars(const char* first, const char* last, double& value, chars_format fmt = chars_format::general); from_chars_result from_chars(const char* first, const char* last, long double& value, chars_format fmt = chars_format::general);-6- Preconditions:
-7- Effects:fmthas the value of one of the enumerators ofchars_format.The pattern is the expected form of the subject sequence in the "C" locale, as described forThe pattern is an optionalstrtod, except that'-'sign followed by one of:
(7.1) —
the sign'+'may only appear in the exponent partINForINFINITY, ignoring case;(7.2) —
ififfmthaschars_format::scientificset but notchars_format::fixed, the otherwise optional exponent part shall appearnumeric_limits<T>::has_quiet_NaNistrue,NANorNAN(n-char-sequenceopt), ignoring case in theNANpart, where:n-char-sequence: digit nondigit n-char-sequence digit n-char-sequence nondigit;
(7.3) —
ififfmthaschars_format::fixedset but notchars_format::scientific, the optional exponent part shall not appear; andfmtis equal tochars_format::scientific, a sequence of characters matching chars-format-dec exponent-part, where:chars-format-dec: fractional-constant digit-sequence;
(7.4) —
ififfmtischars_format::hex, the prefix"0x"or"0X"is assumed. [Example: The string0x123is parsed to have the value0with remaining charactersx123. — end example]fmtis equal tochars_format::fixed, a sequence of characters matching chars-format-dec;(?.?) — if
fmtis equal tochars_format::general, a sequence of characters matching chars-format-dec exponent-partopt; or(?.?) — if
fmtis equal tochars_format::hex, a sequence of characters matching chars-format-hex binary-exponent-partopt, where:chars-format-hex: hexadecimal-fractional-constant hexadecimal-digit-sequence[Note: The pattern is derived from the subject sequence in the
"C"locale forstrtod, with the value offmtlimiting which forms of the subject sequence are recognized, and with no0xor0Xprefix recognized. — end note]For a character sequence
INF,INFINITY,NAN, orNAN(n-char-sequenceopt)the resulting value is obtained as if by evaluatingstrtod(string(first, last).c_str(), nullptr)in the"C"locale. In all other casesIn any case, the resultingvalueis one of at most two floating-point values closest to the value of the string matching the pattern.
*this is not invalidatedSection: 27.3.3 [string.view.template] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2020-06-26 Last modified: 2020-09-06
Priority: 3
View all other issues in [string.view.template].
View all issues with New status.
Discussion:
27.3.3 [string.view.template] states:
For a
basic_string_view str, any operation that invalidates a pointer in the range[str.data(), str.data() + str.size())invalidates pointers, iterators, and references returned fromstr's member functions.
The assignment operators return a reference to *this, but *this is not invalidated.
Previous resolution [SUPERSEDED]:
This wording is relative to N4861.
Modify 27.3.3 [string.view.template] as indicated:
-2- For a
basic_string_view str, any operation that invalidates a pointer in the range[str.data(), str.data() + str.size())invalidates pointers, iterators, and references to the elements in that range and its past-the-end iterator returned fromstr's member functions.
[2020-06-29; Casey comments and provides alternative proposed wording]
I think we should additionally strike "returned from str's member functions" from the end of the sentence.
Provenance shouldn't affect invalidation; ranges::next(some_string_view.begin(), 42), for example,
returns an iterator that denotes an element of some_string_view, but is not a member function
of basic_string_view.
[2020-07-05; Reflector prioritization]
Set priority to 3 after reflector discussions.
Proposed resolution:
This wording is relative to N4861.
Modify 27.3.3 [string.view.template] as indicated:
-2- For a
basic_string_view str, any operation that invalidates a pointer in the range[str.data(), str.data() + str.size())invalidates pointers, iterators, and references to the elements in that range and its past-the-end iteratorreturned from.str's member functions
std::convertible_to have semantic requirement when To is reference-to-function type?Section: 18.4.4 [concept.convertible] Status: New Submitter: S. B. Tam Opened: 2020-06-30 Last modified: 2020-07-12
Priority: 3
View other active issues in [concept.convertible].
View all other issues in [concept.convertible].
View all issues with New status.
Discussion:
18.4.4 [concept.convertible] p2:
Types
FromandTomodelconvertible_to<From, To>only if:
(2.1) —
Tois not an object or reference-to-object type, orstatic_cast<To>(f())is equal totest(f).[…]
This requires the implicit and explicit conversions to produce equal results.
However, it seems that whenTo is a reference-to-function type, this restriction does not apply.
This makes it possible to create a class that models convertible_to, but produces different
results depending on the kind of conversion:
#include <concepts>
int foo() { return 0; }
int bar() { return 42; }
using FT = int();
struct A
{
operator FT&() const { return foo; }
explicit operator FT&() { return bar; }
};
static_assert(std::convertible_to<A, FT&>);
A a;
FT& x = a; // x == foo
auto y = static_cast<FT&>(a); // y == bar
[2020-07-12; Reflector prioritization]
Set priority to 3 after reflector discussions.
Proposed resolution:
transform_inclusive_scan without initial valueSection: 26.10.11 [transform.inclusive.scan] Status: New Submitter: Agustín K-ballo Bergé Opened: 2020-07-07 Last modified: 2020-09-06
Priority: 3
View all issues with New status.
Discussion:
The requirements for the overloads of std::transform_inclusive_scan
without an initial value incorrectly assume that the internal accumulator uses
the iterator's value type, as it does for std::inclusive_scan, rather
than the transformed type of the iterator's value type, as it was intended.
std::string to be convertible to int:
auto vs = {0, 1, 2};
std::transform_inclusive_scan(
vs.begin(), vs.end(),
std::ostream_iterator<std::string>(std::cout, ";"),
[](std::string x, std::string y) { return x + y; },
[](int x) { return std::to_string(x); });
libstdc++ and Microsoft's STL accept the snippet, producing 0;01;012;
as expected, libc++ strictly conforms to the standard and rejects it.
[2020-07-17; Priority set to 3 in telecon]
Proposed resolution:
This wording is relative to N4861.
[Drafting note: Current implementations that accept the code, do some form of
auto acc = unary_op(*first);, therefore the following proposed wording usesdecay_tinstead of e.g.remove_cvref_t.]
Modify 26.10.11 [transform.inclusive.scan] as indicated:
template<class InputIterator, class OutputIterator, class BinaryOperation, class UnaryOperation> constexpr OutputIterator transform_inclusive_scan(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation binary_op, UnaryOperation unary_op); template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, class BinaryOperation, class UnaryOperation> ForwardIterator2 transform_inclusive_scan(ExecutionPolicy&& exec, ForwardIterator1 first, ForwardIterator1 last, ForwardIterator2 result, BinaryOperation binary_op, UnaryOperation unary_op); template<class InputIterator, class OutputIterator, class BinaryOperation, class UnaryOperation, class T> constexpr OutputIterator transform_inclusive_scan(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation binary_op, UnaryOperation unary_op, T init); template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, class BinaryOperation, class UnaryOperation, class T> ForwardIterator2 transform_inclusive_scan(ExecutionPolicy&& exec, ForwardIterator1 first, ForwardIterator1 last, ForwardIterator2 result, BinaryOperation binary_op, UnaryOperation unary_op, T init);-1- Let
-2- […]Ubethe value type ofdecltype(first)decay_t<decltype(unary_op(*first))>.
std::thread's constructor needs to be able to report general memory allocation failuresSection: 32.4.3.3 [thread.thread.constr], 32.4.4.2 [thread.jthread.cons] Status: New Submitter: Billy O'Neal III Opened: 2020-08-14 Last modified: 2020-09-06
Priority: 3
View other active issues in [thread.thread.constr].
View all other issues in [thread.thread.constr].
View all issues with New status.
Discussion:
(j)thread's constructor needs to decay-copy the supplied parameters and callable over to the started
thread through an operating system API that generally only accepts a single void*. The MSVC++ and libc++
implementations do this by putting the parameters in a std::tuple allocated from the heap, passing a
pointer to that tuple through the operating system API, and leaving ownership of the parameters to the other thread.
resource_unavailable_try_again, but the description for this error in the standard is that some thread-based
limitation has been reached, not a general memory limit, so that doesn't seem to meet the spirit of the requirement.
[2020-08-21; Issue processing telecon: set priority to 3]
Jonathan: I prefer Option A, but I think we need something like:
"any exceptions thrown by the decay-copy calls, or ...".
Proposed resolution:
This wording is relative to N4861.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A: The memory allocation failure results in bad_alloc.
Modify 32.4.3.3 [thread.thread.constr] as indicated:
template<class F, class... Args> explicit thread(F&& f, Args&&... args);-3- Constraints: […]
[…] -8- Postconditions:get_id() != id().*thisrepresents the newly started thread. -9- Throws:bad_allocif memory to transfer parameters to the new thread cannot be obtained.system_errorif unable to start the new thread. -10- Error conditions:
(10.1) —
resource_unavailable_try_again— the system lacked the necessary resources to create another thread, or the system-imposed limit on the number of threads in a process would be exceeded.
Modify 32.4.4.2 [thread.jthread.cons] as indicated:
template<class F, class... Args> explicit jthread(F&& f, Args&&... args);-3- Constraints: […]
[…] -8- Postconditions:get_id() != id()istrueandssource.stop_possible()istrueand*thisrepresents the newly started thread. [Note: The calling thread can make a stop request only once, because it cannot replace this stop token. — end note] -9- Throws:bad_allocif memory to transfer parameters to the new thread cannot be obtained.system_errorif unable to start the new thread. -10- Error conditions:
(10.1) —
resource_unavailable_try_again— the system lacked the necessary resources to create another thread, or the system-imposed limit on the number of threads in a process would be exceeded.
Option B: The memory allocation failure results in a system_error with the error condition
out_of_memory.
Modify 32.4.3.3 [thread.thread.constr] as indicated:
template<class F, class... Args> explicit thread(F&& f, Args&&... args);-3- Constraints: […]
[…] -8- Postconditions:get_id() != id().*thisrepresents the newly started thread. -9- Throws:system_errorif unable to start the new thread. -10- Error conditions:
(10.?) —
not_enough_memory— the system lacked memory resources to transfer parameters to the new thread.(10.1) —
resource_unavailable_try_again— the system lacked the necessary resources to create another thread, or the system-imposed limit on the number of threads in a process would be exceeded.
Modify 32.4.4.2 [thread.jthread.cons] as indicated:
template<class F, class... Args> explicit jthread(F&& f, Args&&... args);-3- Constraints: […]
[…] -8- Postconditions:get_id() != id()istrueandssource.stop_possible()istrueand*thisrepresents the newly started thread. [Note: The calling thread can make a stop request only once, because it cannot replace this stop token. — end note] -9- Throws:system_errorif unable to start the new thread. -10- Error conditions:
(10.?) —
not_enough_memory— the system lacked memory resources to transfer parameters to the new thread.(10.1) —
resource_unavailable_try_again— the system lacked the necessary resources to create another thread, or the system-imposed limit on the number of threads in a process would be exceeded.
<stddef.h> declare ::nullptr_t?Section: 17.15.7 [support.c.headers.other] Status: New Submitter: Thomas Köppe Opened: 2020-09-06 Last modified: 2022-09-18
Priority: 3
View other active issues in [support.c.headers.other].
View all other issues in [support.c.headers.other].
View all issues with New status.
Discussion:
From this editorial issue request:
The header<stddef.h> is currently specified in 17.15.7 [support.c.headers.other]
to not declare a global name corresponding to std::byte, but no similar exclusion exists for
std::nullptr_t.
Is an oversight or intentional? There does not seem to be an interoperability reason to provide a global
namespace name ::nullptr_t, since this construction would be meaningless in C and thus the name
would not be encountered in code that is both valid C and C++.
For lack of justification, I would like to propose to require normatively that no name
::nullptr_t be declared by <stddef.h>.
Additional notes: The proposing paper N2431 mentions only an addition
of "nullptr_t" to <cstddef> and does not discuss the impact on <stddef.h>.
By omission this means the default rules for <stddef.h> apply and the global name should exist,
but this does not provide us with a positive signal of intention.
I also realize that this is a rather obscure point, and that vendors are already shipping ::nullptr_t,
so I am also happy to drop this issue as not being worth the churn and the increase in implementation complexity
(since removals don't generally simplify implementations). I would welcome a bit of discussion, though.
[2020-09-29; Priority to P3 after reflector discussions]
[2022-09-18; Daniel comments]
See also 3782(i), which points out that nullptr_t has become a part of the C standard library
as of C23 (see WG14-N3042 and
WG14-N3048).
Proposed resolution:
This wording is relative to N4910.
Modify 17.15.7 [support.c.headers.other] as indicated:
-1- Every C header other than
<complex.h>(17.15.2 [complex.h.syn]),<iso646.h>(17.15.3 [iso646.h.syn]),<stdalign.h>(17.15.4 [stdalign.h.syn]),<stdatomic.h>(32.5.12 [stdatomic.h.syn]),<stdbool.h>(17.15.5 [stdbool.h.syn]), and<tgmath.h>(17.15.6 [tgmath.h.syn]), each of which has a name of the form<name.h>, behaves as if each name placed in the standard library namespace by the corresponding<cname>header is placed within the global namespace scope, except for the functions described in 29.7.6 [sf.cmath], the declaration ofstd::byteandstd::nullptr_t(17.2.1 [cstddef.syn]), and the functions and function templates described in 17.2.5 [support.types.byteops]. It is unspecified whether these names are first declared or defined within namespace scope (6.4.6 [basic.scope.namespace]) of the namespacestdand are then injected into the global namespace scope by explicit using-declarations (9.10 [namespace.udecl]).
In [diff.cpp??] [Editorial note: new compatibility section to be created for C++20], add a new entry:
Affected subclause: 17.15.7 [support.c.headers.other]
Change: Removal of name::nullptr_tfrom header<stddef.h>.
Rationale:std::nullptr_tis a C++-only feature that is not useful for interoperability with ISO C.
Effect on original feature: Valid C++20 code may fail to compile in this International Standard. Occurrences of#include <stddef.h>andnullptr_tshould be replaced with#include <cstddef>andstd::nullptr_t.
is_constructible<T[], T...> may be misleading in C++20Section: 21.3.6.4 [meta.unary.prop] Status: LEWG Submitter: Jonathan Wakely Opened: 2020-10-01 Last modified: 2025-10-21
Priority: 4
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with LEWG status.
Discussion:
According to the current wording,
std::is_constructible<int[], int>
should be true, because the preconditions are met
(all types are complete types or unbounded arrays)
and the variable definition is well-formed since C++20:
using T = int[];
T t(declval<int>()); // equiv. to int t[] = {1};
However, this doesn't construct an object of type int[]
because it deduces the array bound from the initializers,
and so constructs int[1], which is not the type being asked about.
It seems more logical for the trait to give a false result
for an unbounded array, because it's an incomplete type,
and no int[] can ever be constructed.
On the reflector Tim Song noted:
On the other hand, the result is something to which an int(&)[]
can be bound directly thanks to another C++20 change,
so a lot of things might Just Work (for some definition of "Work")
despite the type difference.
This seems to me a reasonable rationale for
is_constructible<int(&&)[], int>
to be true (which it is today),
but not for int[].
Peter Dimov replied:
Placement new, which is often the way to construct we're interested in,
is not going to work even for T[2].
For example:
using T2 = int[2]; T2 x; new(x) T2(1, 2); // ill-formed
We need to decide what behaviour we want here.
Do we just want is_constructible
to reflect the T(declval<Args...>); construct
as currently specified in 21.3.6.4 [meta.unary.prop] p8,
or do we want to give a more useful/meaningful answer here?
Should we revisit 21.3.6.4 [meta.unary.prop] p8
in the light of parenthesized aggregate init,
so that is_constructible<T[], T>
and is_constructible<T[1], T> are false?
There may be some interaction with LWG 3436(i).
[2025-10-21; Priority set to 4 based on age of issue and lack of activity.]
Proposed resolution:
Section: 26.10 [numeric.ops] Status: New Submitter: Matthias Kretz Opened: 2020-10-01 Last modified: 2020-10-02
Priority: 3
View all other issues in [numeric.ops].
View all issues with New status.
Discussion:
The algorithms
partial_sum,
exclusive_scan,
inclusive_scan,
transform_exclusive_scan,
transform_inclusive_scan,
and
adjacent_difference
with no ExecutionPolicy do not have a precondition
"result is not in the range [first, last)".
But they explicitly allow "result may be equal to first".
This suggests the precondition got lost,
because otherwise the permission is redundant.
Suggested fix:
Add
"result is not in the range [first + 1, last)."
to the Preconditions paragraphs
of the affected generalized numeric operations.
[2020-10-02; Issue processing telecon: Priority set to P3.]
Proposed resolution:
array<const int, 0> swappable or not?Section: 23.3.3.4 [array.special] Status: Open Submitter: Casey Carter Opened: 2020-10-01 Last modified: 2020-10-04
Priority: 3
View all issues with Open status.
Discussion:
Per 23.3.3.4 [array.special]/1,
std::array's non-member swap participates in overload resolution
when the array has size 0 or swappable elements.
The effects of non-member swap are "As if by [member swap]",
but member swap's effects are simply
"Equivalent to swap_ranges(begin(), end(), y.begin())"
per 23.3.3.3 [array.members]/4.
In effect, we've gone out of our way to ensure that
is_swappable_v<array<T, 0>>
and swappable<array<T, 0>>
are always true
despite that actually swapping such an array may be ill-formed.
It seems that the wording stops half-way to making
array<T, 0> swappable regardless of T.
I personally find that design distasteful
- it seems a gratuitous difference between
array<T, N> and array<T, 0>
- but I'd prefer a consistent design over the status quo
even if it's the "wrong" design.
[2020-10-02; Issue processing telecon]
Preference for Option B, and successful vote to move to Tentatively Ready. But on the reflector Tim Song pointed out a conflict with 2157(i) and question the decision. Status to Open instead. Priority set to P3 in line with 2157(i).
Proposed resolution:
Wording relative to N4861.
This resolution proposes two wording alternatives: Option A makesarray<T, 0> swappable regardless of T,
and the clearly superior Option B makes array<T, N> swappable
only if T is swappable (i.e., regardless of N)
removing gratuitous special-case behavior for the N == 0 case.
Option A:
Change 23.3.3.3 [array.members] as follows:
constexpr void swap(array& y) noexcept(N == 0 || is_nothrow_swappable_v<T>);
-4- Effects: If N == 0, no effects. Otherwise, equivalent Equivalent to swap_ranges(begin(), end(), y.begin()).
-5- […]
Also remove the now-redundant paragraph four from 23.3.3.5 [array.zero] as follows:
-4- Member functionswap()shall have a non-throwing exception specification.
Option B:
Change 23.3.3.4 [array.special] as follows:
template<class T, size_t N> constexpr void swap(array<T, N>& x, array<T, N>& y) noexcept(noexcept(x.swap(y)));
-1- Constraints: N == 0 oris_swappable_v<T> is true.
Also remove paragraph four from 23.3.3.5 [array.zero] as follows:
-4- Member functionswap()shall have a non-throwing exception specification.
istream_view wordingSection: 25.6.6.3 [range.istream.iterator] Status: New Submitter: Michael Schellenberger Costa Opened: 2020-10-09 Last modified: 2021-09-02
Priority: 3
View all other issues in [range.istream.iterator].
View all issues with New status.
Discussion:
While implementing iranges::stream_view we found some issues with the Preconditions
on the member functions of istream_view::iterator, which are superfluous or incorrect.
25.6.6.3 [range.istream.iterator] p2 reads as:
Preconditions:
parent_->stream_ != nullptristrue.
However in the Effects element 25.6.6.3 [range.istream.iterator] p3 it reads:
Effects: Equivalent to:
*parent_->stream_ >> parent_->object_; return *this;
For the Effects element to be valid, we implicitly require that parent_ != nullptr,
parent_->stream_ != nullptr and — because we are reading from the underlying
stream — !*x.parent_->stream_.
Preconditions:
*this != default_sentinel.
We should use the same precondition for 25.6.6.3 [range.istream.iterator] p4, even if it is implicit via the Effects element in 25.6.6.3 [range.istream.iterator] p5, as that requires experts knowledge of the standard.
The Precondition in 25.6.6.3 [range.istream.iterator] p6 is completely bogus, as accessing the cached object has no dependency on the stream. We assume it is meant that we are not at the end of the stream. Again we should change this to:
Preconditions:
*this != default_sentinel.
[2020-10-14; Priority to P3 after reflector discusssion]
[2021-09-02; Jonathan comments:]
The preconditions were removed by P2325R3 approved in June 2021.
Although the pointers now cannot be null, it's unclear if we want to require
fail() to be false for the stream.
Proposed resolution:
This wording is relative to N4861.
Modify 25.6.6.3 [range.istream.iterator] as indicated:
iterator& operator++();-2- Preconditions:
-3- Effects: Equivalent to:isparent_->stream_ != nullptr*this != default_sentineltrue.*parent_->stream_ >> parent_->object_; return *this;void operator++(int);-4- Preconditions:
-5- Effects: Equivalent toisparent_->stream_ != nullptr*this != default_sentineltrue.++*this.Val& operator*() const;-6- Preconditions:
-7- Effects: Equivalent to:isparent_->stream_ != nullptr*this != default_sentineltrue.return parent_->object_;friend bool operator==(const iterator& x, default_sentinel_t);-8- Effects: Equivalent to:
return x.parent_ == nullptr || !*x.parent_->stream_;
Section: 17.12.6 [cmp.alg], 25.7.6.1 [range.all.general], 17.12.6 [cmp.alg] Status: New Submitter: Alisdair Meredith Opened: 2020-10-27 Last modified: 2021-10-30
Priority: 3
View other active issues in [cmp.alg].
View all other issues in [cmp.alg].
View all issues with New status.
Discussion:
Some of our newer wording for C++20 uses the term "decayed type" as if it were a defined term of art. While I have intuition for what may be intended in these cases, it turns out the "function to function pointer decay" and "array to array pointer decay" were never actually Core terms of art — having searched all standards going back as far as C++03.
We should either define this term for library use, or find a way to state our intent using existing well-defined terms of art. Affected clauses:17.12.6 [cmp.alg]: Most heavily used here.
25.7.6.1 [range.all.general]
17.12.6 [cmp.alg]
[2021-01-15; Telecon prioritization]
Set priority to 3 following reflector and telecon discussions.
Proposed resolution:
std::function taking an F is missing a constraintSection: 22.10.17.3.2 [func.wrap.func.con] Status: New Submitter: Ville Voutilainen Opened: 2020-10-31 Last modified: 2021-08-20
Priority: 3
View all other issues in [func.wrap.func.con].
View all issues with New status.
Discussion:
In P0288, any_invocable is (correctly) constraining
its constructor that takes an F:
template<class F> any_invocable(F&& f);Let
Constraints:VTbedecay_t<F>.
— […]
—
is_constructible_v<VT, F>istrue, and— […]
std::function doesn't do that. According to N4868,
22.10.17.3.2 [func.wrap.func.con] p8 has a constraint for Lvalue-Callable, but not for
copy-constructibility. There is a precondition in p9, but that's not enough for portable
well/ill-formedness.
is_constructible/constructible_from queries, we should
add the relevant constraint.
[2020-11-01; Daniel comments]
This issue has some overlap with LWG 2774(i).
[2021-01-15; Telecon prioritization]
Set priority to 3 following reflector and telecon discussions.
[2021-05-17; Tim comments]
The new constraint causes constraint recursion in an example like:
struct C {
explicit C(std::function<void()>); // #1
void operator()() {}
};
static_assert(std::is_constructible_v<C, const C&>);
Here, to determine whether a C can be constructed from a const C
lvalue, the overload resolution will attempt to determine whether the constructor
marked #1 is a viable candidate, which involves a determination of
whether that lvalue can be implicitly converted to a std::function<void()>,
which, with the new constraint, requires a determination whether
C is copy-constructible — in other words, whether it can be constructed
from a C lvalue.
filesystem::path there, function here) that is
convertible from every type that are, inter alia, copy constructible,
and this then results in constraint recursion when we ask whether a different
type that is constructible from such a class is copy constructible.
The C above is reduced from an internal helper type in libstdc++. Given
the ubiquity of call wrappers — types that are callable in their own right
and therefore may not be able to be ruled out by the Lvalue-Callable constraint,
and can also naturally have a constructor that take the wrapped function object
as the argument, triggering the recursion scenario — it is not clear that
there is a good way to add this constraint without causing undue breakage.
[2021-08-20; LWG telecon]
LWG requested that the constraint cited above for
move_only_function (né any_invocable)
be moved to a Mandates: element instead, to avoid the same
constraint recursion.
Proposed resolution:
This wording is relative to N4868.
Modify 22.10.17.3.2 [func.wrap.func.con] as indicated:
template<class F> function(F f);-8- Constraints:
-9- Preconditions:Fis Lvalue-Callable (22.10.17.3.1 [func.wrap.func.general]) for argument typesArgTypes...and return typeR, andis_copy_constructible_v<F>istrue.Fmeets the Cpp17CopyConstructible requirements. […]
basic_syncbuf::emit()?Section: 31.11.2.4 [syncstream.syncbuf.members] Status: New Submitter: Jonathan Wakely Opened: 2020-11-10 Last modified: 2020-11-21
Priority: 3
View other active issues in [syncstream.syncbuf.members].
View all other issues in [syncstream.syncbuf.members].
View all issues with New status.
Discussion:
31.11.2.4 [syncstream.syncbuf.members] p5 says "May call member functions of wrapped while holding a
lock uniquely associated with wrapped."
streambuf* that is wrapped is associated with a different lock?
I believe the intention is only that for a given streambuf* every syncbuf that wraps it
uses the same lock. The intention was that it's a valid implementation for the same lock to be used for more
than one streambuf* (e.g. using a table of N locks which are indexed by a hash of the
streambuf* value). The current wording can be interpreted to forbid that implementation.
[2020-11-21; Reflector prioritization]
Set priority to 3 during reflector discussions.
Proposed resolution:
basic_syncbuf::emit()Section: 31.11.2.4 [syncstream.syncbuf.members] Status: New Submitter: Jonathan Wakely Opened: 2020-11-10 Last modified: 2020-11-21
Priority: 3
View other active issues in [syncstream.syncbuf.members].
View all other issues in [syncstream.syncbuf.members].
View all issues with New status.
Discussion:
31.11.2.4 [syncstream.syncbuf.members] p2 says:
Postconditions: On success, the associated output is empty.
Are there any postconditions on failure? If part of the associated output was written, is that
part still in the associated output? Will another call to emit() duplicate that part?
[2020-11-21; Reflector prioritization]
Set priority to 3 during reflector discussions.
Proposed resolution:
duration and time_pointSection: 32.2.5.4 [thread.req.lockable.timed], 32.6.4.3 [thread.timedmutex.requirements] Status: New Submitter: Tim Song Opened: 2020-11-14 Last modified: 2020-11-21
Priority: 3
View all issues with New status.
Discussion:
The timed lockable and mutex requirements currently use "rel_time denotes an object of an
instantiation of duration, and abs_time denotes an object of an instantiation of time_point"
to define the variables used to specify the timed lock functions. During LWG review of
P2160R0, it was noted that this definition is deficient in two
aspects:
It doesn't allow for cv-qualification of rel_time and abs_time
For time_points, it should require that the clock is a real Cpp17Clock. We impose
that requirement via 32.2.1 [thread.req.paramname], but there are no template parameters named
Clock here.
[2020-11-21; Reflector prioritization]
Set priority to 3 during reflector discussions.
Proposed resolution:
basic_syncbuf-related manipulators refer to some Allocator without defining itSection: 31.7.6.5 [ostream.manip] Status: New Submitter: Jonathan Wakely Opened: 2020-11-15 Last modified: 2020-11-21
Priority: 3
View all other issues in [ostream.manip].
View all issues with New status.
Discussion:
From this editorial issue request:
The three basic_syncbuf-related manipulators emit_on_flush, noemit_on_flush,
and flush_emit use in their Effects: elements the following wording:
"If
os.rdbuf()is abasic_syncbuf<charT, traits, Allocator>*, calledbuffor the purpose of exposition, calls […]
There are two problems with that wording (even when considering the helpful note following p8): First,
the type Allocator is not defined elsewhere (e.g. it is not part of the function signature)
and second, os.rdbuf() has type basic_streambuf<charT, traits>* and not any
other type.
SYNCBUF to detect basic_syncbuf during the work
on the above mentioned editorial issue to solve these problems it turned out that the suggested wording
fix would introduce an apparently normative change that the syncbuf type must not use a
program-defined specialization.
[2020-11-21; Reflector prioritization]
Set priority to 3 during reflector discussions.
Proposed resolution:
This wording is relative to N4868.
This proposed wording is known to be incorrect, but is nonetheless depicted to present the overall idea.
Modify 31.7.6.5 [ostream.manip] as indicated:
-1- Each instantiation of any of the function templates specified in this subclause is a designated addressable function (16.4.5.2.1 [namespace.std]).
-?- In this subclause,SYNCBUF(p)for a pointerpof typeB*is determined as follows. If*pis a base class subobject of an object of typeS, whereSis a specialization generated from thebasic_syncbufprimary template, andis_convertible_v<S*, B*>istrue, thenSYNCBUF(p)isdynamic_cast<S*>(p). Otherwise,SYNCBUF(p)isstatic_cast<void*>(nullptr). [Note ?: To work around the issue that theAllocatortemplate argument ofScannot be deduced, implementations can introduce an intermediate base class tobasic_syncbufthat manages itsemit_on_syncflag. — end note] […]template<class charT, class traits> basic_ostream<charT, traits>& emit_on_flush(basic_ostream<charT, traits>& os);Let
-8- Effects: IfpbeSYNCBUF(os.rdbuf()).pis not nullos.rdbuf()a, callsbasic_syncbuf<charT, traits, Allocator>*, calledbuffor the purpose of expositionp. Otherwise this manipulator has no effect.buf->set_emit_on_sync(true)[Note 1: To work around the issue that the Allocator template argument cannot be deduced, implementations can introduce an intermediate base class to-9- Returns:basic_syncbufthat manages itsemit_on_syncflag. — end note]os.template<class charT, class traits> basic_ostream<charT, traits>& noemit_on_flush(basic_ostream<charT, traits>& os);Let
-10- Effects: IfpbeSYNCBUF(os.rdbuf()).pis not nullos.rdbuf()a, callsbasic_syncbuf<charT, traits, Allocator>*, calledbuffor the purpose of expositionp. Otherwise this manipulator has no effect. -11- Returns:buf->set_emit_on_sync(false)os.template<class charT, class traits> basic_ostream<charT, traits>& flush_emit(basic_ostream<charT, traits>& os);Let
-12- Effects: CallspbeSYNCBUF(os.rdbuf()).os.flush(). Then, ifpis not nullos.rdbuf()a, callsbasic_syncbuf<charT, traits, Allocator>*, calledbuffor the purpose of expositionp. -13- Returns:buf->emit()os.
chrono::ceil has surprising requirementSection: 30.5.8 [time.duration.cast] Status: New Submitter: Jonathan Wakely Opened: 2020-11-18 Last modified: 2024-09-19
Priority: 3
View all other issues in [time.duration.cast].
View all issues with New status.
Discussion:
30.5.8 [time.duration.cast] p7 requires that the return value is "The least result t
representable in ToDuration for which t >= d".
chrono::ceil<chrono::microseconds>(chrono::duration<float, milli>(m)).count()
is required to be the smallest integer n such that (float)n == m*1000.0f, which might be less
than the mathematically correct value of m × 1000.
(The specific values below assume float uses the IEEE binary32 format and default rounding, but
similar problems will exist for other formats, even if the specific values are different.)
For example, if m == 13421772.0f then the naively expected result is n == 13421772000, but
the standard requires n == 13421771265, a significantly lower value. This surprising result is a
consequence of how the chrono::ceil spec interacts with floating-point arithmetic, due to the fact that
for the integers in the range [13421770753, 13421772799], only one can be exactly represented as
32-bit float. All but that one will be rounded to a different value when converted to float.
A straightforward implementation of chrono::ceil will produce (long long)(13421772.0f * 1000)
which is 13421771776, which is less than the expected result, but compares equal using the t >= d
expression. That expression converts both operands to their common_type, which is
chrono::duration<float, micro>. That means we compare (float)13421771776 >= (13421772.0f * 1000)
which is true. But the spec requires an even worse result. All integers in [13421771265, 13421771776)
are also rounded to that value when converted to float. That means chrono::microseconds(13421771265)
is "the least result representable in ToDuration for which t >= d".
Meeting the "least result" requirement is impractical, and unhelpful. The straightforward result 13421771776
is already lower than the naively expected result (which is surprising for a "ceil" function). To meet the
standard's requirements the implementation would have to do extra work, just to produce an even lower (and even
more surprising) result.
It might be impractical to require the naively expected value to be returned (the additional work might have
unacceptable performance implications), but the standard should at least permit the straightforward result
instead of requiring an even worse one.
The same problem almost certainly exists for chrono::floor in reverse.
[2020-11-29; Reflector prioritization]
Set priority to 3 during reflector discussions.
[2024-09-19; Jonathan adds a note]
Another problem discovered by STL occurs when the result is floating-point. We can't just add 1. In fact, there is no requirement for whole-numberness.
For example, when converting from double to float:
chrono::floor<duration<float>>(duration<double>(0.1))
This produces the result duration<float>(-0.9f)
with the reference implementation in P0092R1,
and the implementations in libstdc++, libc++, and MSVC.
This is because 0.1f <= 0.1 is false,
so the result is duration<float>(0.1f - 1.0f),
which is not the greatest value representable that is not greater than 1.0.
The correct result according to the standard would be
duration<float>(nexttoward(0.1f, -HUGE_VAL)),
but we can't use nexttoward for arbitrary treat_as_floating_point types,
only for float, double and long double.
STL found cases where
ceil<duration<float>>(duration<double>(x))
produces a value that is lower than x, e.g. for x = 13421771263.0
the result is 13421770752.0f.
A possible resolution for this problem would be to make ceil and floor
behave exactly like duration_cast when the result is a floating-point type.
This would still permit a ceil that is smaller than the input
(and a floor result that is larger) but that's just a consequence of
converting to a floating-point type with less precision.
We could also specify that for non-floating-point result types,
the effects should be what all known implementations do.
That would mean the behaviour is at least predictable and explainable,
even if the result is not always the correct mathematical value.
Proposed resolution:
This wording is relative to N4988.
Modify 30.5.8 [time.duration.cast] as indicated:
template<class ToDuration, class Rep, class Period> constexpr ToDuration floor(const duration<Rep, Period>& d);-4- Constraints:
ToDurationis a specialization ofduration.-5-
Returns: The greatest resulttrepresentable inToDurationfor whicht <= d.Effects: Equivalent to:
auto t = duration_cast<ToDuration>(d); if constexpr (treat_as_floating_point_v<typename ToDuration::rep>) return t; else if (t <= d) return t; else return --t;template<class ToDuration, class Rep, class Period> constexpr ToDuration ceil(const duration<Rep, Period>& d);-6- Constraints:
ToDurationis a specialization ofduration.-7-
Returns: The least resulttrepresentable inToDurationfor whicht >= d.Effects: Equivalent to:
auto t = duration_cast<ToDuration>(d); if constexpr (treat_as_floating_point_v<typename ToDuration::rep>) return t; else if (t >= d) return t; else return ++t;
condition_variable::wait_for is overspecifiedSection: 32.7.4 [thread.condition.condvar] Status: New Submitter: Jonathan Wakely Opened: 2020-11-18 Last modified: 2024-06-18
Priority: 3
View other active issues in [thread.condition.condvar].
View all other issues in [thread.condition.condvar].
View all issues with New status.
Discussion:
32.7.4 [thread.condition.condvar] p24 says:
Effects: Equivalent to:
return wait_until(lock, chrono::steady_clock::now() + rel_time);
This is overspecification, removing implementer freedom to make cv.wait_for(duration<float>(1)) work accurately.
steady_clock::now() + duration<float>(n) is time_point<steady_clock,
duration<float, steady_clock::period>>. If the steady clock's period is std::nano
and its epoch is the time the system booted, then in under a second a 32-bit float becomes unable
to exactly represent those time_points! Every second after boot makes duration<float, nano>
less precise.
This means that adding a duration<float> to a time_point (or duration)
measured in nanoseconds is unlikely to produce an accurate value. Either it will round down to a value less
than now(), or round up to one greater than now() + 1s. Either way, the wait_for(rel_time)
doesn't wait for the specified time, and users think the implementation is faulty.
A possible solution is to use steady_clock::now() + ceil<steady_clock::duration>(rel_time)
instead. This converts the relative time to a suitably large integer, and then the addition is not affected
by floating-point rounding errors due to the limited precision of 32-bit float. Libstdc++ has been
doing this for nearly three years. Alternatively, the standard could just say that the relative timeout is
converted to an absolute timeout measured against the steady clock, and leave the details to the implementation.
Some implementations might not be affected by the problem (e.g. if the steady clock measures milliseconds,
or processes only run for a few seconds and use the process start as the steady clock's epoch).
This also affects the other overload of condition_variable::wait_for, and both overloads of
condition_variable_any::wait_for.
[2020-11-29; Reflector prioritization]
Set priority to 3 during reflector discussions.
[2024-06-18; Jonathan adds wording]
Proposed resolution:
This wording is relative to N4981.
Modify 32.7.1 [thread.condition.general] as indicated, adding a new paragraph to the end:
-6- The definitions in 32.7 [thread.condition] make use of the following exposition-only function:template<class Dur> chrono::steady_clock::time_point rel-to-abs(const Dur& rel_time) { return chrono::steady_clock::now() + chrono::ceil<chrono::steady_clock::duration>(rel_time); }
Modify 32.7.4 [thread.condition.condvar] as indicated:
template<class Rep, class Period> cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time);-23- Preconditions:
lock.owns_lock()istrueandlock.mutex()is locked by the calling thread, and either [...]-24- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time));[...]
template<class Rep, class Period, class Predicate> cv_status wait_for(unique_lock<mutex>& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);-35- Preconditions:
lock.owns_lock()istrueandlock.mutex()is locked by the calling thread, and either [...]-36- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time), std::move(pred));[Note 8: There is no blocking if
pred()is initiallytrue, even if the timeout has already expired. — end note]
Modify 32.7.5.2 [thread.condvarany.wait] as indicated:
template<class Lock, class Rep, class Period> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);-11- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time));[...]
template<class Lock, class Rep, class Period, class Predicate> cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);-19- Effects: Equivalent to:
return wait_until(lock,chrono::steady_clock::now() + rel_timerel-to-abs(rel_time), std::move(pred));
Section: 19.6.3.4 [stacktrace.entry.query] Status: Open Submitter: Thomas Köppe Opened: 2020-12-02 Last modified: 2023-01-11
Priority: 2
View all issues with Open status.
Discussion:
The specification of 19.6.3.4 [stacktrace.entry.query] uses the terms "presumed or actual name of the source file" and "actual line number". It makes reference to 15.12 [cpp.predefined], which introduces the term "presumed". It does not clearly define the term, but it describes how the presumed values can be modified with preprocessor directives. However, there is no definition whatsoever of "actual".
The term should either be defined, or we should strike the "actual" parts of the stacktrace wording. We should consult implementers about this. I don't have a proposed resolution, but if we want to keep "actual", then perhaps we should define both "presumed" and "actual" in 15.12 [cpp.predefined].[2021-01-15; Telecon prioritization]
Set priority to 2 following reflector and telecon discussions.
[2023-01-11; LWG telecon]
We want to know the original intended meaning of "actual line number" here.
Presumably debuginfo stored in binaries uses a real line number in the source,
not one that might have been set by a #line directive.
The "presumed or actual" wording was added in R2 of the paper, possibly
as a result of LWG review.
Proposed resolution:
Section: 16.4.5.9 [res.on.arguments] Status: New Submitter: Gonzalo Brito Gadeschi Opened: 2020-12-08 Last modified: 2021-01-15
Priority: 3
View all other issues in [res.on.arguments].
View all issues with New status.
Discussion:
The intent of LWG 1204(i) is to allow standard library APIs accepting rvalue arguments:
to move from their arguments, e.g., without having to specify that they might do this as part of their Effects clause, and
to assume that rvalue arguments do not alias any pointer in the scope of the standard library API,
e.g., to allow vector's push_back(T&& t) to assume that t is not an
element of the vector.
The current wording in 16.4.5.9 [res.on.arguments]/1.3 states:
If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument.
This sentence is not clear about the scope in which the reference can be assumed to be unique, and it does not explicitly state that the function can modify the argument, e.g., to move from it.
If the scope of the "unique reference" is "whole program scope", this example:
void example(vector<int>& a, int* b)
{
int* c = b; // reference to object pointed at by b
a.push_back(move(*b)); // UB: rvalue reference aliases c: not unique in whole-program scope
assert(c == b); // FAILS: if rvalue reference to *b is unique, b is unique, and c == b is false
}
exhibits UB because the implementation may assume that the reference to b is unique, which
does not hold since c is also a reference to b.
restrict. This allows aliasing optimizations, for example:
void std_api(int&& a, int&& b); // allowed to assume thataandbdo not alias int a, b, c; std_api(move(a), move(b)); // OK: two unique references instd_apistd_api(move(c), move(c)); // UB:aandbalias
See llvm Bug 48238 for a bug tracking the implementation of these optimizations in clang.
This also allows optimizingvector::push_back(T&& t) since if t does not
alias any pointer in vector::push_back's scope, it also does not alias this,
this->data(), (*this)[0], etc.
[2021-01-15; Telecon prioritization]
Set priority to 3 following reflector and telecon discussions.
Proposed resolution:
This wording is relative to N4868.
Modify 16.4.5.9 [res.on.arguments] as indicated:
-1- Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.
(1.1) — If an argument to a function has an invalid value (such as a value outside the domain of the function or a pointer invalid for its intended use), the behavior is undefined.
(1.2) — If a function argument is described as being an array, the pointer actually passed to the function shall have a value such that all address computations and accesses to objects (that would be valid if the pointer did point to the first element of such an array) are in fact valid.
(1.3) — If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to
[Example ?:this argumentthe value within the function's scope and may move from it.void std_api(int&& a); int a; std_api(move(a)); //ais in an unspecified but valid state— end example]
[Example ?:void std_api(int&& a, int&& b); int a, b, c; std_api(move(a), move(b)); // OK:int&& aandint&& bdo not alias std_api(move(c), move(c)); // UB:int&& aandint&& balias— end example]
[Example ?:std::vector<int> a = {...}; a.push_back(move(42)); // OK: unique reference a.push_back(move(a[0])); // UB:(*this)[0]and rvalue argument alias— end example]
[Note 1: If the parameter is a generic parameter of the formT&&and an lvalue of typeAis bound, the argument binds to an lvalue reference (13.10.3.2 [temp.deduct.call]) and thus is not covered bythe previous sentencethis item. — end note][Note 2: If a program casts an lvalue to an xvalue while passing that lvalue to a library function (e.g., by calling the function with the argumentstd::move(x)), the program is effectively asking that function to treat that lvalue as a temporary object. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. — end note]
Section: 22.10.19 [unord.hash] Status: New Submitter: Jonathan Wakely Opened: 2020-12-28 Last modified: 2021-01-29
Priority: 3
View all other issues in [unord.hash].
View all issues with New status.
Discussion:
See this editorial issue.
22.10.19 [unord.hash] p5.4 ends with "shall not throw an exception unlesshash<Key>
is a program-defined specialization that depends on at least one program-defined type."
This seems wrong, because hash<optional<T>> is not a program-defined specialization,
but it might throw if hash<T> can throw. There are also other partial specializations
of std::hash defined in the standard library but that depend on program-defined specializations
and so can throw.
[2021-01-29; reflector prioritization]
Set priority to 3 following reflector discussions. It was pointed out that this wording could be simplified if 3513(i) changes the definition of program-defined type.Proposed resolution:
Section: 3.43 [defns.prog.def.type] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2020-12-29 Last modified: 2021-01-30
Priority: 3
View all issues with New status.
Discussion:
Consider the following definitions:
3.42 [defns.prog.def.spec]
program-defined specialization
<library> explicit template specialization or partial specialization that is not part of the C++ standard library and not defined by the implementation
and
3.43 [defns.prog.def.type]
program-defined type
<library> non-closure class type or enumeration type that is not part of the C++ standard library and not defined by the implementation, or a closure type of a non-implementation-provided lambda expression, or an instantiation of a program-defined specialization [Note 1 to entry: Types defined by the implementation include extensions (4.1) and internal types used by the library. — end note]
A program-defined type is either a type or an instantiation. "program-defined type" is used in 16.4.5.2.1 [namespace.std] p2 to give permission to specialize standard class templates:
Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace
stdprovided that (a) the added declaration depends on at least one program-defined type and (b) the specialization meets the standard library requirements for the original template.
ISO requires that the terms in Clause 3 be substitutable with their definitions. If this were done for "program-defined type", we'd end up with "or an instantiation of a program-defined specialization". It's fine to depend on a type, but not an instantiated type, because all you need is the name of its specialization (its type) as a template argument to explicitly or partially specialize a template.
[2021-01-29; reflector prioritization]
Set priority to 3 following reflector discussions. It was pointed out that it might be easier to resolve 3512(i) if this issue changes the definition of program-defined type.Proposed resolution:
This wording is relative to N4878.
Modify 3.43 [defns.prog.def.type] as indicated:
3.43 [defns.prog.def.type]
program-defined type
<library> non-closure class type or enumeration type that is not part of the C++ standard library and not defined by the implementation, or a closure type of a non-implementation-provided lambda expression, oran instantiationa name of a program-defined specialization or instantiation thereof [Note 1 to entry: Types defined by the implementation include extensions (4.1 [intro.compliance]) and internal types used by the library. — end note]
thread::id spaceship may be inconsistent with equalitySection: 32.4.3.2 [thread.thread.id] Status: New Submitter: Casey Carter Opened: 2021-01-26 Last modified: 2021-03-12
Priority: 3
View all other issues in [thread.thread.id].
View all issues with New status.
Discussion:
32.4.3.2 [thread.thread.id]/5-7 specify the behavior of == and <=> for
std::thread::id:
bool operator==(thread::id x, thread::id y) noexcept;-5- Returns:
trueonly ifxandyrepresent the same thread of execution or neitherxnoryrepresents a thread of execution.strong_ordering operator<=>(thread::id x, thread::id y) noexcept;-6- Let
-7- Returns:P(x, y)be an unspecified total ordering overthread::idas described in 25.8.strong_ordering::lessifP(x, y)istrue. Otherwise,strong_ordering::greaterifP(y, x)istrue. Otherwise,strong_ordering::equal.
"Unspecified total ordering" provides too much freedom, since it does not require that !P(x, y)
holds when x and y both represent the same thread of execution or both represent no
thread of execution. A conforming implementation could return strong_ordering::equal from
<=> for a pair of thread::id values for which == returns false.
We should guarantee consistency of == and <=> for thread::id to
preserve sanity of the programming model.
[2021-03-12; Reflector poll]
Set priority to 3 following reflector poll.
Proposed resolution:
This wording is relative to N4878.
Modify 32.4.3.2 [thread.thread.id] as indicated:
strong_ordering operator<=>(thread::id x, thread::id y) noexcept;-6- Let
-7- Returns:P(x, y)bean unspecifieda total ordering overthread::idas described in 26.8 [alg.sorting], unspecified except thatP(x, y)isfalsewhenxandyboth represent the same thread of execution, or when neither represents a thread of execution.strong_ordering::lessifP(x, y)istrue. Otherwise,strong_ordering::greaterifP(y, x)istrue. Otherwise,strong_ordering::equal.
Section: 23.4.3.1 [map.overview], 23.4.4.1 [multimap.overview], 23.5.3.1 [unord.map.overview], 23.5.4.1 [unord.multimap.overview] Status: New Submitter: Mike Spertus Opened: 2021-03-09 Last modified: 2021-04-20
Priority: 3
View other active issues in [map.overview].
View all other issues in [map.overview].
View all issues with New status.
Discussion:
The resolution for LWG 3025(i) enabled code like the following to be accepted:
map m1{{pair{1, 2}, {3, 4}}, less<int>()};
but breaks code that had been previously working like the following
using value_type = pair<const int, int>;
map m2{{value_type{1, 2}, {3, 4}}, less<int>()};
as shown on godbolt.
[Acknowledgment to Tim Song and Arthur O'Dwyer for independently pointing out this case on the LWG mailing list][2021-04-20; Reflector poll]
Priority set to 3. Three preferences expressed for Option B, none for A.
Proposed resolution:
This wording is relative to N4878.
We present two partial wording options for
std::map, denoted by (A) and (B) below. If the committee accepts one of them, we will complete them to all key-value containers.
(A) Wording option 1: In this option, we restore the deduction guide that was removed in LWG 3025(i) while maintaining the one that was added, demonstrates this working.
Modify 23.4.3.1 [map.overview] as indicated:
[…]
template<class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T>>>
map(initializer_list<pair<Key, T>>, Compare = Compare(), Allocator = Allocator())
-> map<Key, T, Compare, Allocator>;
template<class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key, T>>>
map(initializer_list<pair<const Key, T>>, Compare = Compare(), Allocator = Allocator())
-> map<Key, T, Compare, Allocator>;
template<class InputIterator, class Allocator>
map(InputIterator, InputIterator, Allocator)
-> map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>,
less<iter-key-type<InputIterator>>, Allocator>;
template<class Key, class T, class Allocator>
map(initializer_list<pair<Key, T>>, Allocator) -> map<Key, T, less<Key>, Allocator>;
template<class Key, class T, class Allocator>
map(initializer_list<pair<const Key, T>>, Allocator) -> map<Key, T, less<Key>, Allocator>;
[…]
(B) Wording option 2: This one follows Tim Song's suggestion:
"It seems that the cleanest fix is to 1) disallow the initializer_list<value_type> constructors from being
used for CTAD, and 2) change the guides to use remove_const_t<Key>." This change has been tested locally
with g++ similar to the above godbolt.
Modify 23.4.3.1 [map.overview] as indicated:
[…]
// types
using key_type = Key;
using mapped_type = T;
using value_type = type_identity_t<pair<const Key, T>>;
[…]
template<class Key, class T, class Compare = less<remove_const_t<Key>>,
class Allocator = allocator<pair<const Key, T>>>
map(initializer_list<pair<Key, T>>, Compare = Compare(), Allocator = Allocator())
-> map<remove_const_t<Key>, T, Compare, Allocator>;
template<class InputIterator, class Allocator>
map(InputIterator, InputIterator, Allocator)
-> map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>,
less<iter-key-type<InputIterator>>, Allocator>;
template<class Key, class T, class Allocator>
map(initializer_list<pair<Key, T>>, Allocator)
-> map<remove_const_t<Key>, T, less<remove_const_t<Key>>, Allocator>;
[…]
ranges::set_intersection and ranges::set_difference algorithm requirements are too strictSection: 26.8.7.4 [set.intersection], 26.8.7.5 [set.difference] Status: LEWG Submitter: Alexander Bessonov Opened: 2021-03-16 Last modified: 2021-04-20
Priority: 3
View all issues with LEWG status.
Discussion:
The std::mergeable concept requires elements of both source ranges to be copyable to the output iterator, while
the standard specifically tells that both algorithms ranges::set_intersection and ranges::set_difference
only use the first range as the source of elements to be copied into output. The following code snippet illustrates the problem:
#include <vector>
#include <ranges>
#include <algorithm>
#include <cassert>
int main()
{
std::vector<std::pair<int, int>> v1;
std::vector<int> v2;
assert(std::ranges::is_sorted(v1));
assert(std::ranges::is_sorted(v2));
std::vector<std::pair<int, int>> v3;
// Compilation error on the following line:
std::ranges::set_intersection(v1, v2, std::back_inserter(v3),
std::less{}, [](const auto& p) { return p.first; });
}
The proposed solution is to introduce a new concept. It could be declared "exposition-only" and is worded
half-mergeable below:
template<class I1, class I2, class Out, class R = ranges::less,
class P1 = identity, class P2 = identity>
concept half-mergeable =
input_iterator<I1> &&
input_iterator<I2> &&
weakly_incrementable<Out> &&
indirectly_copyable<I1, Out> &&
// indirectly_copyable<I2, Out> && <— this line removed
indirect_strict_weak_order<R, projected<I1, P1>, projected<I2, P2>>;
After such template is introduced, std::mergeable may be defined based on it:
template<class I1, class I2, class Out, class R = ranges::less,
class P1 = identity, class P2 = identity>
concept mergeable = half-mergeable<I1, I2, Out, R, P1, P2> &&
indirectly_copyable<I2, Out>;
See also the related discussion on reddit.
[2021-04-20; Reflector poll]
Priority set to 3. Send to LEWG.
Proposed resolution:
This wording is relative to N4878.
Modify 24.2 [iterator.synopsis], header <iterator> synopsis, as indicated:
[…] // 24.3.7.7 [alg.req.mergeable], concept mergeable template<class I1, class I2, class Out, class R = ranges::less, class P1 = identity, class P2 = identity> concept half-mergeable = see below; // exposition only template<class I1, class I2, class Out, class R = ranges::less, class P1 = identity, class P2 = identity> concept mergeable = see below; […]
Modify 24.3.7.7 [alg.req.mergeable] as indicated:
23.3.7.7 Concept
-1- Themergeable[alg.req.mergeable]mergeableconcept specifies the requirements of algorithms that merge sorted sequences into an output sequence by copying elements.template<class I1, class I2, class Out, class R = ranges::less, class P1 = identity, class P2 = identity> concept half-mergeable = // exposition only input_iterator<I1> && input_iterator<I2> && weakly_incrementable<Out> && indirectly_copyable<I1, Out> && indirect_strict_weak_order<R, projected<I1, P1>, projected<I2, P2>>; template<class I1, class I2, class Out, class R = ranges::less, class P1 = identity, class P2 = identity> concept mergeable = half-mergeable<I1, I2, Out, R, P1, P2> &&input_iterator<I1> && input_iterator<I2> && weakly_incrementable<Out> && indirectly_copyable<I1, Out> &&indirectly_copyable<I2, Out>&& indirect_strict_weak_order<R, projected<I1, P1>, projected<I2, P2>>;
Modify 26.8.7.4 [set.intersection] as indicated:
[…] template<input_iterator I1, sentinel_for<I1> S1, input_iterator I2, sentinel_for<I2> S2, weakly_incrementable O, class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity> requires half-mergeablemergeable<I1, I2, O, Comp, Proj1, Proj2> constexpr ranges::set_intersection_result<I1, I2, O> ranges::set_intersection(I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}); template<input_range R1, input_range R2, weakly_incrementable O, class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity> requires half-mergeablemergeable<iterator_t<R1>>, iterator_t<R2>, O, Comp, Proj1, Proj2> constexpr ranges::set_intersection_result<borrowed_iterator_t<R1>, borrowed_iterator_t<R2>, O> ranges::set_intersection(R1&& r1, R2&& r2, O result, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});[…]
-6- Remarks: Stable (16.4.6.8 [algorithm.stable]). If[first1, last1)containsmelements that are equivalent to each other and[first2, last2)containsnelements that are equivalent to them, the firstmin(m, n)elements are copied from the first range to the output range, in order.
Modify 26.8.7.5 [set.difference] as indicated:
[…] template<input_iterator I1, sentinel_for<I1> S1, input_iterator I2, sentinel_for<I2> S2, weakly_incrementable O, class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity> requires half-mergeablemergeable<I1, I2, O, Comp, Proj1, Proj2> constexpr ranges::set_difference_result<I1, O> ranges::set_difference(I1 first1, S1 last1, I2 first2, S2 last2, O result, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {}); template<input_range R1, input_range R2, weakly_incrementable O, class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity> requires half-mergeablemergeable<iterator_t<R1>>, iterator_t<R2>, O, Comp, Proj1, Proj2> constexpr ranges::set_difference_result<borrowed_iterator_t<R1>, O> ranges::set_difference(R1&& r1, R2&& r2, O result, Comp comp = {}, Proj1 proj1 = {}, Proj2 proj2 = {});[…]
-6- Remarks: If[first1, last1)containsmelements that are equivalent to each other and[first2, last2)containsnelements that are equivalent to them, the lastmax(m - n, 0)elements from[first1, last1)is copied to the output range, in order.
noexcept for std::rbegin/rend for arrays and
initializer_listSection: 24.7 [iterator.range] Status: New Submitter: Jiang An Opened: 2021-03-21 Last modified: 2025-07-01
Priority: 3
View other active issues in [iterator.range].
View all other issues in [iterator.range].
View all issues with New status.
Discussion:
Overloads for std::rbegin/rend for built-in arrays and std::initializer_list's has
no preconditions and never throw exceptions, thus should be noexcept. LWG 2280(i)
addressed a similar issue for std::begin/end.
template<class T, size_t N> constexpr reverse_iterator<T*> rbegin(T (&array)[N]) noexcept; template<class T, size_t N> constexpr reverse_iterator<T*> rend(T (&array)[N]) noexcept; template<class E> constexpr reverse_iterator<const E*> rbegin(initializer_list<E> il) noexcept; template<class E> constexpr reverse_iterator<const E*> rend(initializer_list<E> il) noexcept;
If this change is accepted, we may also specify conditional noexcept for std::crbegin/crend
(in 24.7 [iterator.range] p14, 15), by adding noexcept(noexcept(std::rbegin/crend(c))), like in
LWG 2280(i).
[2021-03-21; Daniel comments]
There is intentionally no P/R provided at this point, but I'm volunteering to provide it if we got feedback whether
adding conditional noexcept specifiers similar to those provided by LWG 2280(i) would
be preferred or not.
[2021-04-20; Reflector poll]
Priority set to 3.
Jonathan: This would create a strange situation where std::rbegin
and std::crbegin on an initializer_list are noexcept but
std::begin and std::cbegin aren't guaranteed to be
(because an initializer_list uses the generic std::begin
and std::cbegin overloads, which have no conditional noexcept).
Casey: I don't think we should mark these rbegin/rend overloads noexcept
without making the pertinent reverse_iterator constructors
conditionally noexcept.
[2025-07-01; P3623R0 would resolve this]
Proposed resolution:
Section: 16.2 [library.c] Status: New Submitter: Jiang An Opened: 2021-03-29 Last modified: 2021-04-20
Priority: 2
View all issues with New status.
Discussion:
P0551R3 has made almost all standard library functions non-addressable, including all functions from the C standard library. However, C17 (and the latest C23 working draft) explicitly allows taking address of a C standard library function in 7.1.4/1.
Should we require something like "every function from the C standard library is addressable unless it is overloaded", in order to minimize the incompatibility with C? Or explicitly say something in C.8 [diff.library] if such requirement is not wanted?[2021-04-20; Reflector poll]
Priority set to 2.
Proposed resolution:
Section: 16.4.5.3 [reserved.names] Status: New Submitter: Hubert Tong Opened: 2021-05-10 Last modified: 2021-05-20
Priority: 3
View all other issues in [reserved.names].
View all issues with New status.
Discussion:
The C standard has a section called "Future library directions". This is subclause 7.31 in C17, which is the version that the C++ working draft references. C reserves the names described in that subclause for its standard library, allowing C library vendors to introduce some names from future standards as conforming, "orthogonal" extensions. ISO C++ does not appear to reserve these names in a clear way. In particular, 16.4.5.3 [reserved.names] has wording about names and function signatures "from the C standard library declared with external linkage", but C's "Future library directions" describes names that "may be added to the declarations in [some] header[s]".
Extra clarity regarding whether these names are actually intended to be reserved by C++ would be appreciated.[2021-05-20; Reflector poll]
Priority set to 3.
Proposed resolution:
Section: 16.3.2.3 [structure.requirements] Status: New Submitter: Tim Song Opened: 2021-05-23 Last modified: 2021-05-26
Priority: 3
View all other issues in [structure.requirements].
View all issues with New status.
Discussion:
16.3.2.3 [structure.requirements] p9 says:
A declaration may explicitly impose requirements through its associated constraints (13.5.3 [temp.constr.decl]). When the associated constraints refer to a concept (13.7.9 [temp.concept]), the semantic constraints specified for that concept are additionally imposed on the use of the declaration.
There are at least two issues with this wording:
First, "associated constraints" is a Core term that refers to the constraints on a declaration after normalization, at which point direct uses of concepts have been decomposed into the constituent atomic constraints and are no longer visible.
Second, "refers to" is too vague. Does !C<T> "refer to"
C and impose its semantic constraints? Does C1<T> || C2<T>
"refer to" both C1 and C2 and therefore impose the semantic
constraints of both?
[2021-05-26; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 23.2.7.1 [associative.reqmts.general], 23.2.8.1 [unord.req.general] Status: New Submitter: Joaquín M López Muñoz Opened: 2021-08-04 Last modified: 2024-01-29
Priority: 3
View other active issues in [associative.reqmts.general].
View all other issues in [associative.reqmts.general].
View all issues with New status.
Discussion:
For the expression a.merge(a2), it is not explicitly stated whether a2 can be the
same object as a. libstdc++-v3 and libc++ seemingly assume this is not allowed, as the following code
produces an infinite loop with both standard library implementations:
#include <set>
int main()
{
std::multiset<int> c={0, 0};
c.merge(c);
}
A strict reading of postconditions seems to ban the case where a and a2 are the same:
23.2.7.1 [associative.reqmts.general]: "Iterators referring to the transferred elements […]
now behave as iterators into a, not into a2": if a and a2 are the same,
a transferred iterator can't be both an iterator to a and not an iterator to a2.
23.2.8.1 [unord.req.general]: "Iterators referring to the transferred elements and all iterators
referring to a will be invalidated, but iterators to elements remaining in a2 will remain valid":
if a and a2 are the same, an iterator can't both be invalidated and remain valid.
Even if a provision is made that, when a and a2 are the same, no elements are transferred by
convention, 23.2.8.1 [unord.req.general] would still implicitly ban the case, as all iterators would be
invalidated but the iterators to the remaining elements (again, all iterators) would remain valid, which is
contradictory.
std::list take inconsistent approaches:
splice(const_iterator position, list& x) requires that source and destination be not the same.
splice(const_iterator position, list& x, const_iterator i) implicitly allows addressof(x) == this,
as the case position == i is taken care of.
std::list::merge explicitly allows the case addressof(x) == this (resulting in a no-op).
[2021-08-20; Reflector poll]
Set priority to 3 after reflector poll.
Tim Song commented:
"I think the current PR of LWG2414(i) bans this code,
but we might want to have consistency with list::merge instead."
Proposed resolution:
std::async exceptions are handledSection: 32.10.9 [futures.async] Status: New Submitter: Jonathan Wakely Opened: 2021-08-23 Last modified: 2021-09-30
Priority: 3
View other active issues in [futures.async].
View all other issues in [futures.async].
View all issues with New status.
Discussion:
32.10.9 [futures.async] (3.1) says:
Any exception propagated from the execution of
invoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)is stored as the exceptional result in the shared state.
It's not clear whether this includes the evaluation of the decay-copy calls in the calling
thread, or only the invocation of invoke with the results of those decay-copy calls.
invoke, not the
calls to decay-copy. Exceptions from the decay-copy calls are propagated
to the caller of std::async. We should clarify that that's what the standard means.
[2021-09-20; Reflector poll]
Set priority to 3 after reflector poll.
[2021-09-20; Jonathan updates wording to change the Throws: and attempt to align the Effects: with the deferred function case. ]
Previous resolution [SUPERSEDED]:
This wording is relative to N4892.
Modify 32.10.9 [futures.async] as indicated:
template<class F, class... Args> [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>> async(F&& f, Args&&... args); template<class F, class... Args> [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>> async(launch policy, F&& f, Args&&... args);-2- Mandates: […]
-3- Effects: The first function behaves the same as a call to the second function with apolicyargument oflaunch::async | launch::deferredand the same arguments forFandArgs. The second function creates a shared state that is associated with the returnedfutureobject. The further behavior of the second function depends on thepolicyargument as follows (if more than one of these conditions applies, the implementation may choose any of the corresponding policies):
(3.1) — If
launch::asyncis set inpolicy, callsinvoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)(22.10.4 [func.require], 32.4.3.3 [thread.thread.constr]) as if in a new thread of execution represented by athreadobject with the calls todecay-copybeing evaluated in the thread that calledasync. Any return value is stored as the result in the shared state. Any exception propagated from theexecution ofcall toinvoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)invokeis stored as the exceptional result in the shared state. [Note ?: Exceptions from thedecay-copycalls are propagated to the caller. — end note] Thethreadobject is stored in the shared state and affects the behavior of any asynchronous return objects that reference that state.[…]
Proposed resolution:
This wording is relative to N4892.
Modify 32.10.9 [futures.async] as indicated:
template<class F, class... Args> [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>> async(F&& f, Args&&... args); template<class F, class... Args> [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>> async(launch policy, F&& f, Args&&... args);-2- Mandates: […]
-3- Effects: The first function behaves the same as a call to the second function with apolicyargument oflaunch::async | launch::deferredand the same arguments forFandArgs. The second function creates a shared state that is associated with the returnedfutureobject. The further behavior of the second function depends on thepolicyargument as follows (if more than one of these conditions applies, the implementation may choose any of the corresponding policies):
(3.1) — If
launch::asyncis set inpolicy, callsinvoke(decay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args))...)(22.10.4 [func.require], 32.4.3.3 [thread.thread.constr]) as if in a new thread of execution represented by athreadobject with the calls todecay-copybeing evaluated in the thread that calledasync. Any return value is stored as the result in the shared state. Any exception propagated from the execution ofinvoke(is stored as the exceptional result in the shared state, wheredecay-copy(std::forward<F>(f)), decay-copy(std::forward<Args>(args)...)std::move(g), std::move(xyz))gis the result ofdecay-copy(std::forward<F>(f))andxyzis the result ofdecay-copy(std::forward<Args>(args)).... [Note ?: Exceptions from thedecay-copycalls are propagated to the caller. — end note] Thethreadobject is stored in the shared state and affects the behavior of any asynchronous return objects that reference that state.[…]
[…]
-6- Throws:
system_errorifpolicy == launch::asyncand the implementation is unable to start a new thread;if memory for the internal data structures cannot be allocated; or any exception thrown by the initialization of the objects returned by thestd::bad_allocdecay-copycalls.
Section: 22.4.4.2 [tuple.cnstr] Status: New Submitter: Jonathan Wakely Opened: 2021-08-23 Last modified: 2021-09-20
Priority: 3
View other active issues in [tuple.cnstr].
View all other issues in [tuple.cnstr].
View all issues with New status.
Discussion:
We do not specify whether or not short-circuiting is expected to happen for individual conditions stated in a Constraints: element. For example, 22.4.4.2 [tuple.cnstr] p12 says:
Constraints:
sizeof...(Types)equalssizeof...(UTypes)andsizeof...(Types) ≤ 1andis_constructible_v<Ti , Ui>istruefor alli.
It's not even possible to test the is_constructible part unless the first part is true, so presumably it is
expected that the sizeof... expressions are tested first, and so the is_constructible traits don't
even need to be instantiated.
[2021-09-20; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 17.12.3 [cmp.common] Status: New Submitter: Peter Brett Opened: 2021-08-23 Last modified: 2021-09-20
Priority: 3
View all issues with New status.
Discussion:
17.12.3 [cmp.common]/1 says:
The type
common_comparison_categoryprovides an alias for the strongest comparison category to which all of the template arguments can be converted.
A naive reader like me might interpret this as meaning that (1) you attempt to convert the template arguments to comparison categories and then (2) obtain the strongest among them.
However, the intent is in fact to realize the common comparison type notion from 11.10.3 [class.spaceship]/4. To obtain a non-void
result, all the template arguments must be comparison categories, rather than
convertible to comparison categories.
17.12.3 [cmp.common]/2 mildly contradicts the first paragraph:
Remarks: The member typedef-name
typedenotes the common comparison type (11.10.3 [class.spaceship]) ofTs..., the expanded parameter pack, orvoidif any element ofTsis not a comparison category type.
It more precisely states the behaviour, cross-references 11.10.3 [class.spaceship], and uses the correct core terminology for the metafunction that the template represents.
Suggested resolution; Delete 17.12.3 [cmp.common]/1, because it does not provide any information not already more precisely included in 17.12.3 [cmp.common]/2.[2021-09-20; Reflector poll]
Set priority to 3 after reflector poll.
[2021-09-20; Reflector poll]
Jens suggests alternative wording.
Previous resolution [SUPERSEDED]:
This wording is relative to N4892.
Modify 17.12.3 [cmp.common] as indicated:
-1- The typecommon_comparison_categoryprovides an alias for the strongest comparison category to which all of the template arguments can be converted. [Note 1: A comparison category type is stronger than another if they are distinct types and an instance of the former can be converted to an instance of the latter. — end note]template<class... Ts> struct common_comparison_category { using type = see below; };-2- Remarks: The member typedef-name
typedenotes the common comparison type (11.10.3 [class.spaceship]) ofTs..., the expanded parameter pack, orvoidif any element ofTsis not a comparison category type.
Proposed resolution:
This wording is relative to N4892.
Modify 17.12.3 [cmp.common] as indicated:
-1- The type
common_comparison_categoryprovides an alias for the strongest comparison categoryto which all of the template arguments can be convertedamong all the template arguments. [Note 1: A comparison category type is stronger than another if they are distinct types and an instance of the former can be converted to an instance of the latter. — end note]template<class... Ts> struct common_comparison_category { using type = see below; };-2- Remarks: The member typedef-name
typedenotes the common comparison type (11.10.3 [class.spaceship]) ofTs..., the expanded parameter pack, orvoidif any element ofTsis not a comparison category type.
Section: 28.5.2.2 [format.string.std] Status: New Submitter: Mark de Wever Opened: 2021-09-05 Last modified: 2021-12-04
Priority: 2
View other active issues in [format.string.std].
View all other issues in [format.string.std].
View all issues with New status.
Discussion:
The alignment options specified in 28.5.2.2 [format.string.std], Table [tab:format.align] causes an inconsistency when formatting characters. The output differs depending on whether an integer is formatted using a character presentation type or when using a character directly:
format("{:3}", '*'); -> "* " // aligned at the start of the available space
format("{:3c}", 42); -> " *" // aligned at the end of the available space
I expect both calls to return the same value: "* ". The current wording
mixes the type and the presentation type. To me, it seems clearer to adjust to wording
to only use the presentation type. Another approach would be adjusting the wording to
add an exception when an integer type uses the character presentation.
[2021-09-20; Reflector poll]
Set priority to 2 after reflector poll.
Victor said "It mostly looks correct but I think the wording needs a bit more work because we don't mention arithmetic presentation types anywhere."
[2021-11-29; Daniel comments]
This issue touches the same wording area as LWG 3644(i) does.
Proposed resolution:
This wording is relative to N4892.
Modify 28.5.2.2 [format.string.std], Table [tab:format.align], as indicated:
Table 59 — Meaning of align options [tab:format.align] Option Meaning <Forces the field to be aligned to the start of the available space. This is the default when the presentation type is a non-arithmetic type for non-arithmetic types,.charT, andbool, unless an integer presentation type is specified>Forces the field to be aligned to the end of the available space. This is the default when the presentation type is an arithmetic type for arithmetic types other than.charTandboolor when an integer presentation type is specified[…]
std::three_way_comparable_with<T, U, void> can be satisfied but can't be modeledSection: 17.12.4 [cmp.concept] Status: New Submitter: Jiang An Opened: 2021-09-06 Last modified: 2021-10-23
Priority: 3
View all other issues in [cmp.concept].
View all issues with New status.
Discussion:
Due to the current specification of std::common_comparison_category,
compare-as<void, void> is satisfied. And thus given two types T
and U with regular ==, !=, <, >, <=,
and >= but irregular <=> that has return type void,
std::three_way_comparable_with<T, U, void> is satisfied.
std::three_way_comparable_with<T, U, void>
can't be modeled, because Cat(t <=> u) == Cat(C(t) <=> C(u))
is ill-formed if Cat is void (see this godbolt link).
I don't think this issue has any impact on meaningful codes, but it seems to be a hole in the
current specification.
It seems that compares-as<void, void> should be changed to false.
compares-as<NonComparisonCategoryType, void> might need refinement.
(Note: The same problem does not exists for std::three_way_comparable, because
std::three_way_comparable<T, void> can be modeled if the <=> on T
has "regular" definition or its return type is not a comparison category type, but the return value
has "regular" semantics)
[2021-09-20; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
const overload of lazy_split_view::begin should be constrained by const PatternSection: 25.7.16.2 [range.lazy.split.view] Status: New Submitter: Hewill Kang Opened: 2021-09-23 Last modified: 2021-10-14
Priority: 3
View other active issues in [range.lazy.split.view].
View all other issues in [range.lazy.split.view].
View all issues with New status.
Discussion:
Consider the following code snippet:
#include <ranges>
int main() {
auto p = std::views::iota(0)
| std::views::take(1)
| std::views::reverse;
auto r = std::views::single(42)
| std::views::lazy_split(p);
auto f = r.front();
}
r.front() is ill-formed even if r is a forward_range.
This is because the const overload of lazy_split_view::begin is not constrained by the
const Pattern, which makes it still well-formed in such cases. When the const overload
of view_interface<lazy_split_view<V, Pattern>>::front is instantiated, the
subrange{parent_->pattern_} inside lazy_split_view::outer-iterator<true>::operator++()
will cause a hard error since const Pattern is not a range.
[2021-09-24; Daniel comments]
This issue is related to LWG 3592(i).
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4892.
Modify 25.7.16.2 [range.lazy.split.view], class template lazy_split_view synopsis, as indicated:
namespace std::ranges { […] template<input_range V, forward_range Pattern> requires view<V> && view<Pattern> && indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && (forward_range<V> || tiny-range<Pattern>) class lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> { private: […] public: […] constexpr auto begin() { […] } constexpr auto begin() const requires forward_range<V> && forward_range<const V> && forward_range<const Pattern>{ return outer-iterator<true>{*this, ranges::begin(base_)}; } constexpr auto end() requires forward_range<V> && common_range<V> { […] } constexpr auto end() const { if constexpr (forward_range<V> && forward_range<const V> && common_range<const V> && forward_range<const Pattern>) return outer-iterator<true>{*this, ranges::end(base_)}; else return default_sentinel; } }; […] }
reverse_iterator's converting assignment is overconstrainedSection: 24.5.1.4 [reverse.iter.cons], 24.5.4.4 [move.iter.cons] Status: New Submitter: Hewill Kang Opened: 2021-09-26 Last modified: 2021-10-14
Priority: 3
View all other issues in [reverse.iter.cons].
View all issues with New status.
Discussion:
In order to remove the incorrect bi-convertibility of reverse_iterator<int*> and
reverse_iterator<const int*>, LWG 3435(i) adds two constraints to
reverse_iterator's converting assignment, namely
convertible_to<const U&, Iterator> and
assignable_from<Iterator&, const U&>, but since this function only assigns
u.current to current, there is no need to require
convertible_to<const U&, Iterator> — the latter is sufficient.
move_sentinel and
counted_iterator' converting assignment.
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
[Tim Song commented:]
This was
intentional,
but I think we missed the fact that counted_iterator did something
else already. These should probably be made consistent one way or another.
[Tomasz Kamiński commented]
The move_iterator/reverse_iterator were present before C++20,
and this change restores their compatibility with C++17 code,
where only assignment was required.
They are materially different from adapters introduced with C++20,
and I believe we should put more weight into backward compatibility
than consistency with newer iterator wrappers.
Proposed resolution:
This wording is relative to N4892.
Modify 24.5.1.4 [reverse.iter.cons] as indicated:
template<class U> constexpr reverse_iterator& operator=(const reverse_iterator<U>& u);-5- Constraints:
-6- Effects: Assignsis_same_v<U, Iterator>isfalse,andconst U&modelsconvertible_to<Iterator>,assignable_from<Iterator&, const U&>is modeled.u.currenttocurrent. -7- Returns:*this.
Modify 24.5.4.4 [move.iter.cons] as indicated:
[Drafting note: As drive-by fix a missing "Returns:
*this" has been added as well.]
template<class U> constexpr move_iterator& operator=(const move_iterator<U>& u);-5- Constraints:
-6- Effects: Assignsis_same_v<U, Iterator>isfalse,andconst U&modelsconvertible_to<Iterator>,assignable_from<Iterator&, const U&>is modeled.u.currenttocurrent. -?- Returns:*this.
Section: 28.6.7.2 [re.regex.construct], 28.6.10 [re.alg] Status: New Submitter: Jonathan Wakely Opened: 2021-09-27 Last modified: 2021-10-14
Priority: 3
View other active issues in [re.regex.construct].
View all other issues in [re.regex.construct].
View all issues with New status.
Discussion:
ECMAScript says that \0 is an ordinary character and can be matched. POSIX says the opposite:
std::regex{"", 1, regex::basic} should throw an exception?
And std::regex_match(string{"a\0b", 3}, regex{"a.b", regex::basic}) should fail?
The POSIX rule is because those interfaces are specified with NTBS arguments, so there's no way to
distinguish "a\0b" and "a". The C++ interfaces could allow it, but we never specify
any divergence from POSIX, so presumably the rule still applies. Is that what was intended and is it
what we want?
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
syntax_option_type?Section: 28.6.7.2 [re.regex.construct], 28.6.4.2 [re.synopt] Status: New Submitter: Jonathan Wakely Opened: 2021-09-27 Last modified: 2021-10-14
Priority: 3
View other active issues in [re.regex.construct].
View all other issues in [re.regex.construct].
View all issues with New status.
Discussion:
28.6.4.2 [re.synopt] says:
A valid value of type
syntax_option_typeshall have at most one of the grammar elementsECMAScript,basic,extended,awk,grep,egrep, set.
But then we never say what happens if an invalid value is used.
What doesstd::regex(".", std::regex::grep|std::regex::awk) do? Is it undefined? Does it throw?
It seems reasonable for basic_regex constructors to throw if f is not a valid value, i.e.
for each non-default constructor:
Throws:
regex_erroriffis not a valid value, or if […] is not a valid regular expression according to the grammar specified byf.
However, there is no regex_constants::error_code value suitable for this error condition.
flags() returns f." This prevents an
implementation from storing f|ECMAScript in flags() if no grammar element is present in f.
This seems like an unnecessary restriction, and forces implementations to do extra work to check if the
ECMAScript grammar is in use. Arguably, it would even be better to require implementations to set
ECMAScript in flags() if no grammar element was set in the flags passed to the constructor.
This problem was introduced by LWG 2330(i).
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
regex_constants::match_prev_avail is underspecifiedSection: 28.6.4.3 [re.matchflag] Status: New Submitter: Jonathan Wakely Opened: 2021-09-27 Last modified: 2021-10-14
Priority: 3
View all other issues in [re.matchflag].
View all issues with New status.
Discussion:
The standard doesn't say what it means if match_prev_avail is set. Table [tab:re.matchflag] says:
--firstis a valid iterator position. When this flag is set the flagsmatch_not_bolandmatch_not_bowshall be ignored by the regular expression algorithms (28.6.10 [re.alg]) and iterators (28.6.11 [re.iter]).
What difference does it make whether --first is a valid iterator position or not?
--first?
Examples like regex_match("xa"+1, regex("^a"), match_prev_avail)
and regex_match("xa"+1, regex("\\ba"), match_prev_avail)
are presumably supposed to inspect the character at --first
to determine if there is a match.
The standard doesn't specify that *--first is ever inspected,
only that it's a valid character
(which is a useless guarantee if nothing looks at it).
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
regex_traits::locale_type requirementsSection: 28.6.2 [re.req] Status: New Submitter: Jonathan Wakely Opened: 2021-09-28 Last modified: 2021-10-14
Priority: 3
View other active issues in [re.req].
View all other issues in [re.req].
View all issues with New status.
Discussion:
Why is locale_type part of the regular expression traits requirements in 28.6.2 [re.req]?
When would locale_type not be std::locale? What are the requirements on the type?
Does it have to provide exactly the same interface as std::locale, or just some unspecified
interface that a custom regex traits type needs from it? Why is none of this specified?
locale_type in the standard is that it's copy constructible.
Clearly it needs to be default constructible as well, otherwise you can't construct a basic_regex,
since none of them allows passing in a locale, so they have to default construct it (see also LWG 2431(i)).
The other requirements on locale_type are a mystery. Why do we allow custom locale types,
but not say anything about what they should do? Can we just require locale_type to be std::locale?
Is anybody really going to use boost::locale with std::basic_regex, when they
could just use boost::basic_regex instead?
Why does the regular expression traits requirements table say that imbue and getloc
talk about the locale used, "if any". How would there not be one already?
Why is imbuing a locale into a basic_regex a separate operation from compiling the regular expression
pattern? Is the following supposed to change the compiled regex?
std::regex r("[a-z]");
r.imbue(std::locale("en_GB.UTF-8"));
Hasn't the regex constructor already made use of the locale to compile the "[a-z]" pattern,
and so changing the locale is too late? So do we need to do the following to compile the regex with
a specific locale?
std::regex r;
r.imbue(std::locale("en_GB.UTF-8"));
r.assign("[a-z]");
Why require two-stage initialization like this, is it just so that we appear consistent with the
imbue/getloc API of std::ios_base? It works for ios_base,
because the new locale is effective after imbuing it, but for basic_regex the pattern
has already been compiled using the old locale and imbuing a new one can't change that. Is the
basic_regex supposed to store the pattern and recompile it after imbue, or is
this just an inappropriate API for basic_regex?
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
convertible_to and temporary-bound referencesSection: 18.4.4 [concept.convertible] Status: New Submitter: Tim Song Opened: 2021-09-28 Last modified: 2021-10-23
Priority: 3
View other active issues in [concept.convertible].
View all other issues in [concept.convertible].
View all issues with New status.
Discussion:
The semantic requirements of convertible_to express implicit
conversion by means of a function:
To test(FromR (&f)()) {
return f();
}
and it requires that static_cast<To>(f()) be equal to test(f) for some
equality-preserving function f. However, when To is a reference type
but FromR is such that the conversion binds the reference to a temporary, the
static_cast is valid but test(f) would produce a dangling reference.
void-to-void case, but the semantic requirements
wording already excluded that case anyway.
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
std::ranges::iota_view<int, long> has non-subtractable iterator and
sentinel typesSection: 25.6.4.4 [range.iota.sentinel] Status: New Submitter: Jiang An Opened: 2021-09-25 Last modified: 2021-10-14
Priority: 3
View all issues with New status.
Discussion:
Currently std::ranges::iota_view<int, long> uses its special sentinel type, as its
W and Bound are different types and Bound is not std::unreachable_sentinel_t.
However, as W (int) is not an iterator type, the iterator and sentinel types don't satisfy
std::sized_sentinel_for, and thus not subtractable.
operator- overloads for iota_view::iterator and iota_view::sentinel
like iota_view::size and operator- for iota_view::iterator.
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
[Tim Song commented:]
We might be able to simplify the (y_value > x.value_) conditional
since we know that we are working with an iterator and its sentinel.
Proposed resolution:
This wording is relative to N4892.
Modify 25.6.4.4 [range.iota.sentinel] as indicated:
namespace std::ranges { template<weakly_incrementable W, semiregular Bound> requires weakly-equality-comparable-with<W, Bound> && copyable<W> struct iota_view<W, Bound>::sentinel { private: Bound bound_ = Bound(); // exposition only public: sentinel() = default; constexpr explicit sentinel(Bound bound); friend constexpr bool operator==(const iterator& x, const sentinel& y); friend constexpriter_difference_t<W>IOTA-DIFF-T(W) operator-(const iterator& x, const sentinel& y) requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>; friend constexpriter_difference_t<W>IOTA-DIFF-T(W) operator-(const sentinel& x, const iterator& y) requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>; }; }[…]
friend constexpriter_difference_t<W>IOTA-DIFF-T(W) operator-(const iterator& x, const sentinel& y) requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;-3- Effects: Equivalent to:
return x.value_ - y.bound_;using D = IOTA-DIFF-T(W); if constexpr (is-integer-like<W>) { auto y_value = W(y.bound_); if constexpr (is-signed-integer-like<W>) { return D(D(x.value_) - D(y_value)); } else { return (y_value > x.value_) ? D(-D(y_value - x.value_)) : D(x.value_ - y_value); } } else { return x.value_ - y.bound_; }friend constexpriter_difference_t<W>IOTA-DIFF-T(W) operator-(const sentinel& x, const iterator& y) requires (is-integer-like<W> && is-integer-like<Bound>) || sized_sentinel_for<Bound, W>;-4- Effects: Equivalent to:
return -(y - x);
nullopt_t is copyableSection: 22.5.5 [optional.nullopt] Status: New Submitter: Frank Birbacher Opened: 2021-10-01 Last modified: 2024-01-29
Priority: 3
View all other issues in [optional.nullopt].
View all issues with New status.
Discussion:
The Standard defines a number of types that are used to create overload disambiguators for constructors,
like nullopt_t and allocator_tag_t. Values of such types are passed by value to such
constructors to give it particular meaning. For pass-by-value these types need to be copy-constructible
and for consistency should also be copy-assignable. Of those types the specification of nullopt_t
doesn't clearly state that the type is copyable, 22.5.5 [optional.nullopt].
nullopt_t is defined differently from other such types is to avoid ambiguity
in expressions that assign an empty brace initializer to an optional.
The meaning of such
assignment should be to engage the optional instead of taking the braces to create a temporary
nullopt_t for assignment and thus reset the optional.
The RHS of such assignment should be a temporary empty optional
instead of a temporary nullopt_t.
Types that aren't affected: nullptr_t (fundamental type), allocator_tag_t,
piecewise_construct_t, in_place_t, in_place_type_t, in_place_index_t
(all basically defined as a class T { explicit T() = default; } which works fine for pass-by-value)
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
[Daniel commented:]
I would prefer to see the wording use
is_trivially_copy_constructible_v
and trivially_copy_assignable_v,
which is consistent with similar usage of trivial-requirements in
std::optional.
[Tim commented:]
We need to say that it models copyable and is trivially copyable
(not sure if we need the latter but might as well - does anyone do it differently?).
"has a copy constructor" isn't enough - T(T&) is a copy constructor.
Proposed resolution:
This wording is relative to N4892.
Modify 22.5.5 [optional.nullopt] as indicated:
struct nullopt_t{see below}; inline constexpr nullopt_t nullopt(unspecified);-1- The struct
-2- Typenullopt_tis an empty class type used as a unique type to indicate the state of not containing a value foroptionalobjects. In particular,optional<T>has a constructor withnullopt_tas a single argument; this indicates that an optional object not containing a value shall be constructed.nullopt_tshall not have a default constructor or an initializer-list constructor,andshall not be an aggregate, and shall have a copy constructor and a copy assignment operator, both shall be public and trivial.
iota_view::size and the most negative signed integer valuesSection: 25.6.4.2 [range.iota.view] Status: New Submitter: Jiang An Opened: 2021-10-01 Last modified: 2021-10-14
Priority: 3
View all other issues in [range.iota.view].
View all issues with New status.
Discussion:
According to 25.6.4.2 [range.iota.view]/15, when both W and Bound are
integer-like, the expression in the return statement uses -value_ and -bound_.
These operations result in undefined behavior when - is applied to the most negative integer
value of a promoted type.
value_ and bound_ to the return type
(make-unsigned-like-t<common_type_t<W, Bound>>) and then perform the subtraction.
Such method should give the same results with UB eliminated.
Additionally, if we decide that iota_view<uint8_t, uint8_t>(uint8_t(1)).size() is well-defined
(LWG 3597(i)), it should give the correct result. We can truncate the result to fit the type W.
[2021-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4892.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A: Just fixes the most negative values
Modify 25.6.4.2 [range.iota.view] as indicated:
constexpr auto size() const requires see below;-15- Effects: Equivalent to:
if constexpr (is-integer-like<W> && is-integer-like<Bound>) {return (value_ < 0) ? ((bound_ < 0) ? to-unsigned-like(-value_) - to-unsigned-like(-bound_) : to-unsigned-like(bound_) + to-unsigned-like(-value_)) : to-unsigned-like(bound_) - to-unsigned-like(value_);using UC = make-unsigned-like-t<common_type_t<W, Bound>>; return UC(bound_) - UC(value_); } else return to-unsigned-like(bound_ - value_);-16- Remarks: […]
Option B: Also fixes pathological cases involving unsigned-integer-like types
Modify 25.6.4.2 [range.iota.view] as indicated:
constexpr auto size() const requires see below;-15- Effects: Equivalent to:
if constexpr (is-integer-like<W> && is-integer-like<Bound>) {return (value_ < 0) ? ((bound_ < 0) ? to-unsigned-like(-value_) - to-unsigned-like(-bound_) : to-unsigned-like(bound_) + to-unsigned-like(-value_)) : to-unsigned-like(bound_) - to-unsigned-like(value_);using UC = make-unsigned-like-t<common_type_t<W, Bound>>; if constexpr (is-signed-integer-like<W>) return UC(bound_) - UC(value_); else return UC(W(UC(bound_) - UC(value_))); } else return to-unsigned-like(bound_ - value_);-16- Remarks: […]
incrementable_traits has wrong operand typesSection: 24.3.2.1 [incrementable.traits] Status: New Submitter: Hewill Kang Opened: 2021-09-30 Last modified: 2022-01-31
Priority: 3
View other active issues in [incrementable.traits].
View all other issues in [incrementable.traits].
View all issues with New status.
Discussion:
The last specialization of incrementable_traits requires a - b to be well-formed,
where the types of both operands are const lvalue reference of type T. However inside
the struct, it uses decltype(declval<T>() - declval<T>()) to define the
difference_type, that is, non-const rvalue reference of type T.
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll that failed to reach consensus. Some suggested NAD: "Implicit expression variations ([concepts.equality]/6) apply here."
Proposed resolution:
This wording is relative to N4892.
Modify 24.3.2.1 [incrementable.traits] as indicated:
namespace std {
[…]
template<class T>
requires (!requires { typename T::difference_type; } &&
requires(const T& a, const T& b) { { a - b } -> integral; })
struct incrementable_traits<T> {
using difference_type = make_signed_t<decltype(declval<const T&>() - declval<const T&>())>;
};
[…]
}
Section: 16.3.3.3.4.1 [character.seq.general] Status: New Submitter: Dawn Perchik Opened: 2021-10-17 Last modified: 2022-01-29
Priority: 3
View all issues with New status.
Discussion:
The definitions for execution character set and execution wide-character set were reworded and moved to 16.3.3.3.4.1 [character.seq.general]/p1.2 after applying CWG motion 10 "P2314R4 Character sets and encodings", but I can't figure out what these terms mean from the wording, which now reads:
"The execution character set and the execution wide-character set are supersets of the basic literal character set (5.3 [lex.charset]). The encodings of the execution character sets and the sets of additional elements (if any) are locale-specific."
Would it be possible to provide complete definitions for these and give examples of each?
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
Should say something, even if just "unspecified".
P1885R9 will expose the possible encoding programmatically,
so could say it's implementationd-defined and implementations can document
that you should ask std::text_encoding.
Proposed resolution:
std::reverse_iterator with containers should not require manually including <iterator>Section: 24.5.1.1 [reverse.iterators.general] Status: New Submitter: Jiang An Opened: 2021-10-23 Last modified: 2022-01-29
Priority: 3
View all issues with New status.
Discussion:
Currently it is unspecified whether the definitions of std::reverse_iterator and its related
operators are available in <vector>, <array>, etc. So, it's unspecified
now whether the following program is well-formed because it's unspecified whether the equality operator
is available:
#include <vector>
int main()
{
auto v = std::vector<int>(42);
for (auto it = v.rbegin(); it != v.rend(); ++it);
for (auto it = std::rbegin(v); it != std::rend(v); ++it);
}
Such underspecification also leaves the guarantee that std::rbegin, std::rend,
std::crbegin, and std::crend are available in some other headers seems not so meaningful.
In order to guarantee these function templates can be used meaningfully with containers, users are still
required to include <iterator> manually.
rbegin (that returns std::reverse_iterator)
or std::rbegin is provided, the definitions of std::reverse_iterator and its related operators
are also provided. This strategy is already implemented by libc++, libstdc++, and MSVC STL, and thus I believe we
should standardize it to reduce uncertainty for users.
Note that the situation for std::reverse_iterator is different from LWG 1361(i), because every
operation on std::size_t is still valid when the typedef-name itself is absent, but == and
!= on std::reverse_iterator fail if the corresponding declarations are unavailable.
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4901.
Modify 24.5.1.1 [reverse.iterators.general] as indicated:
-1- Class template
-?- In addition to being available via inclusion of thereverse_iteratoris an iterator adaptor that iterates from the end of the sequence defined by its underlying iterator to the beginning of that sequence.<iterator>header, class templatereverse_iteratorand function templates in 24.5.1.8 [reverse.iter.cmp] and 24.5.1.9 [reverse.iter.nonmember] are available when any of the following headers are included:<array>(23.3.2 [array.syn]),<deque>(23.3.4 [deque.syn]),<forward_list>(23.3.6 [forward.list.syn]),<list>(23.3.10 [list.syn]),<map>(23.4.2 [associative.map.syn]),<regex>(28.6.3 [re.syn]),<set>(23.4.5 [associative.set.syn]),<span>(23.7.2.1 [span.syn]),<stacktrace>(19.6.2 [stacktrace.syn]),<string>(27.4.2 [string.syn]),<string_view>(27.3.2 [string.view.synop]),<unordered_map>(23.5.2 [unord.map.syn]),<unordered_set>(23.5.5 [unord.set.syn]), and<vector>(23.3.12 [vector.syn]).
<typeinfo>, <initializer_list>, and
<compare> in the standard librarySection: 17.7 [support.rtti], 17.11 [support.initlist], 17.12 [cmp] Status: New Submitter: Jiang An Opened: 2021-10-23 Last modified: 2025-10-22
Priority: 3
View all issues with New status.
Discussion:
Standard library headers <typeinfo>, <initializer_list>, and <compare>
are required for some core language features, as specified in 7.6.1.8 [expr.typeid]/7,
9.5.5 [dcl.init.list]/2, and 7.6.8 [expr.spaceship]/8. In C++11 (via N2930),
every header that has dependency on std::initializer_list is required to include
<initializer_list>. The similar requirements are added for operator<=> and
<compare> in C++20 (via LWG 3330(i)).
No operation is done for <typeinfo>, although <typeindex> (std::type_index),
<functional> (std::function, since C++11), and <any> (std::any)
have dependency on std::type_info;
<iterator> has dependency on std::initializer_list since C++14/LWG 2128(i)
(the std::rbegin overload and its friends), but it is not required to include <initializer_list>;
<stacktrace> is not required to include <compare> while it provides operator
<=> overloads.
The situation may be quite serious for std::type_index. Perhaps no expected operation on std::type_index
is guaranteed to work when only <typeindex> but not <typeinfo> is included.
<typeinfo> and <initializer_list> when
the required standard interface depends on them. I think we should standardize the existing practice (except
that <stackstrace> has not been implemented now) to reduce uncertainty for users.
[2021-10-24; Daniel comments]
This issue is related to and depending on LWG 3625(i).
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
[2025-10-22; Expected to be resolved by P3016R6.]
Proposed resolution:
This wording is relative to N4901.
[Drafting note: The proposed wording below contains one conditional change, it is therefore depending upon LWG 3625(i).]
Add #include <typeinfo> to 22.7.2 [any.synop], 22.10.2 [functional.syn], and
17.7.6 [type.index.synopsis].
Add #include <initializer_list> to 24.2 [iterator.synopsis].
Add #include <compare> to 19.6.2 [stacktrace.syn].
If we decide to add range access function templates (24.7 [iterator.range]) to <stacktrace>,
we should also add #include <initializer_list> to 19.6.2 [stacktrace.syn].
<stacktrace> provide range access function templates?Section: 19.6.2 [stacktrace.syn], 24.7 [iterator.range] Status: Open Submitter: Jiang An Opened: 2021-10-23 Last modified: 2025-10-10
Priority: 3
View all other issues in [stacktrace.syn].
View all issues with Open status.
Discussion:
Range access function templates (24.7 [iterator.range]) are available in every standard header
for containers. As std::basic_stacktrace provides some container-like interfaces (member functions
begin, end, size, etc.), should we add these free function templates to
<stacktrace> for consistency?
[2021-10-24; Daniel comments]
This issue is related to LWG 3624(i).
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
[2025-10-07; Status updated New → Open]
This will be resolved by P3016R6.
The <initializer_list> part is covered by 3624(i).
Proposed resolution:
This wording is relative to N4901.
[Drafting note: The proposed wording below contains one conditional change, it is therefore depending upon a decision]
Modify 24.7 [iterator.range] as indicated:
-1- In addition to being available via inclusion of the
<iterator>header, the function templates in 24.7 [iterator.range] are available when any of the following headers are included:<array>(23.3.2 [array.syn]),<deque>(23.3.4 [deque.syn]),<forward_list>(23.3.6 [forward.list.syn]),<list>(23.3.10 [list.syn]),<map>(23.4.2 [associative.map.syn]),<regex>(28.6.3 [re.syn]),<set>(23.4.5 [associative.set.syn]),<span>(23.7.2.1 [span.syn]),<stacktrace>(19.6.2 [stacktrace.syn]),<string>(27.4.2 [string.syn]),<string_view>(27.3.2 [string.view.synop]),<unordered_map>(23.5.2 [unord.map.syn]),<unordered_set>(23.5.5 [unord.set.syn]), and<vector>(23.3.12 [vector.syn]). […]
If we decide that <initializer_list> should be included if the header has dependency on
std::initializer_list (it may be introduce by std::rbegin, std::data, etc.),
#include <initializer_list> should also be added to 19.6.2 [stacktrace.syn].
std::basic_stacktrace required to use contiguous storage?Section: 19.6.4.1 [stacktrace.basic.overview] Status: New Submitter: Jiang An Opened: 2021-10-23 Last modified: 2022-01-29
Priority: 3
View all issues with New status.
Discussion:
Currently std::basic_stacktrace has an exposition-only std::vector member for
storing its elements. According to 16.3.3.6 [objects.within.classes]/3, it seems that it is
effectively required that elements of a std::basic_stacktrace are contiguously stored.
However, the implication seems not used otherwhere. The iterator type of a std::basic_stacktrace
is only required to be random access iterator.
std::basic_stacktrace uses contiguous storage, we should
explicitly strengthen some requirements, perhaps a the member function data should be added.
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
"The problem here is that a handful of member functions
(operator[], at, perhaps begin)
expose references to the vector elements directly,
which can be read to require contiguity.
We should rephrase the members at issue to not do that."
Proposed resolution:
This wording is relative to N4901.
[Drafting note: The proposed wording below contains also conditional changes, it is therefore depending upon a decision]
Modify 19.6.4.1 [stacktrace.basic.overview] as indicated:
-1- The class template
basic_stacktracesatisfies the requirements of an allocator-aware container (Table 80 [tab:container.alloc.req]), a sequence container (23.2.4 [sequence.reqmts]), a contiguous container, and a reversible container (23.2.2 [container.requirements.general]) except that […]
Modify 19.6.4.3 [stacktrace.basic.obs] as indicated:
using const_iterator = implementation-defined;-1- The type models
(random_access_iteratorcontiguous_iterator24.3.4.13 [iterator.concept.random.access]24.3.4.14 [iterator.concept.contiguous]) and meets the Cpp17RandomAccessIterator requirements (24.3.5.7 [random.access.iterators]).
Optional additional changes (the following parts are proposed only if data() is wanted)
Modify 19.6.4.1 [stacktrace.basic.overview], class template basic_stacktrace synopsis, as indicated:
[…] const_reference operator[](size_type) const; const_reference at(size_type) const; const stacktrace_entry* data() const noexcept; // 19.6.4.4 [stacktrace.basic.cmp], comparisons […]
Modify 19.6.4.3 [stacktrace.basic.obs] as indicated:
const_reference at(size_type frame_no) const;-13- […]
-14- […]const stacktrace_entry* data() const noexcept;-?- Returns:
frames_.data().
Section: 26.11 [specialized.algorithms] Status: New Submitter: Jiang An Opened: 2021-10-23 Last modified: 2022-01-29
Priority: 3
View other active issues in [specialized.algorithms].
View all other issues in [specialized.algorithms].
View all issues with New status.
Discussion:
Most uninitialized memory algorithms (26.11 [specialized.algorithms]) are specified by
plain "Effects: Equivalent to:". According to 16.3.2.4 [structure.specifications]/4,
such wording requires propagation of "Constraints" of selected constructors. The first two
overloads of std::reduce (26.10.4 [reduce]) are specified similarly.
std, depend on "Constraints" in
the specifications of many standard library types (but not any user-defined type), which is
implementable but brings serious inconsistency.
Perhaps we should add "Mandates:" to these algorithms (except for algorithms in std::ranges).
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
Tim:
"P2. Not for this particular case (I'm pretty sure there'll be agreement
that this shouldn't induce any constraint), but for the more general
issue of "Effects: Equivalent to" propagating Constraints:;
I'm not sure that's the right approach in general (unlike the other elements,
Constraints: requires special handling beyond "use this code" and is
pretty hard to work through if we have a lengthy code block) - and it
certainly doesn't really make a lot of sense to propagate Constraints:
but not actual core-language constraints."
Proposed resolution:
basic_regex construction and assignment from iterator rangeSection: 28.6.7.2 [re.regex.construct] Status: New Submitter: Jonathan Wakely Opened: 2021-10-31 Last modified: 2022-01-29
Priority: 4
View other active issues in [re.regex.construct].
View all other issues in [re.regex.construct].
View all issues with New status.
Discussion:
We have:
template<class ForwardIterator>
basic_regex(ForwardIterator first, ForwardIterator last,
flag_type f = regex_constants::ECMAScript);
and:
template<class InputIterator>
basic_regex& assign(InputIterator first, InputIterator last,
flag_type f = regex_constants::ECMAScript);
Ignoring the lack of proper requirements (which is LWG 3341(i)),
why does the constructor take forward iterators,
but the assign function takes input iterators?
Why could construction from input iterators not be implemented as simply
assign(first, last, f)?
The current constructor signature is the result of N2409
which was resolving LWG 682(i).
It looks like the assign function should have been changed
at the same time, to keep them consistent.
I see no reason why they can't both take input iterators.
The meta-programming needed to avoid an additional string copy for the
input iterator case is trivial with if constexpr
and C++20 iterator concepts.
[2022-01-29; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
volatile atomicsSection: 32.5 [atomics] Status: New Submitter: Wesley Maxey Opened: 2021-11-05 Last modified: 2024-01-29
Priority: 3
View all other issues in [atomics].
View all issues with New status.
Discussion:
The specification of atomic and atomic<T*> (in 32.5.8.1 [atomics.types.generic.general]
and 32.5.8.5 [atomics.types.pointer]) explicitly deletes the following functions:
atomic(const atomic&) = delete; atomic& operator=(const atomic&) = delete; atomic& operator=(const atomic&) volatile = delete;
The intent is to make atomic objects not copyable, so that initializing an atomic object from another atomic, or assigning an atomic object with a value from another atomic, must be an explicit operation.
We also explicitly supportvolatile objects of types that are specializations of std::atomic;
some of the functions that are vital for the support of volatile atomics are the following conversion operators:
operator T() const volatile noexcept; // for non-pointers operator T*() const volatile noexcept; // for pointers
The presence of this conversion operator means that all the statements in the following piece of code compile successfully today, despite the deleted functions mentioned earlier:
volatile std::atomic<int> a; volatile std::atomic<int> b(a); std::atomic<int> c(a); b = a; c = a;
However, if a is not a volatile object, none of the last four lines compile.
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
This PR would allow
atomic<int> x, y = std::move(x);because const volatile& doesn't bind to rvalues. It sounds like we'll need to delete both const volatile& and const volatile&&.
Proposed resolution:
This wording is relative to N4901.
Modify 32.5.8.1 [atomics.types.generic.general], class template atomic synopsis, as indicated:
[…] atomic(const volatile atomic&) = delete; atomic& operator=(const volatile atomic&) = delete; atomic& operator=(const volatile atomic&) volatile = delete; […]
Modify 32.5.8.3 [atomics.types.int], class template atomic<integral> specialization
synopsis, as indicated:
[…] atomic(const volatile atomic&) = delete; atomic& operator=(const volatile atomic&) = delete; atomic& operator=(const volatile atomic&) volatile = delete; […]
Modify 32.5.8.4 [atomics.types.float], class template atomic<floating-point> specialization
synopsis, as indicated:
[…] atomic(const volatile atomic&) = delete; atomic& operator=(const volatile atomic&) = delete; atomic& operator=(const volatile atomic&) volatile = delete; […]
Modify 32.5.8.5 [atomics.types.pointer], class template atomic<T*> partial specialization
synopsis, as indicated:
[…] atomic(const volatile atomic&) = delete; atomic& operator=(const volatile atomic&) = delete; atomic& operator=(const volatile atomic&) volatile = delete; […]
memory_resource objects destroyed?Section: 20.5.4 [mem.res.global], 19.5.3.5 [syserr.errcat.objects] Status: New Submitter: Jiang An Opened: 2021-11-07 Last modified: 2022-01-29
Priority: 3
View all other issues in [mem.res.global].
View all issues with New status.
Discussion:
Both std::pmr::new_delete_resource and std::pmr::null_memory_resource return pointers
to static-duration objects. It seems unspecified when the pointed-to objects are destroyed, so users can't
reliably use these objects during destructions of their static-duration objects.
std::generic_category and std::system_category have the same issue, except that the
referred-to objects may have different storage duration.
Should we specify in which case can these objects be used in the termination of a program?
[2022-01-29; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
pmr::memory_resource::do_allocate needs clarificationSection: 20.5.2 [mem.res.class] Status: New Submitter: Jonathan Wakely Opened: 2021-11-12 Last modified: 2022-01-30
Priority: 3
View all other issues in [mem.res.class].
View all issues with New status.
Discussion:
20.5.2.3 [mem.res.private] says that pmr::memory_resource::do_allocate
returns "a pointer to allocated storage" and references 6.8.6.5.2 [basic.stc.dynamic.allocation].
But it's not really clear which parts of 6.8.6.5.2 [basic.stc.dynamic.allocation] define
"allocated storage". pmr::memory_resource::allocate is not "an allocation function"
and not a "replaceable allocation function", so "the value returned by a replaceable allocation
function is a non-null pointer value" doesn't apply here, and neither does "different from any
previously returned value".
pmr::memory_resource::allocate allowed to return a null pointer on success? Is it
allowed to return the same address twice, without an intervening deallocation? What about if you call
pmr::monotonic_buffer_resource::release(), is that a deallocation?
When discussed on the reflector the consensus was that returning null should not be allowed,
it should throw an exception or return a valid dereferenceable pointer. The reference to
6.8.6.5.2 [basic.stc.dynamic.allocation] doesn't work to specify this though, so we should
restate the requirements without directly using the core wording for operator new.
It was also suggested that returning the same value should not be allowed without an intervening
deallocation, but that "deallocation" should not only mean a call to deallocate on the resource,
but include things like pmr::monotonic_buffer_resource::release(), and when a memory
resource's destructor returns memory to an upstream resource.
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
vector<bool>::swap(reference, reference) is uselessSection: 23.3.14 [vector.bool] Status: New Submitter: Jonathan Wakely Opened: 2021-11-12 Last modified: 2025-02-07
Priority: 3
View other active issues in [vector.bool].
View all other issues in [vector.bool].
View all issues with New status.
Discussion:
vector<bool> provides a static member function that can be used to swap
rvalues of type vector<bool>::reference like so:
vector<bool> v{true, false};
vector<bool>::swap(v[0], v[1]);
This is not useful. Nobody calls swap like that. This fails to make v[0] swappable with
v[1] as per 16.4.4.3 [swappable.requirements]. The similar SGI STL bit_vector class
that vector<bool> is partially inspired by has a "global function" with the same signature,
described as:
"Swaps the bits referred to by
xandy. This is a global function, not a member function. It is necessary because the ordinary version ofswaptakes arguments of typeT&, andbit_vector::referenceis a class, not a built-in C++ reference."
For some reason this became a static member function of vector<bool> in the C++ standard.
Previous resolution [SUPERSEDED]:
This wording is relative to N4901.
Create a new subclause [vector.bool.general] below 23.3.14 [vector.bool] and move paragraphs p1-p3 (including the class template
vector<bool, Allocator>partial specialization synopsis) into that subclause.Add to the synopsis in [vector.bool.general] p1 (née 23.3.14 [vector.bool] p1):
[…] // bit reference class reference { friend class vector; constexpr reference() noexcept; public: constexpr reference(const reference&) = default; constexpr ~reference(); constexpr operator bool() const noexcept; constexpr reference& operator=(bool x) noexcept; constexpr reference& operator=(const reference& x) noexcept; constexpr const reference& operator=(bool x) const noexcept; constexpr void flip() noexcept; // flips the bit friend constexpr void swap(reference x, reference y) noexcept; friend constexpr void swap(reference x, bool& y) noexcept; friend constexpr void swap(bool& x, reference y) noexcept; }; […]Remove the static
swapfunction from the class templatevector<bool, Allocator>partial specialization synopsis:[…] constexpr void swap(vector&);constexpr static void swap(reference x, reference y) noexcept;constexpr void flip() noexcept; // flips all bits […]Create a new subclause [vector.bool.ref] after p3, with p4 as its first paragraph, and add after it:
22.3.12.? Class
-1-vector<bool, Allocator>::reference[vector.bool.ref]referenceis a class that simulates the behavior of references of a single bit invector<bool>. The conversion function returnstruewhen the bit is set, andfalseotherwise. The assignment operators set the bit when the argument is (convertible to)trueand clear it otherwise.flipreverses the state of the bit.constexpr void flip() noexcept;-?- Effects:
*this = !*this;friend constexpr void swap(reference x, reference y) noexcept; friend constexpr void swap(reference x, bool& y) noexcept; friend constexpr void swap(bool& x, reference y) noexcept;-?- Effects: Exchanges the contents of
xandyas if by:bool b = x; x = y; y = b;Create a new subclause [vector.bool.mem] after that, containing the paragraphs describing
flip()and thehashspecialization:22.3.12.? Class
vector<bool, Allocator>members [vector.bool.mem]constexpr void flip() noexcept;-1- Effects: Replaces each element in the container with its complement.
constexpr static void swap(reference x, reference y) noexcept;
-6- Effects: Exchanges the contents ofxandyas if by:bool b = x; x = y; y = b;template<class Allocator> struct hash<vector<bool, Allocator>>;-7- The specialization is enabled (22.10.19 [unord.hash]).
Create a new subclause [depr.vector.bool.swap] after [depr.string.capacity]
D.? Deprecated
-?- The following member is declared in addition to those members specified in 23.3.14 [vector.bool]:vector<bool, Allocator>swap [depr.vector.bool.swap]namespace std { template<class Allocator> class vector<bool, Allocator> { public: constexpr static void swap(reference x, reference y) noexcept; }; }constexpr static void swap(reference x, reference y) noexcept;-?- Effects:
swap(x, y).
[2022-01-22; Jonathan replaces swap(x, y) in the Annex D
wording, following reflector discussion about lookup for swap
finding itself in that context.
]
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N4901.
Create a new subclause [vector.bool.general] below 23.3.14 [vector.bool] and move paragraphs p1-p3 (including the class template
vector<bool, Allocator>partial specialization synopsis) into that subclause.Add to the synopsis in [vector.bool.general] p1 (née 23.3.14 [vector.bool] p1):
[…] // bit reference class reference { friend class vector; constexpr reference() noexcept; public: constexpr reference(const reference&) = default; constexpr ~reference(); constexpr operator bool() const noexcept; constexpr reference& operator=(bool x) noexcept; constexpr reference& operator=(const reference& x) noexcept; constexpr const reference& operator=(bool x) const noexcept; constexpr void flip() noexcept; // flips the bit friend constexpr void swap(reference x, reference y) noexcept; friend constexpr void swap(reference x, bool& y) noexcept; friend constexpr void swap(bool& x, reference y) noexcept; }; […]Remove the static
swapfunction from the class templatevector<bool, Allocator>partial specialization synopsis:[…] constexpr void swap(vector&);constexpr static void swap(reference x, reference y) noexcept;constexpr void flip() noexcept; // flips all bits […]Create a new subclause [vector.bool.ref] after p3, with p4 as its first paragraph, and add after it:
22.3.12.? Class
-1-vector<bool, Allocator>::reference[vector.bool.ref]referenceis a class that simulates the behavior of references of a single bit invector<bool>. The conversion function returnstruewhen the bit is set, andfalseotherwise. The assignment operators set the bit when the argument is (convertible to)trueand clear it otherwise.flipreverses the state of the bit.constexpr void flip() noexcept;-?- Effects:
*this = !*this;friend constexpr void swap(reference x, reference y) noexcept; friend constexpr void swap(reference x, bool& y) noexcept; friend constexpr void swap(bool& x, reference y) noexcept;-?- Effects: Exchanges the contents of
xandyas if by:bool b = x; x = y; y = b;Create a new subclause [vector.bool.mem] after that, containing the paragraphs describing
flip()and thehashspecialization:22.3.12.? Class
vector<bool, Allocator>members [vector.bool.mem]constexpr void flip() noexcept;-1- Effects: Replaces each element in the container with its complement.
constexpr static void swap(reference x, reference y) noexcept;
-6- Effects: Exchanges the contents ofxandyas if by:bool b = x; x = y; y = b;template<class Allocator> struct hash<vector<bool, Allocator>>;-7- The specialization is enabled (22.10.19 [unord.hash]).
Create a new subclause [depr.vector.bool.swap] after [depr.string.capacity]
D.? Deprecated
-?- The following member is declared in addition to those members specified in 23.3.14 [vector.bool]:vector<bool, Allocator>swap [depr.vector.bool.swap]namespace std { template<class Allocator> class vector<bool, Allocator> { public: constexpr static void swap(reference x, reference y) noexcept; }; }constexpr static void swap(reference x, reference y) noexcept;-?- Effects: Exchanges the contents of
xandyas if by:bool b = x; x = y; y = b;
[2024-08-21; Jonathan provides improved wording]
Rebase on the current draft, change "exchanges the contents" to "exchanges the denoted values", and don't split the subclause into new subclauses.
Previous resolution [SUPERSEDED]:
This wording is relative to N4988.
Add to the synopsis in 23.3.14.1 [vector.bool.pspc] p1:
[…] // bit reference class reference { friend class vector; constexpr reference() noexcept; public: constexpr reference(const reference&) = default; constexpr ~reference(); constexpr operator bool() const noexcept; constexpr reference& operator=(bool x) noexcept; constexpr reference& operator=(const reference& x) noexcept; constexpr const reference& operator=(bool x) const noexcept; constexpr void flip() noexcept; // flips the bit friend constexpr void swap(reference x, reference y) noexcept; friend constexpr void swap(reference x, bool& y) noexcept; friend constexpr void swap(bool& x, reference y) noexcept; }; […]Remove the static
swapfunction from the same synopsis:[…] constexpr void swap(vector&) noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value);static constexpr void swap(reference x, reference y) noexcept;constexpr void flip() noexcept; // flips all bits constexpr void clear() noexcept; […]Modify the paragraphs below the synopsis as shown:
-4-
referenceis a class that simulates the behavior of references of a single bit invector<bool>. The conversion function returnstruewhen the bit is set, andfalseotherwise. The assignment operators set the bit when the argumentis (convertible to)converts totrueand clear it otherwise.flipreverses the state of the bit.constexpr void reference::flip() noexcept;-?- Effects:
*this = !*this;constexpr void swap(reference x, reference y) noexcept; constexpr void swap(reference x, bool& y) noexcept; constexpr void swap(bool& x, reference y) noexcept;-?- Effects: Exchanges the values denoted by
xandyas if by:bool b = x; x = y; y = b;constexpr void flip() noexcept;-1- Effects: Replaces each element in the container with its complement.
constexpr static void swap(reference x, reference y) noexcept;
-6- Effects: Exchanges the contents ofxandyas if by:bool b = x; x = y; y = b;template<class Allocator> struct hash<vector<bool, Allocator>>;-7- The specialization is enabled (22.10.19 [unord.hash]).
Create a new subclause [depr.vector.bool.swap] after D.20 [depr.format]
D.? Deprecated
-?- The following member is declared in addition to those members specified in 23.3.14 [vector.bool]:vector<bool, Allocator>swap [depr.vector.bool.swap]namespace std { template<class Allocator> class vector<bool, Allocator> { public: static constexpr void swap(reference x, reference y) noexcept; }; }static constexpr void swap(reference x, reference y) noexcept;-?- Effects: Exchanges the values denoted by
xandyas if by:bool b = x; x = y; y = b;
[2025-02-07; Jonathan provides improved wording]
Add swap for bitset::reference, as proposed in LWG 4187(i).
Proposed resolution:
This wording is relative to N5001.
Modify 22.9.2.1 [template.bitset.general] as indicated:
namespace std {
template<size_t N> class bitset {
public:
// bit reference
class reference {
public:
constexpr reference(const reference&) = default;
constexpr ~reference();
constexpr reference& operator=(bool x) noexcept; // for b[i] = x;
constexpr reference& operator=(const reference&) noexcept; // for b[i] = b[j];
constexpr bool operator~() const noexcept; // flips the bit
constexpr operator bool() const noexcept; // for x = b[i];
constexpr reference& flip() noexcept; // for b[i].flip();
friend constexpr void swap(reference x, reference y) noexcept;
friend constexpr void swap(reference x, bool& y) noexcept;
friend constexpr void swap(bool& x, reference y) noexcept;
};
[…]
};
[…]
}
Add to the synopsis in 23.3.14.1 [vector.bool.pspc] p1:
[…]
// bit reference
class reference {
friend class vector;
constexpr reference() noexcept;
public:
constexpr reference(const reference&) = default;
constexpr ~reference();
constexpr operator bool() const noexcept;
constexpr reference& operator=(bool x) noexcept;
constexpr reference& operator=(const reference& x) noexcept;
constexpr const reference& operator=(bool x) const noexcept;
constexpr void flip() noexcept; // flips the bit
friend constexpr void swap(reference x, reference y) noexcept;
friend constexpr void swap(reference x, bool& y) noexcept;
friend constexpr void swap(bool& x, reference y) noexcept;
};
[…]
Remove the static swap function from the same synopsis:
[…]
constexpr void swap(vector&)
noexcept(allocator_traits<Allocator>::propagate_on_container_swap::value ||
allocator_traits<Allocator>::is_always_equal::value);
static constexpr void swap(reference x, reference y) noexcept;
constexpr void flip() noexcept; // flips all bits
constexpr void clear() noexcept;
[…]
Modify the paragraphs below the synopsis as shown:
-4-
referenceis a class that simulates the behavior of references of a single bit invector<bool>. The conversion function returnstruewhen the bit is set, andfalseotherwise. The assignment operators set the bit when the argumentis (convertible to)converts totrueand clear it otherwise.flipreverses the state of the bit.constexpr void reference::flip() noexcept;-?- Effects:
*this = !*this;constexpr void swap(reference x, reference y) noexcept; constexpr void swap(reference x, bool& y) noexcept; constexpr void swap(bool& x, reference y) noexcept;-?- Effects: Exchanges the values denoted by
xandyas if by:bool b = x; x = y; y = b;constexpr void flip() noexcept;-1- Effects: Replaces each element in the container with its complement.
constexpr static void swap(reference x, reference y) noexcept;
-6- Effects: Exchanges the contents ofxandyas if by:bool b = x; x = y; y = b;template<class Allocator> struct hash<vector<bool, Allocator>>;-7- The specialization is enabled (22.10.19 [unord.hash]).
Create a new subclause [depr.vector.bool.swap] after D.20 [depr.format]
D.? Deprecated
-?- The following member is declared in addition to those members specified in 23.3.14 [vector.bool]:vector<bool, Allocator>swap [depr.vector.bool.swap]namespace std { template<class Allocator> class vector<bool, Allocator> { public: static constexpr void swap(reference x, reference y) noexcept; }; }static constexpr void swap(reference x, reference y) noexcept;-?- Effects: Exchanges the values denoted by
xandyas if by:bool b = x; x = y; y = b;
Section: 16.4.4 [utility.requirements] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2021-11-01 Last modified: 2022-10-01
Priority: 3
View other active issues in [utility.requirements].
View all other issues in [utility.requirements].
View all issues with New status.
Discussion:
This originated from the editorial issues #4863 and #4869.
Some Throws: elements are specified to throw the exceptions an evaluation ofE exits with.
This wording excludes exceptions thrown involving the initialization and destruction of parameters
of E, temporaries of E, and the destruction of the result of E.
The proposed wording below fixes this with front matter. As if affects more than just Throws: elements, it talks
about requirements and guarantees when E exits via an exception.
As noted in the originating editorial issues, some LWG members prefer fixing each individual case
of wording used to describe exception propagation rather than patching them up with front matter.
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll. "Any throwing destructor is library UB already, so there's no need to contort the wording to accommodate those."
Previous resolution [SUPERSEDED]:
This wording is relative to N4901.
Add a new subclause [exception.propagation] at the end of 16.4.4 [utility.requirements] (after 16.4.4.6.2 [allocator.requirements.completeness]):
16.4.4.? Exception propagation requirements [exception.propagation]
-?- Some functions defined in the C++ standard library impose requirements and guarantees R-G when a described evaluationEexits via an exception. LetFbe an evaluation that is implied by evaluatingEup to the complete evaluation of its enclosing full-expression. Unless stated otherwise, an execution ofFthat exits via an exception also has R-G imposed. [Note ?: This includes when initializing and destroying parameters, evaluating default arguments, and destroying temporaries (including discarded-value expressions) (7.6.1.3 [expr.call]) exit via an exception. — end note]
[2022-09-28; Johel provides revised wording]
Proposed resolution:
This wording is relative to N4917.
Add a new subclause [exception.propagation] at the end of 16.4.4 [utility.requirements] (after 16.4.4.6.2 [allocator.requirements.completeness]):
16.4.4.? Exception propagation requirements [exception.propagation]
-?- Some functions defined in the C++ standard library impose requirements and guarantees R-G when a described evaluationEof a constructor or construction exits via an exception. LetFbe the initialization denoted byE. Unless stated otherwise,Falso has R-G imposed. [Note ?: This includes the initialization of parameters and the evaluation of default arguments as part ofF. — end note]
operator== to format_to_n_resultSection: 28.5.1 [format.syn] Status: New Submitter: Mark de Wever Opened: 2021-11-14 Last modified: 2022-01-30
Priority: 3
View other active issues in [format.syn].
View all other issues in [format.syn].
View all issues with New status.
Discussion:
During the 2019 Cologne meeting the papers P1614R2 "The Mothership has
Landed" and P0645R10 "Text Formatting" have been accepted. P1614R2 adds
operator== to to_chars_result and from_chars_result. P0645R10
adds a similar type format_to_n_result without an operator==.
LWG 3373(i) reaffirms these three types are similar by ensuring they can be used in
structured bindings.
operator== wasn't applied to format_to_n_result. I propose to add
operator== to format_to_n_result to keep these types similar.
The Out template argument of format_to_n_result is unconstrained. Since
it's returned from format_to_n it's indirectly constrained to an
output_iterator. An output_iterator doesn't require equality_comparable,
thus the defaulted operator== can be defined deleted.
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll.
Several votes for NAD. Unclear why to_chars_result is equality
comparable, but whatever the reason, this is unrelated to them and doesn't
need to be.
Proposed resolution:
This wording is relative to N4901.
Modify 28.5.1 [format.syn], header <format> synopsis, as indicated:
[…]
template<class Out> struct format_to_n_result {
Out out;
iter_difference_t<Out> size;
friend bool operator==(const format_to_n_result&, const format_to_n_result&) = default;
};
[…]
move_only_function assignment operators seem to be defined suboptimalSection: 22.10.17.4.3 [func.wrap.move.ctor] Status: New Submitter: Alexander Guteniev Opened: 2021-11-20 Last modified: 2022-01-30
Priority: 3
View other active issues in [func.wrap.move.ctor].
View all other issues in [func.wrap.move.ctor].
View all issues with New status.
Discussion:
22.10.17.4.3 [func.wrap.move.ctor]/22 and 22.10.17.4.3 [func.wrap.move.ctor]/25 define the effects of assignment as following:
move_only_function& operator=(move_only_function&& f);[…]Effects: Equivalent to:
move_only_function(std::move(f)).swap(*this);template<class F> move_only_function& operator=(F&& f);Effects: Equivalent to:
move_only_function(std::forward<F>(f)).swap(*this);
The assignment via swap with temporary makes the implementation to do the following:
move out the previous target to a temporary location
move in the new target
finally destroy the previous target.
As everything is noexcept here, I think it can be short cut to just:
destroy the previous target.
move in the new target
Looks like the implementation cannot do such optimization in a generic case with small functor optimization enabled and non-trivial move constructor for the new target and with non-trivial destruction of the previous target, since the difference is observable.
Apparently the optimization is precluded for no reason.[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll. Some suggestions for NAD, but others disagreed.
Proposed resolution:
This wording is relative to N4901.
Modify 22.10.17.4.3 [func.wrap.move.ctor] as indicated:
move_only_function& operator=(move_only_function&& f);[…]-22- Effects: Sets the target object of
thisto the target object offbefore the assignment and leavesfin a valid state with an unspecified value.Equivalent to:move_only_function(std::move(f)).swap(*this);template<class F> move_only_function& operator=(F&& f);-25- Effects: Equivalent to:
*this = move_only_function(std::forward<F>(f)).swap(*this);
std::format does not define "integer presentation type"Section: 28.5.2.2 [format.string.std] Status: New Submitter: Charlie Barto Opened: 2021-11-23 Last modified: 2022-11-01
Priority: 2
View other active issues in [format.string.std].
View all other issues in [format.string.std].
View all issues with New status.
Discussion:
28.5.2.2 [format.string.std] specifies the behavior of several format specifiers in terms of "integer presentation types"; for example 28.5.2.2 [format.string.std]/4 states:
"The sign option is only valid for arithmetic types other than
charTandboolor when an integer presentation type is specified".
Unfortunately nowhere does the standard actually define the term "integer presentation type". The
closest it comes is in 28.5.2.2 [format.string.std]/19 and [tab:format.type.int], but that
explicitly excludes charT and bool. [tab:format.type.char] and [tab:format.type.bool]
then refer to [tab:format.type.int].
'c' is used with charT
or bool, but the following table is what msvc does right now (throws is the same as does not
compile after P2216 in all these cases, although not in general for 'c'):
Argument type Specifiers Throws? bool#Yes bool#cNo bool:+Yes bool+cYes bool^No bool^cNo bool0Yes bool0cYes boolcNo charT#Yes charT#cYes charT+Yes charT+cYes charT^No charT^cNo charT0Yes charT0cYes
As you can see we don't interpret 'c' as an "integer type specifier", except when explicitly
specified for bool with #. I think this is because for # the standard states
"This option is valid for arithmetic types other than
charTandboolor when an integer presentation type is specified, and not otherwise",
and [tab:format.type.bool] puts 'c' in the same category as all the other "integer type specifiers",
whereas [tab:format.type.char] separates it out into the char-specific types. If this issue's proposed resolution
is adopted our behavior would become non-conforming (arguably it already is) and "#c" with bools
would become invalid.
[2021-11-29; Tim comments]
This issue touches the same wording area as LWG 3586(i) does.
[2022-01-30; Reflector poll]
Set priority to 2 after reflector poll.
[2021-11-29; Jonathan comments]
LWG 3648(i) removed 'c' as a valid presentation type for bool.
The last change in the resolution below (and the drafting note) can be dropped.
LWG 3586(i) could be resolved as part of this issue by using "this is the default unless formatting a floating-point type or using an integer presentation type" for '<' and by using "this is the default when formatting a floating-point type or using an integer presentation type" for '>'.
Proposed resolution:
This wording is relative to N4901.
Modify 28.5.2.2 [format.string.std] as indicated:
-6- The
[…]#option causes the alternate form to be used for the conversion. This option is only valid for arithmetic types other thancharTandboolor when an integer presentation type is specified, and not otherwise. For integral types, the alternate form inserts the base prefix (if any) specified in Table 65 into the output after the sign character (possibly space) if there is one, or before the output ofto_charsotherwise. For floating-point types, the alternate form causes the result of the conversion of finite values to always contain a decimal-point character, even if no digits follow it. Normally, a decimal-point character appears in the result of these conversions only if a digit follows it. In addition, forgandGconversions, trailing zeros are not removed from the result.[…][Drafting note: This modification is a simple cleanup given the other changes further below, to bring the wording for
#in line with the wording for the other modifiers, in the interest of preventing confusion.]-16- The type determines how the data should be presented.
-?- An integer presentation type is one of the following type specifiers in Table [tab:format.type.integer_presentation], or none, if none is defined to have the same behavior as one of the type specifiers in Table [tab:format.type.integer_presentation].
Table ? — Meaning of type options for integer representations [tab:format.type.integer_presentation] Type Meaning bto_chars(first, last, value, 2);the base prefix is0b.BThe same as b, except that the base prefix is0B.dto_chars(first, last, value).oto_chars(first, last, value, 8); the base prefix is0ifvalueis nonzero and is empty otherwise.xto_chars(first, last, value, 16); the base prefix is0x.XThe same as x, except that it uses uppercase letters for digits above9and the base prefix is0X.[Drafting note: This is the same as [tab:format.type.int] with "none" and
'c'removed]-17- The available string presentation types are specified in Table 64 ([tab:format.type.string]).
[…]
Table 65 — Meaning of type options for integer types [tab:format.type.int] Type Meaning b,B,d,o,x,XAs specified in Table [tab:format.type.integer_presentation] .to_chars(first, last, value, 2);the base prefix is0bBThe same asb, except that the base prefix is0B.cCopies the character static_cast<charT>(value)to the output. Throwsformat_errorifvalueis not in the range of representable values forcharT.dto_chars(first, last, value).oto_chars(first, last, value, 8); the base prefix is0ifvalueis nonzero and is empty otherwise.xto_chars(first, last, value, 16); the base prefix is0x.XThe same asx, except that it uses uppercase letters for digits above9and the base prefix is0X.none The same as d.[Note 8: If the formatting argument type ischarTorbool, the default is insteadcors, respectively. — end note]
Table 66 — Meaning of type options for charT[tab:format.type.char]Type Meaning none, cCopies the character to the output. b,B,d,o,x,XAs specified in Table [tab:format.type.int][tab:format.type.integer_presentation].
Table 67 — Meaning of type options for bool[tab:format.type.bool]Type Meaning none, sCopies textual representation, either trueorfalse, to the output.b,B,c,d,o,x,XAs specified in Table [tab:format.type.int][tab:format.type.integer_presentation] for the valuestatic_cast<unsigned char>(value).cCopies the character static_cast<unsigned char>(value)to the output.[Drafting note: allowing the
The inconsistency with respect to using'c'specifier forboolis pretty bizarre behavior, but that's very clearly what the standard says now, so I'm preserving it. I would suggest keeping discussion of changing that behavior to a separate issue or defect report (the reworking of the tables in this issue makes addressing that easier anyway).static_cast<unsigned char>here andstatic_cast<charT>in [tab:format.type.int] is pre-existing and should be addressed in a separate issue if needed. ]
nothrow-input-iterator constraints should not mention copyingSection: 26.11.2 [special.mem.concepts] Status: New Submitter: Konstantin Varlamov Opened: 2021-11-30 Last modified: 2022-01-30
Priority: 3
View all issues with New status.
Discussion:
26.11.2 [special.mem.concepts] states that a type qualifies as a nothrow-input-iterator
only if no exceptions are thrown from, among other things, copy construction and copy
assignment. However, being copyable isn't part of the requirements for an input_iterator on which
nothrow-input-iterator is based (indeed, one of the things forward_iterator
adds to input_iterator is copyability), and the nothrow-input-iterator concept
doesn't add any new constraints related to copyability.
[2021-12-19; Daniel comments]
During LWG discussion of the issue one argument brought forward against the proposed wording was that it
might be incomplete, because it doesn't adjust the nothrow-forward-iterator concept,
which adds among other things the copyable requirements. But
nothrow-forward-iterator also requires nothrow-sentinel-for<I, I>,
which already extends this necessary no-throw requirement for copy operations by p. 4:
Types
SandImodelnothrow-sentinel-foronly if no exceptions are thrown from copy construction, move construction, copy assignment, move assignment, or comparisons between valid values of typeIandS.
It should also be emphasized that the definitions of move construction (3.34 [defns.move.constr]) and move assignment (3.33 [defns.move.assign]) are compatible even for copyable input iterator types, because these definitions refer just to expression conditions, and not to concrete operator overloads. So as long as an implementation applies these expression conditions, we are safe.
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4901.
Modify 26.11.2 [special.mem.concepts] as indicated:
template<class I> concept nothrow-input-iterator = // exposition only input_iterator<I> && is_lvalue_reference_v<iter_reference_t<I>> && same_as<remove_cvref_t<iter_reference_t<I>>, iter_value_t<I>>;-2- A type
Imodelsnothrow-input-iteratoronly if no exceptions are thrown from increment,copy construction,move construction,copy assignment,move assignment, or indirection through valid iterators.
Section: 28.5 [format] Status: New Submitter: Barry Revzin Opened: 2021-12-08 Last modified: 2022-01-30
Priority: 3
View other active issues in [format].
View all other issues in [format].
View all issues with New status.
Discussion:
Is this program guaranteed to be valid:
#include <format>
#include <algorithm>
struct Thing { };
template <>
struct std::formatter<Thing> {
std::string_view spec;
constexpr auto parse(std::format_parse_context& ctx) {
auto end = std::find(ctx.begin(), ctx.end(), '}');
spec = std::string_view(ctx.begin(), end);
return end;
}
auto format(Thing, std::format_context& ctx) {
return std::ranges::copy(spec, ctx.out()).out;
}
};
int main() {
std::print("{:lwg issue}\n", Thing{});
}
In parse(), the formatter for Thing holds onto a string view of its specifiers.
And then in format(), it just prints them. I don't think we say anywhere that this works.
Does this code print "lwg issue" because there's no issue or does it print some garbage
memory somewhere because there is one?
string_view's into the format string (for named
argument support), which implies that it should work. But it'd be nice to come out and say that.
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll.
"Presumably we need to say in [formatter.requirements] that
[pc.begin(), pc.end())
is guaranteed to be a valid range until the
next call to parse() or f is destroyed,
whichever comes first."
Proposed resolution:
<coroutine> is freestanding, but uses std::hash which is notSection: 17.13.2 [coroutine.syn] Status: New Submitter: Jonathan Wakely Opened: 2021-12-17 Last modified: 2022-01-30
Priority: 3
View all other issues in [coroutine.syn].
View all issues with New status.
Discussion:
The <coroutine> header is required for freestanding implementations, but it defines a
specialization of std::hash, which is not required for freestanding implementations.
std::hash specialization conditionally present.
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll. Would be resolved by P1642 for C++23, but it's still a defect in C++20.
Proposed resolution:
basic_streambuf::sputn is both overspecified and underspecifiedSection: 31.6.3.3.5 [streambuf.pub.put] Status: New Submitter: Jonathan Wakely Opened: 2022-01-17 Last modified: 2022-01-30
Priority: 3
View all issues with New status.
Discussion:
The specification for basic_streambuf::sputn is:
Returns:
xsputn(s, n).
One interpretation of this implies that sputn can't insert characters directly into the
put area if there is space for them, it has to make a virtual call. That has significant overhead
for repeated calls, such as inserting many small strings/string_views in a loop.
xsputn happens. Strictly speaking, it only says it
returns the value that xsputn would return, and doesn't even have to produce any of its effects!
We should describe the effects, not the return value, and we should do so in a way that does not
overconstrain the implementation. It should not be necessary to make a virtual call to xsputn
if the put area has capacity for the characters. On the other hand, if it doesn't have capacity, then
calling xsputn is the best option; it allows the derived streambuf to decide how
best to handle large writes.
The proposed resolution replaces the Returns: element with an Effects: element, so that
we specify that those effects actually occur. A normative remark is added to give the implementation
leeway to avoid the virtual call when it isn't needed.
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll.
Jonathan to revise P/R to also cover sgetn.
Proposed resolution:
This wording is relative to N4901.
Modify 31.6.3.3.5 [streambuf.pub.put] as indicated:
streamsize sputn(const char_type* s, streamsize n);-?- Preconditions:
-?- Effects:[s, s+n)is a valid range.return xsputn(s, n). -?- Remarks: When(epptr() - pptr()) >= n, it is unspecified whether the characters are written directly to the output sequence without callingxsputn.-2- Returns:xsputn(s, n).
basic_string::append/assign(NTBS, pos, n) suboptimalSection: 27.4.3.7.2 [string.append], 27.4.3.7.3 [string.assign] Status: New Submitter: Jonathan Wakely Opened: 2022-01-21 Last modified: 2022-01-30
Priority: 3
View all other issues in [string.append].
View all issues with New status.
Discussion:
This will allocate temporary string, then copy one byte from it:
std::string s;
s.append("a very very long string that doesn't fit in SSO buffer", 13, 1);
The problem is that there is no overload accepting an NTBS there, so it converts to a temporary string using:
append(const basic_string&, size_t, size_t = npos);
C++17 added a new overload for a string_view:
append(const T&, size_t, size_t = npos);
But that overload is constrained to not allow const char* arguments, so we still create a temporary string.
append(const T&, ...) overload into:
append(const T&, size_t); append(const T&, size_t, size_t);
and then remove the !is_convertible_v<const T&, const charT*> constraint from the second
overload (Option A in the proposed resolution).
append(const T&, size_t, size_t) overload alone and just add one
taking exactly the arguments used above. That seems simpler to me, and I prefer that (Option B in the Proposed Resolution).
The same problem applies to assign("...", pos, n).
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4901.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A:
Modify 27.4.3.1 [basic.string.general], class template basic_string synopsis, as indicated:
[…] // 27.4.3.7 [string.modifiers], modifiers […] template<class T> constexpr basic_string& append(const T& t); template<class T> constexpr basic_string& append(const T& t, size_type pos); template<class T> constexpr basic_string& append(const T& t, size_type pos, size_type n= npos); constexpr basic_string& append(const charT* s, size_type n); […] template<class T> constexpr basic_string& assign(const T& t); template<class T> constexpr basic_string& assign(const T& t, size_type pos); template<class T> constexpr basic_string& assign(const T& t, size_type pos, size_type n= npos); constexpr basic_string& assign(const charT* s, size_type n); […]
Modify 27.4.3.7.2 [string.append] as indicated:
template<class T> constexpr basic_string& append(const T& t);-3- Constraints:
(3.1) —
is_convertible_v<const T&, basic_string_view<charT, traits>>istrueand(3.2) —
is_convertible_v<const T&, const charT*>isfalse.-4- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return append(sv.data(), sv.size());template<class T> constexpr basic_string& append(const T& t, size_type pos);-?- Constraints:
(?.1) —
is_convertible_v<const T&, basic_string_view<charT, traits>>istrueand(?.2) —
is_convertible_v<const T&, const charT*>isfalse.-?- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return append(sv.substr(pos));template<class T> constexpr basic_string& append(const T& t, size_type pos, size_type n= npos);-5- Constraints:
(5.1) —is_convertible_v<const T&, basic_string_view<charT, traits>>istrueand
(5.2) —.is_convertible_v<const T&, const charT*>isfalse-6- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return append(sv.substr(pos, n));
Modify 27.4.3.7.3 [string.assign] as indicated:
template<class T> constexpr basic_string& assign(const T& t);-4- Constraints:
(4.1) —
is_convertible_v<const T&, basic_string_view<charT, traits>>istrueand(4.2) —
is_convertible_v<const T&, const charT*>isfalse.-5- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return assign(sv.data(), sv.size());template<class T> constexpr basic_string& assign(const T& t, size_type pos);-?- Constraints:
(?.1) —
is_convertible_v<const T&, basic_string_view<charT, traits>>istrueand(?.2) —
is_convertible_v<const T&, const charT*>isfalse.-?- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return assign(sv.substr(pos));template<class T> constexpr basic_string& assign(const T& t, size_type pos, size_type n= npos);-6- Constraints:
(6.1) —is_convertible_v<const T&, basic_string_view<charT, traits>>istrueand
(6.2) —.is_convertible_v<const T&, const charT*>isfalse-7- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return assign(sv.substr(pos, n));
Option B:
Modify 27.4.3.1 [basic.string.general], class template basic_string synopsis, as indicated:
[…] // 27.4.3.7 [string.modifiers], modifiers […] constexpr basic_string& append(const charT* s, size_type n); constexpr basic_string& append(const charT* s); constexpr basic_string& append(const charT* s, size_type pos, size_type n); constexpr basic_string& append(size_type n, charT c); […] constexpr basic_string& assign(const charT* s, size_type n); constexpr basic_string& assign(const charT* s); constexpr basic_string& assign(const charT* s, size_type pos, size_type n); constexpr basic_string& assign(size_type n, charT c); […]
Modify 27.4.3.7.2 [string.append] as indicated:
constexpr basic_string& append(const charT* s, size_type n);-7- Preconditions:
-8- Effects: Appends a copy of the range[s, s + n)is a valid range.[s, s + n)to the string. -9- Returns:*this.constexpr basic_string& append(const charT* s);-10- Effects: Equivalent to:
return append(s, traits::length(s));constexpr basic_string& append(const charT* s, size_type pos, size_type n);-?- Effects: Equivalent to:
return append(basic_string_view<charT, traits>(s).substr(pos, n));
Modify 27.4.3.7.3 [string.assign] as indicated:
constexpr basic_string& assign(const charT* s, size_type n);-8- Preconditions:
-9- Effects: Replaces the string controlled by[s, s + n)is a valid range.*thiswith a copy of the range[s, s + n). -10- Returns:*this.constexpr basic_string& assign(const charT* s);-11- Effects: Equivalent to:
return assign(s, traits::length(s));constexpr basic_string& assign(const charT* s, size_type pos, size_type n);-?- Effects: Equivalent to:
return assign(basic_string_view<charT, traits>(s).substr(pos, n));
basic_string(const T&, const Alloc&) turns moves into copiesSection: 27.4.3.3 [string.cons], 27.4.3.7.3 [string.assign] Status: New Submitter: Jonathan Wakely Opened: 2022-01-21 Last modified: 2022-01-30
Priority: 3
View all other issues in [string.cons].
View all issues with New status.
Discussion:
This will do a copy not a move:
struct Str : std::string {
Str() = default;
Str(Str&& s) : std::string(std::move(s)) { }
};
Str s;
Str s2(std::move(s));
The problem is that the new C++17 constructor:
basic_string(const T&, const Alloc& = Alloc());
is an exact match, but the basic_string move constructor requires a derived-to-base conversion.
assign(const T&) and operator=(const T&).
We can fix this by constraining those functions with !derived_from<T, basic_string>, so
that the move constructor is the only viable function. Libstdc++ has done something very similar since
2017, but using !is_convertible<const T*, const basic_string*> instead of derived_from.
[2022-01-30; Reflector poll]
Set priority to 3 after reflector poll.
Jonathan to revise P/R to use is_base_of or
is_convertible instead of derived_from.
Proposed resolution:
This wording is relative to N4901.
Modify 27.4.3.3 [string.cons] as indicated:
template<class T> constexpr explicit basic_string(const T& t, const Allocator& a = Allocator());-7- Constraints:
(7.1) —
is_convertible_v<const T&, basic_string_view<charT, traits>>istrueand(7.?) —
derived_from<T, basic_string>isfalseand(7.2) —
is_convertible_v<const T&, const charT*>isfalse.-8- Effects: Creates a variable,
sv, as if bybasic_string_view<charT, traits> sv = t;and then behaves the same asbasic_string(sv.data(), sv.size(), a).[…]
template<class T> constexpr basic_string& operator=(const T& t);-27- Constraints:
(27.1) —
is_convertible_v<const T&, basic_string_view<charT, traits>>istrueand(27.?) —
derived_from<T, basic_string>isfalseand(27.2) —
is_convertible_v<const T&, const charT*>isfalse.-28- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return assign(sv);
Modify 27.4.3.7.3 [string.assign] as indicated:
template<class T> constexpr basic_string& assign(const T& t);-4- Constraints:
(4.1) —
is_convertible_v<const T&, basic_string_view<charT, traits>>istrueand(4.?) —
derived_from<T, basic_string>isfalseand(4.2) —
is_convertible_v<const T&, const charT*>isfalse.-5- Effects: Equivalent to:
basic_string_view<charT, traits> sv = t; return assign(sv.data(), sv.size());
std::allocator_traits<Alloc>::rebind_alloc SFINAE-friendly?Section: 20.2.9.2 [allocator.traits.types] Status: New Submitter: Jiang An Opened: 2022-01-24 Last modified: 2022-03-04
Priority: 3
View all other issues in [allocator.traits.types].
View all issues with New status.
Discussion:
20.2.9.2 [allocator.traits.types]/11 says that the instantiation of rebind_alloc is
sometimes ill-formed, however, it's unclear such ill-formedness results in substitution failure or hard error.
It seems that current implementations (libc++, libstd++, MSVC STL) give substitution errors, and
we should reword 20.2.9.2 [allocator.traits.types]/11 with "Constraints:".
[2022-01-29; Daniel comments]
This issue has some overlap with LWG 3545(i) in regard to the question how we should handle
the rebind member template of the pointer_traits primary template as specified by
20.2.3.2 [pointer.traits.types] p3. It would seem preferable to decide for the same approach in both
cases.
[2022-03-04; Reflector poll]
Set priority to 3 after reflector poll. Probably NAD, since [allocator.requirements.general]/11 allows any allocator instantiation to fail with a hard error outside the immediate context, making it impossible to guarantee a SFINAE-friendly result. Also unclear what motivation there is for it being SFINAE friendly.
Proposed resolution:
join_view's difference type is too smallSection: 25.7.14 [range.join] Status: New Submitter: Tomasz Kamiński Opened: 2022-01-30 Last modified: 2022-03-04
Priority: 2
View all other issues in [range.join].
View all issues with New status.
Discussion:
Despite the fact that join_view may produce more elements than each joined view(s)
contains, we require implementations to use the common_type of their difference types
(per 25.7.14.3 [range.join.iterator] p3), which may lead to UB. As we already have provided
implementation freedom to define extended integer-like types we should make provision for them
to be used here as well. The same issue applies to the join_with_view as proposed by
P2441.
[2022-03-04; Reflector poll]
Set priority to 2 after reflector poll. Might be a design issue.
Proposed resolution:
[recursive_]directory_iterator constructors refer to undefined optionsSection: 31.12.11.2 [fs.dir.itr.members], 31.12.12.2 [fs.rec.dir.itr.members] Status: New Submitter: Jonathan Wakely Opened: 2022-01-31 Last modified: 2024-01-29
Priority: 3
View all other issues in [fs.dir.itr.members].
View all issues with New status.
Discussion:
31.12.11.2 [fs.dir.itr.members] p2 and 31.12.12.2 [fs.rec.dir.itr.members] p3 refer to the options
parameter, but not all overloads have a parameter of that name.
[2022-03-04; Reflector poll]
Set priority to 3 after reflector poll.
[2022-12-18; Daniel comments and provides wording]
It seems to me that we should differentiate the problems of directory_iterator and
recursive_directory_iterator regarding the constructors that don't provide any options arguments
at least from a logical point of view:
recursive_directory_iterator we can at least
guess that the intention of the existing wording is that these suppose to reflect that the effective options within
the Effects: element are also equal to directory_options::none.
But for directory_iterator we don't have any options "getter" nor Postconditions from deducing
the intent.
Fortunately, existing implementations (I looked at the current V2 2022 library and libstdc++ implementation)
seem to behave already as if directory_options::none is set for the affected constructors for both
iterator types.
Note that this issue here has some wording overlap with the the proposed wording of LWG 2708(i),
I'm therefore using the exact wording form currently suggested there. If LWG would like to change this form (e.g.
by using the word "overload" instead of "signature") this should also be done in that other issue to reduce possible
merge conflicts. Note that the here suggested form using "signature" has existing practice in other places
of Clause 31.12 [filesystems], so I recommend keeping that form.
Proposed resolution:
This wording is relative to N4917.
Modify 31.12.11.2 [fs.dir.itr.members] as indicated:
[Drafting note:
(1) The proposed wording form takes care to use the same wording form used in LWG 2708(i) forrecursive_directory_iteratorat the time of writing.
(2) We don't need similar wording for the default constructor because this creates an end iterator and there exist no way for the user to observe the effects ofdirectory_optionsfor such an iterator value. ]
directory_iterator() noexcept;-1- Effects: Constructs the end iterator.
explicit directory_iterator(const path& p); directory_iterator(const path& p, directory_options options); directory_iterator(const path& p, error_code& ec); directory_iterator(const path& p, directory_options options, error_code& ec);-?- For the signatures with no parameter
-2- Effects: For the directory thatoptions, letoptionsbedirectory_options::none.presolves to, constructs an iterator for the first element in a sequence ofdirectory_entryelements representing the files in the directory, if any; otherwise the end iterator. However, if(options & directory_options::skip_permission_denied) != directory_options::noneand construction encounters an error indicating that permission to access
[…]pis denied, constructs the end iterator and does not report an error.
Modify 31.12.12.2 [fs.rec.dir.itr.members] as indicated:
[Drafting note:
(1) The proposed wording form takes care to be in sync with the wording form used in LWG 2708(i) at the time of writing. This issue here doesn't touch the wording for theoptionsfunction though, and leaves any changes to LWG 2708(i), because these changes don't seem essential to solve the issue here.
(2) We don't need similar wording for the default constructor because this creates an end iterator and from 31.12.12.1 [fs.class.rec.dir.itr.general] p2 we can conclude that user code has no way to determine the effectivedirectory_optionsvalue of this iterator value (interestingly p15 quoted below can be read to apply here as well, but 31.12.12.1 [fs.class.rec.dir.itr.general] p2 seems to be very prohibitive and LWG 2708(i) is going to solve this special case as well).
(3) We easily simplify the wording of p3 as shown below, because the "scope" of the introductory new paragraph includes the Postconditions: element. But similar to the arguments provided in (1) we don't wish to introduce merge conflicts with the LWG 2708(i) wording and therefore intentionally leave any changes of p3 to that issue. ]
recursive_directory_iterator() noexcept;-1- Effects: Constructs the end iterator.
explicit recursive_directory_iterator(const path& p); recursive_directory_iterator(const path& p, directory_options options); recursive_directory_iterator(const path& p, directory_options options, error_code& ec); recursive_directory_iterator(const path& p, error_code& ec);[…]-?- For the signatures with no parameter
-2- Effects: Constructs an iterator representing the first entry in the directory to whichoptions, letoptionsbedirectory_options::none.presolves, if any; otherwise, the end iterator. However, if(options & directory_options::skip_permission_denied) != directory_options::noneand construction encounters an error indicating that permission to access
-3- Postconditions:pis denied, constructs the end iterator and does not report an error.options() == optionsfor the signatures with adirectory_optionsargument, otherwiseoptions() == directory_options::none. […]directory_options options() const;-15- Returns: The value of the argument passed to the constructor for the
-16- Throws: Nothing.optionsparameter, if present, otherwisedirectory_options::none.
std::filesystem operations should be observable behaviourSection: 4.1.2 [intro.abstract] Status: New Submitter: Jens Maurer Opened: 2022-02-03 Last modified: 2022-03-04
Priority: 3
View all issues with New status.
Discussion:
4.1.2 [intro.abstract] p6.2 should be amended to say that
filesystem operations such as mkdir are observable behaviour.
Any resolution would need CWG review.
[2022-03-04; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 28.3.3.1.4 [locale.members] Status: New Submitter: Hubert Tong Opened: 2022-02-12 Last modified: 2022-03-04
Priority: 2
View all other issues in [locale.members].
View all issues with New status.
Discussion:
LWG 2394(i) removed the only text in the wording that requires that the name of a locale is usable for constructing further locales.
The relevant notes from the wiki appear to make it seem like LWG thought the change was editorial. Perhaps the resolution was motivated by a different defect than the one that led to the change? Namely,explicit locale(const char* std_name);
is said to use "standard C locale names".
There is noLC_MESSAGES in standard C.
Thus, it is a question whether the aforementioned constructor should be able to consume names produced by the
C++ implementation.
See also the use of the name of the C++ locale in locale::global() for use with setlocale.
The following would restore the equivalence of locales that have the same name. It also addresses the
suitability of the name for use with setlocale and the locale(const char*) constructor
as a matter of QoI.
[2022-03-04; Reflector poll]
Set priority to 2 after reflector poll.
Proposed resolution:
This wording is relative to N4901.
Modify 28.3.3.1.4 [locale.members] as indicated:
string name() const;-5- Returns: The name of
-?- Remarks: Two locales have identical names only if their facets have identical virtual function semantics. -?- Recommended practice: The name of a locale that has a name should be such that*this, if it has one; otherwise, the string"*".setlocale(LC_ALL, name().c_str())returns a non-null pointer. [Note 1: With such a namelocale(name().c_str())succeeds and does not throwruntime_error. — end note]
std::ios_base::iword/pword might be misspecifiedSection: 31.5.2.6 [ios.base.storage] Status: New Submitter: Jiang An Opened: 2022-02-14 Last modified: 2022-03-04
Priority: 4
View all other issues in [ios.base.storage].
View all issues with New status.
Discussion:
Currently 31.5.2.6 [ios.base.storage] p5 and p8 say "On failure, a valid long&/void*&
initialized to 0". Such wording seems wrong, because a long&/void*& variable or
return value can't be initialized with 0. And the values of referenced objects may be underspecified,
because an implementation may reuse the same long/void* objects on failure, and thus it's insufficient
to specify the initial values of these objects only.
[2022-03-04; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
This wording is relative to N4901.
Modify 31.5.2.6 [ios.base.storage] as indicated:
long& iword(int idx);-3- […]
-4- […] -5- Returns: On successiarray[idx]. On failure, anvalidlvalue of typelongwith value&0Linitialized to.0void*& pword(int idx);-6- […]
-7- […] -8- Returns: On successparray[idx]. On failure, anvalidlvalue of typevoid*with a null pointer value&initialized to. -9- Remarks: After a subsequent call to0pword(int)for the same object, the earlier return value may no longer be valid.
std::chrono::time_zone might be overly unspecifiedSection: 30.11.5.1 [time.zone.overview] Status: New Submitter: Jiang An Opened: 2022-02-23 Last modified: 2025-03-19
Priority: 4
View all issues with New status.
Discussion:
In 30.11.5.1 [time.zone.overview], only defaulted move constructor and move assignment operator for
std::chrono::time_zone are shown, other constructors are said to be "unspecified additional constructors".
Presumably the intent is that the default constructor is not declared (suppressed) and the copy constructor is
implicitly deleted, but it is not clear if they are not "unspecified additional constructors" and hence
implicitly specified.
constexpr,
or noexcept. Perhaps we want these functions to be non-deleted and noexcept, while triviality
and constexpr-ness should be left unspecified.
[2022-03-04; Reflector poll]
Set priority to 4 after reflector poll.
[2025-03-18; Jonathan provides wording]
I don't think it matters whether they are trivial or constexpr, because they
cannot be used. Users only have access to const time_zone lvalues via
locate_zone and the tzdb::zones container.
The move constructor and move assignment operator only need to exist so that
the implementation can populate that container.
Proposed resolution:
This wording is relative to N5001.
Modify 30.11.5.1 [time.zone.overview] as indicated:
namespace std::chrono { class time_zone { time_zone(unspecified); public: time_zone(time_zone&&) = default; time_zone& operator=(time_zone&&) = default;// unspecified additional constructors[...] }; }
-1-
A time_zone represents all time zone transitions for a specific geographic
area.
time_zone construction is unspecified, and performed
only
as part of database initialization.
[Note 1:
const time_zone objects can be accessed via functions such as locate_zone.
— end note]
<ranges> sufficient for istream_view?Section: 25.6.6 [range.istream] Status: LEWG Submitter: Barry Revzin Opened: 2022-02-24 Last modified: 2022-11-10
Priority: 3
View all issues with LEWG status.
Discussion:
The following is rejected by libstdc++:
#include <ranges> void call(std::ranges::istream_view<int>& v);
The error is quite cryptic, but ultimately the issue is not including <istream>. I think
this currently isn't required to work, so the fact that it does not work is conforming. But should it be
required to work?
[2022-03-04; Reflector poll]
Set priority to 3 after reflector poll.
[Kona 2022-11-08; discussed at joint LWG/SG9 session. Send to LEWG (with suggested options)]
Proposed resolution:
move_only_function with empty ref-qualifier is over-constrainedSection: 22.10.17.4.3 [func.wrap.move.ctor] Status: New Submitter: Zhihao Yuan Opened: 2022-02-27 Last modified: 2022-03-04
Priority: 2
View other active issues in [func.wrap.move.ctor].
View all other issues in [func.wrap.move.ctor].
View all issues with New status.
Discussion:
The following test compiles and holds:
struct X
{
int operator()() & { return 1; } // #1
int operator()() && { return 2; } // #2
};
using T = move_only_function<int()>;
assert(T(X{})() == 1);
In other words, #2 is never used. But if you really remove #2, the code doesn't compile.
move_only_function<R(Args...) cv>
erasing callable objects with operator() without ref-qualifiers.
But the actual outcome outlawed programs that were valid when using std::function.
It also outlaws future programs such as
T x = [captures](this auto&) { return ...; }
where declaring this auto&& without forwarding only to satisfy
move_only_function's constraints would be strange.
[2022-03-04; Reflector poll]
Set priority to 2 after reflector poll. Probably needs a paper for LEWG.
Proposed resolution:
This wording is relative to N4901.
Modify 22.10.17.4.3 [func.wrap.move.ctor] as indicated:
template<class VT> static constexpr bool is-callable-from = see below;-1- If
noexistrue,is-callable-from<VT>is equal to:is_nothrow_invocable_r_v<R, VT cv ref, ArgTypes...> &&is_nothrow_invocable_r_v<R, VT inv-quals, ArgTypes...>Otherwise,
is-callable-from<VT>is equal to:is_invocable_r_v<R, VT cv ref, ArgTypes...> &&is_invocable_r_v<R, VT inv-quals, ArgTypes...>
pmr::vector without including <memory_resource>)Section: 20.5.1 [mem.res.syn], 20.2.2 [memory.syn] Status: New Submitter: Jiang An Opened: 2022-02-28 Last modified: 2025-10-22
Priority: 4
View all issues with New status.
Discussion:
The issue reflected in LWG 3679(i) is not limited to <ranges> and
std::ranges::istream_view.
#include <vector>
// some standard headers other than <memory> and <memory_resource>
template<class T> my_ator {/*definition, meeting the requirements of Cpp17Allocator*/};
int main()
{
std::vector<int> v1; // Generally works. Is this guaranteed?
std::pmr::vector<int> v2; // Usually fails to work on libstdc++. Is this intendedly permitted??
std::vector<int, my_ator<int>> v3; // Generally works. Is this guaranteed?
}
Currently libstdc++ only provides forward declarations of std::pmr::polymorphic_allocator
in headers of standard allocator-aware containers, which means that users are required to include
both <memory_resource> and <vector> in order to create a
std::pmr::vector<T> object. If libstdc++ is technically conforming here, one may say
the definition of std::allocator is also not guaranteed to be available in these headers,
so <memory> is required to be included together with such a header to make
standard-container<T> work.
std::allocator_traits. If these containers are not guaranteed to work when the definition of
std::allocator_traits is not available, and the definition of std::allocator_traits
is not guaranteed to be provided in headers of these containers, then users are effectively always
required to include <memory> in order to create a container object, even if they are
using their own allocators.
[2022-05-17; Reflector poll]
Set priority to 4 after reflector poll.
[2025-10-22; Jonathan comments after reflector discussion.]
"We could use the 'In addition to being available via inclusion of ...' form, see also LWG 3623(i)."
Proposed resolution:
Section: 16.4.4.6.1 [allocator.requirements.general] Status: New Submitter: Jiang An Opened: 2022-03-18 Last modified: 2022-05-17
Priority: 3
View other active issues in [allocator.requirements.general].
View all other issues in [allocator.requirements.general].
View all issues with New status.
Discussion:
Currently (at least since P0593R6), the allocate function of an allocator is
required to create an array of elements (16.4.4.6.1 [allocator.requirements.general]), which means
the allocated storage must be properly aligned (otherwise the array of requested size can't be created).
However, according to paragraph 12 it is also allowed that "the allocator also may silently ignore the
requested alignment".
[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll.
"You can allocate from such an allocator but you can't construct objects in the allocated memory without manually checking the alignment of the returned pointer. That doesn't seem useful in practice."
"maybe even NAD/LEWG? I can't see us declaring most in-the-wild allocators
to no longer be allocators. If anything, a design change is necessary to have
a protocol for allocators to declare proper support for overalignment and for
allocator_traits to implement such support "manually" for older
allocators."
Proposed resolution:
std::allocator<T>::allocate_at_least in constant evaluationSection: 20.2.10.2 [allocator.members] Status: New Submitter: Jiang An Opened: 2022-03-22 Last modified: 2022-05-17
Priority: 3
View all other issues in [allocator.members].
View all issues with New status.
Discussion:
std::allocator<T>::allocate_at_least is a constexpr function that allocates memory during
constant evaluation, but its restrictions is not clear. Presumably the restrictions are same as those of
std::allocator<T>::allocate, and we should specify allocate_at_least in term of allocate.
allocation_result<T*>{allocate(n), n} now. Perhaps we
should adopt this strategy for constant evaluation to avoid additional mechanism in the compiler.
[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll. Suggestion to fix this in Core instead.
Proposed resolution:
This wording is relative to N4910.
Modify 20.2.10.2 [allocator.members] as indicated:
[[nodiscard]] constexpr allocation_result<T*> allocate_at_least(size_t n);-6- Mandates:
-7- Returns:Tis not an incomplete type (6.9.1 [basic.types.general]).allocation_result<T*>{ptr, count}, whereptris a pointer to the initial element of an array ofcount Tandcount ≥ n. -8- Throws:bad_array_new_lengthifnumeric_limits<size_t>::max() / sizeof(T) < n, orbad_allocif the storage cannot be obtained. -9- Remarks: The storage for the array is obtained by calling::operator new, but it is unspecified when or how often this function is called. This function starts the lifetime of the array object, but not that of any of the array elements. This function returnsallocation_result<T*>{allocate(n), n}within the evaluation of a core constant expression.
lazy_split_view, CTAD doesn't work when given an input_range input and a tiny-range patternSection: 25.7.16.2 [range.lazy.split.view] Status: New Submitter: Konstantin Varlamov Opened: 2022-03-23 Last modified: 2022-05-17
Priority: 3
View other active issues in [range.lazy.split.view].
View all other issues in [range.lazy.split.view].
View all issues with New status.
Discussion:
In lazy_split_view, the deduction guide that accepts two arbitrary types wraps the arguments in
views::all_t (25.7.16.2 [range.lazy.split.view]):
template<class R, class P> lazy_split_view(R&&, P&&) -> lazy_split_view<views::all_t<R>, views::all_t<P>>;
When trying to use an input_range as the input, lazy_split_view requires the pattern
type to satisfy the exposition-only concept tiny-range. Trying to use CTAD with an
input_range and a tiny-range as arguments results in a compiler error, as demonstrated
in the demo link:
// AssumingInputRangeandTinyRangeare valid types satisfying the // corresponding concepts. std::ranges::lazy_split_view view{InputRange(), TinyRange()}; // Compiler error
The underlying reason is that tiny-range requires the given type to contain a static member function
size() that returns a number <=1 (25.7.16.2 [range.lazy.split.view]):
template<class R>
concept tiny-range = // exposition only
sized_range<R> &&
requires { typename require-constant<remove_reference_t<R>::size()>; } &&
(remove_reference_t<R>::size() <= 1);
However, when given a range, views::all_t wraps the type in a ranges::owning_view.
owning_view doesn't satisfy tiny-range for any template parameter because it
never contains the static size() function required by the concept.
owning_view so that it satisfies tiny-range
when the given type is a tiny-range (that would require moving the tiny-range
concept from 25.7.16.2 [range.lazy.split.view] to 25.5.2 [range.utility.helpers]). A more
localized solution can be to change the deduction guide in lazy_split_view to avoid wrapping
a type satisfying tiny-range in views::all_t.
[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll. One vote for NAD.
Proposed resolution:
This wording is relative to N4910.
Modify 25.5.2 [range.utility.helpers] as indicated:
[Drafting note: This change effectively just moves the definitions of
require-constantandtiny-rangefrom 25.7.16.2 [range.lazy.split.view] to 25.5.2 [range.utility.helpers].]
[…]
template<class T, class U>
concept different-from = // exposition only
!same_as<remove_cvref_t<T>, remove_cvref_t<U>>;
template<auto> struct require-constant; // exposition only
template<class R>
concept tiny-range = // exposition only
sized_range<R> &&
requires { typename require-constant<remove_reference_t<R>::size()>; } &&
(remove_reference_t<R>::size() <= 1);
Modify 25.7.6.3 [range.owning.view], class template owning_view synopsis, as indicated:
[…]
constexpr static auto size() requires tiny-range<R>
{ return R::size(); }
constexpr auto size() requires sized_range<R>
{ return ranges::size(r_); }
constexpr auto size() const requires sized_range<const R>
{ return ranges::size(r_); }
[…]
Modify 25.7.16.2 [range.lazy.split.view], class template lazy_split_view synopsis, as indicated:
[Drafting note: This change effectively just moves the definitions of
require-constantandtiny-rangefrom 25.7.16.2 [range.lazy.split.view] to 25.5.2 [range.utility.helpers].]
namespace std::ranges {
template<auto> struct require-constant; // exposition only
template<class R>
concept tiny-range = // exposition only
sized_range<R> &&
requires { typename require-constant<remove_reference_t<R>::size()>; } &&
(remove_reference_t<R>::size() <= 1);
template<input_range V, forward_range Pattern>
requires view<V> && view<Pattern> &&
indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
(forward_range<V> || tiny-range<Pattern>)
class lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> {
[…]
};
[…]
}
lazy_split_view, comparing a default-constructed outer-iterator or
inner-iterator with std::default_sentinel results in null pointer dereferenceSection: 25.7.16.3 [range.lazy.split.outer], 25.7.16.5 [range.lazy.split.inner] Status: New Submitter: Konstantin Varlamov Opened: 2022-03-23 Last modified: 2022-05-17
Priority: 3
View other active issues in [range.lazy.split.outer].
View all other issues in [range.lazy.split.outer].
View all issues with New status.
Discussion:
The internal iterator types outer-iterator and inner-iterator of
lazy_split_view are default-constructible, but trying to compare a default-constructed
instance of either of these classes to std::default_sentinel results in null pointer
dereference (and, in all likelihood, a crash), as demonstrated in this
demo link:
// AssumingOuterIteris an alias forouter-iteratorof // somelazy_split_viewinstantiation. OuterIter o; o == std::default_sentinel; // Null pointer dereference InnerIter i; // Similar toOuterIterabove. i == std::default_sentinel; // Null pointer dereference
This is due to unchecked pointer access in the implementation of outer-iterator
(25.7.16.3 [range.lazy.split.outer] p8):
return x.current == ranges::end(x.parent_->base_) && !x.trailing_empty_;
(parent_ is null for a default-constructed iterator x, making the access
to base_ invalid)
inner-iterator (25.7.16.5 [range.lazy.split.inner] p7):
auto [pcur, pend] = subrange{x.i_.parent_->pattern_};
(For a default-constructed inner-iterator x, i_ is a default-constructed
outer-iterator member variable and i_.parent_ is null, making the
access to pattern_ invalid)
std::default_sentinel to be a well-defined operation that returns true. Alternatively,
the corresponding operator== functions should add a non-normative note stating that the
iterator cannot be default-constructed.
[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll. Three votes for NAD.
Proposed resolution:
This wording is relative to N4910.
Modify 25.7.16.3 [range.lazy.split.outer] as indicated:
friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t);-8- Effects: Equivalent to:
if (!x.parent_) return true; return x.current == ranges::end(x.parent_->base_) && !x.trailing_empty_;
Modify 25.7.16.5 [range.lazy.split.inner], as indicated:
friend constexpr bool operator==(const inner-iterator& x, default_sentinel_t);-7- Effects: Equivalent to:
if (!x.i_.parent_) return true; auto [pcur, pend] = subrange{x.i_.parent_->pattern_}; […]
std::bad_expected_accessSection: 22.8.4 [expected.bad], 22.8.5 [expected.bad.void], 22.8.6.6 [expected.object.obs], 22.8.7.6 [expected.void.obs] Status: New Submitter: Jiang An Opened: 2022-03-24 Last modified: 2024-07-24
Priority: 2
View all issues with New status.
Discussion:
The move constructor and the move assignment operator of standard exception types are not covered by
17.9.3 [exception]/2, and thus it is currently effectively unspecified whether these move
functions of std::bad_expected_access<void> are noexcept. Furthermore,
std::bad_expected_access<void> has protected special member functions, which overrides
(or conflicts with?) the general rule in 17.9.3 [exception]/2.
std::bad_expected_access<E> stores an E object, and copying
the stored object may throw an exception. Is it intended that the copy functions of
std::bad_expected_access<E> are noexcept while those of E are not?
When the copy happens because a std::bad_expected_access<E> is caught by value, if the
copy constructor of E throws, std::terminate is called no matter whether that of
std::bad_expected_access<E> is noexcept. But
std::bad_expected_access<E> may be copied/moved in other circumstances.
I think the move constructor and the move assignment operator of a standard exception type should be
specified to be public and noexcept when they exist, although sometimes whether they exist may be
unspecified. And the move functions should also propagate the result of what() when the source
and target have the same dynamic type, except that they can leave the result of what() from
the source valid but unspecified.
Related to this, std::expected<T, E>::value overloads are specified to throw
std::bad_expected_access<std::decay_t<E>> when an E is contained.
However, it seems that the copy constructor of std::bad_expected_access is implicitly
deleted if E is move-only, so the throw-expression is ill-formed.
Is it intended that std::expected<T, E>::value is ill-formed in such cases?
[2022-05-17; Reflector poll]
Set priority to 2 after reflector poll.
[2023-05-25; Jonathan comments]
The last part was clarified by LWG 3843(i), confirming that
value() is ill-formed for move-only E.
[2024-07-24; Jonathan comments]
LWG 4031(i) made the move (and copy) operations of
bad_expected_access<void> non-throwing.
Proposed resolution:
num_get overflow determination unclear and incorrectSection: 28.3.4.3.2.3 [facet.num.get.virtuals] Status: New Submitter: Hubert Tong Opened: 2022-03-28 Last modified: 2022-05-17
Priority: 3
View other active issues in [facet.num.get.virtuals].
View all other issues in [facet.num.get.virtuals].
View all issues with New status.
Discussion:
28.3.4.3.2.3 [facet.num.get.virtuals] stage 3 specifies that "by the rules of" various strto* functions,
sequences of chars are converted to a numeric value (producing the "converted value" that is referred to).
strto* function
is not outside the range of representable values for long long, unsigned long long, float,
double, and long double; therefore (unless if we expect no range-related errors for those types),
the field "represents a value" other than the "converted value".
Issue 1: It is too subtle to have two distinct values without calling more attention to them by giving them names
aside from the prose descriptions.
If the field "represents" a value other than the value that would be returned from the appropriate strto*
function, then what value does the field "represent"?
Note that, strictly speaking, it is the process that results in the converted value that the wording says is obtained
"by the rules of" the strto* functions, which is not the same thing as saying that the value represented is
interpreted "by the rules of" the strto* functions.
If the field "represents" the mathematical value, then for unsigned integer types, all negative values cannot be
represented. This does not match existing practice.
If negative integer values are interpreted using the rules of the strto* functions by obtaining the
magnitude and then having it "negated (in the return type)", then the return type of strtoull is
unsigned long long, meaning (where unsigned long long is 64-bit) that "-18446744073709551615"
is 1 (even when converting to unsigned integer types of less width). That does not match existing practice.
It is also worth noting that negating in the return type does not work well if the magnitude is not representable as
a positive value in the return type (e.g., for signed integer types and their most negative representable values).
Issue 2: The effect of the minus sign with respect to unsigned integer types can reasonably be interpreted in ways
that do not match existing practice (and are presumably unintended). The interpretation that works better for
unsigned long long does not work as well for signed long long.
If the field does not "represent" the mathematical value, then for floating-point types, it is unclear whether
the minus sign takes effect before or after any possible rounding. For literals, the minus sign takes effect after
rounding.
Issue 3: The effect of the minus sign with respect to floating-point types is unclear.
Lastly, for floating-point types with signed infinities, there are no finite values outside the range of
representable values; therefore, conversions of all finite values to such types are specified to "succeed". That
does not match existing implementation practice.
Issue 4: The conditions for identifying range-related errors for conversions to floating-point types do not match
the conditions that constitute overflow for floating-point types. There is implementation divergence: libc++ appears
to check for floating-point overflow; libstdc++ appears to check for infinities.
[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
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 thanswapis used in the specification of a declarationDin 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_vin 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 nameswapis 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-likegetfunction templates. An implementation shall behave as if every tuple-likegetfunction template is found in the definition of such an entity. Furthermore, an implementation shall ensure that nogetfunction 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
getfunction 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-implselects tuple-likegetfunction 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-implselects tuple-likegetfunction templates.
Add at the end of 25.5.4.1 [range.subrange.general] (after the synopsis) as indicated:
[Drafting note: Although IIUC
pair-likeis not needed to handlearrayandsubrange.]
[…]
-?- Remarks:pair-likeselects tuple-likegetfunction templates.
Add after the synopsis of 25.7.23.2 [range.elements.view] as indicated:
[…]
-?- Remarks:has-tuple-elementselects tuple-likegetfunction templates. […]
Section: 23.2.7.1 [associative.reqmts.general], 23.2.8.1 [unord.req.general] Status: New Submitter: Jens Maurer Opened: 2022-04-19 Last modified: 2022-05-17
Priority: 3
View other active issues in [associative.reqmts.general].
View all other issues in [associative.reqmts.general].
View all issues with New status.
Discussion:
Keys for elements of associative containers are presented as const subobjects,
preventing their modification by user code according to 9.2.9.2 [dcl.type.cv] p4.
std::map<int, int> map; map.emplace(1, 2); using KT = std::map<int, int>::key_type; auto it = map.begin(); it->first.~KT(); new (const_cast<int*>(&it->first)) KT(3);
This, of course, breaks the ordering of the keys, and should be undefined behavior.
Related issue: CWG 2514.[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll. One vote for NAD.
Proposed resolution:
This wording is relative to N4910.
Modify 23.2.7.1 [associative.reqmts.general] as indicated:
-5- For
setandmultisetthe value type is the same as the key type. Formapandmultimapit is equal topair<const Key, T>. Ending the lifetime of the key subobject of a container element by means other than invoking a member function of the container results in undefined behavior.
Modify 23.2.8.1 [unord.req.general] as indicated:
-7- For
unordered_setandunordered_multisetthe value type is the same as the key type. Forunordered_mapandunordered_multimapit ispair<const Key, T>. Ending the lifetime of the key subobject of a container element by means other than invoking a member function of the container results in undefined behavior.
float/double/long double overloads be fused into template overloads?Section: 29.7 [c.math] Status: New Submitter: Jiang An Opened: 2022-04-28 Last modified: 2022-05-17
Priority: 2
View all other issues in [c.math].
View all issues with New status.
Discussion:
IIUC LWG 3234(i) will be resolved by the recently approved paper P1467R9. While considering adding the newly required overloads of math special functions to MSVC STL, I found that it may be more convenient to implement the whole overload set as a single function template.
However, it's unclear for me whether the every "overload for each cv-unqualified floating-point type", or every currently separately shown overloads in the synopsis of<cmath>, is
required to be a separated function. As discussed in
microsoft/STL#1335, if there were only a
separated double overload (usually comes from the C standard library) and a fused template
overload, calling the overload set with {} would be accepted, which is definitely ambiguous
when there are separated float/double/long double overloads.
I think it may be better to allow implementations to arbitrarily fuse the required overloads.
[2022-05-17; Reflector poll]
Set priority to 2 after reflector poll. One vote for NAD.
Proposed resolution:
traits_type::length be customizable?Section: 27.2.2 [char.traits.require] Status: New Submitter: Jiang An Opened: 2022-05-04 Last modified: 2022-05-17
Priority: 4
View other active issues in [char.traits.require].
View all other issues in [char.traits.require].
View all issues with New status.
Discussion:
MSVC STL's implementation of the std::quoted overload for const charT* calculates
the length of the NTCTS and stores the result within the return value. Because the returned value may
be output by std::basic_ostream specializations with different traits_types, this
strategy can be conforming only if all possible traits_type::length functions for the same
char_type have equivalent return values.
traits_type::length should be customizable. In a related PR,
Stephan T. Lavavej said :
I argue that you've found a defect in the
The original implementation and thechar_traitsspecification — it should say that whatevereq()does, it should considercharT()to be distinct from all other values, which aligns with the common understanding of how null-terminated strings behave.char_traits::lengthchange both handle arbitrary character types — the only difference would be for customchar_traitsthat consider null terminators to be equal to other values, which I have never seen used in practice (e.g. case-insensitive traits don't do this).
If it is decided that traits_type::length is customizable, then the implementation in MSVC STL
should be fixed. Otherwise, we should explicitly require in 27.2.2 [char.traits.require] that
whenever c is not "equal" to char_type(), traits_type::eq(c, char_type()) is
false.
charT values seems not easy to specify, as there may be no
operator== for charT, or the operator== behaves pathologically. IMO possible
way may be
using the equivalence relation of value representations, or
using == and only imposing the requirement when char_type is an encoded
character type (or more generally, a scalar type, while treating an enum type as its underlying type).
[2022-05-17; Reflector poll]
Set priority to 4 after reflector poll. One vote for NAD.
Proposed resolution:
Section: 31.2.2 [stream.types] Status: New Submitter: Jiang An Opened: 2022-05-07 Last modified: 2025-10-21
Priority: 3
View all issues with New status.
Discussion:
Raised from editorial issue #5240.
The phrase "signed basic integral types" in 31.2.2 [stream.types] has been present since C++98 but never defined. It is unclear whether "basic integral types" are "standard integer types" or "integer types" (including extended integer types). Asstd::streamoff should be wide enough to represent the largest possible file size, and
std::uintmax_t is used as the return type of std::filesystem::file_size, we should not
disallow std::streamoff to be an extended integer type which may be wider than long long.
On the other hand, as std::size_t and std::ptrdiff_t have already been allowed to be
extended integer types, std::streamsize should also be allowed to be an extended integer type for consistency.
So I think we should just use "signed integer types" instead of "signed basic integral types" in
31.2.2 [stream.types].
[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll.
[2025-06-16; Jonathan adds wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 31.2.2 [stream.types] as indicated:
using streamoff = implementation-defined;-1- The type
streamoffis a synonym for one of the signedbasicintegral types of sufficient size to represent the maximum possible file size for the operating system.256using streamsize = implementation-defined;-2- The type
streamsizeis a synonym for one of the signedbasicintegral types. It is used to represent the number of characters transferred in an I/O operation, or the size of I/O buffers.257256) Typically
long long.257) Most places where
streamsizeis used would usesize_tin C, orssize_tin POSIX.
[2025-10-21; Jonathan provides improved wording]
Proposed resolution:
This wording is relative to N5008.
Modify 31.2.2 [stream.types] as indicated:
using streamoff = implementation-defined;-1- The type
streamoffis a synonym for one of the signed integerbasic integraltypes of sufficient size to represent the maximum possible file size for the operating system.256using streamsize = implementation-defined;-2- The type
streamsizeis a synonym for one of the signed integerbasic integraltypes. It is used to represent the number of characters transferred in an I/O operation, or the size of I/O buffers.257256) Typically
long long.257) Most places where
streamsizeis used would usesize_tin C, orssize_tin POSIX.
reference_constructs_from_temporary/reference_converts_from_temporary seem wrongSection: 21.3.6.4 [meta.unary.prop] Status: New Submitter: Jiang An Opened: 2022-05-10 Last modified: 2022-05-17
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with New status.
Discussion:
std::reference_constructs_from_temporary and std::reference_converts_from_temporary are only
useful when T is a reference type, and a reference type is always complete. Whenever T is an
incomplete object type, it's clear that these traits inherit from std::false_type, without any further
detection which may result in UB. However, when T is X& or X&& where
X is an incomplete object type, UB may be needed because of the potentially problematic detection in
std::is_constructible or std::is_convertible.
[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4910.
Modify 21.3.3 [meta.type.synop], Table 46 ([tab:meta.unary.prop]) — "Type property predicates" — as indicated:
Table 46: Type property predicates [tab:meta.unary.prop] Template Condition Preconditions …template<class T, class U>
struct reference_constructs_from_temporary;conjunction_v<is_reference<T>, is_constructible<T, U>>is
true, and the initializationT t(VAL<U>);bindstto a
temporary object whose lifetime is extended (6.8.7 [class.temporary]).If Tis a reference type,remove_reference_t<T>
andUshall beTacomplete types,
cvvoid, oranarrays of unknown bound.template<class T, class U>
struct reference_converts_from_temporary;conjunction_v<is_reference<T>, is_convertible<U, T>>is
true, and the initializationT t = VAL<U>;bindstto a
temporary object whose lifetime is extended (6.8.7 [class.temporary]).If Tis a reference type,remove_reference_t<T>
andUshall be complete types,
cvvoid, or arrays of unknown bound.
lexically_relative on UNC drive paths (\\?\C:\...) results in a default-constructed valueSection: 31.12.6.5.11 [fs.path.gen] Status: New Submitter: Nicole Mazzuca Opened: 2022-05-12 Last modified: 2022-05-17
Priority: 3
View all other issues in [fs.path.gen].
View all issues with New status.
Discussion:
As a resolution to LWG 3070(i), in path lexically_relative(const path& base) const,
bullet 3.4 was added:
If: […] any filename in
relative_path()orbase.relative_path()can be interpreted as a root-name, […] returnspath().
This resolution was correct when we have really weird paths like abc\X:\c, but the MSVC standard library
implementation treats UNC drive-relative paths as:
\\?\C:\foo\bar = { root-name = \\?, root-directory = \, relative-path = C:\foo\bar }
If we were able to go back in time, we might have root-name = \\?\C:, but we can't make that
change at that point without silently breaking users; therefore, we believe it would be best to instead change
lexically_relative() to work around this issue.
If
relative_path().has_root_path() && base.relative_path().has_root_path(), andrelative_path().root_path() == base.relative_path().root_path(), thenreturn relative_path().lexically_relative(base.relative_path()).
[2022-05-17; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
std::format work with character arrays of unknown bound?Section: 28.5.6.4 [format.formatter.spec] Status: New Submitter: S. B. Tam Opened: 2022-05-31 Last modified: 2022-06-21
Priority: 3
View other active issues in [format.formatter.spec].
View all other issues in [format.formatter.spec].
View all issues with New status.
Discussion:
Consider
#include <format>
#include <iostream>
extern char str[];
auto result = std::format("{}", str);
char str[] = "hello";
int main()
{
std::cout << result << std::endl;
}
Currently MSVC STL (as well as fmtlib when fmt::format is used instead of std::format)
accepts the initializer of result, while libc++ produces an error (apparently because there's no
formatter for char[]).
Should this be valid?
Daniel:
This issue is similar to LWG 3701(i), but not the same, because the latter wantstemplate<size_t N> struct formatter<charT[N], charT>, while this one needs
template<>struct formatter<charT[], charT> (that is, without the array bound).
[2022-06-21; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 24.3.4.11 [iterator.concept.forward], 24.3.5.5 [forward.iterators] Status: New Submitter: Jiang An Opened: 2022-06-15 Last modified: 2022-07-06
Priority: 3
View all issues with New status.
Discussion:
There are two different definitions of multi-pass guarantee since P0896R4. The old one (perhaps introduced by N3066) seems less reasonable because it requires increment on rvalue iterators of class types, and the semantics of such increment is largely unspecified.
Perhaps only the new definition should be used.[2022-07-06; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
ranges::to reserves the wrong sizeSection: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: Hewill Kang Opened: 2022-06-20 Last modified: 2025-02-27
Priority: 4
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with New status.
Discussion:
In bullet 1.1.4 of ranges::to, if the Container satisfies container-insertable
and R models sized_range, it will first construct the Container with args...
and then preallocate memory by calling c.reserve().
However, this only makes sense when c is default-initialized. If instead the size of the Container
created by args... is not 0, the value passed into c.reserve() will be wrong, for example:
ranges::to<std::string>(std::views::single('o'), "hell");
The size of the string created by "hell" is already 4, whereas the size of
R is only 1, which makes c.reserve(1) useless.
[2022-07-08; Reflector poll]
Set priority to 4 after reflector poll. Some suggestions for NAD.
[2025-02-27; post-Hagenberg status]
The proposed resolution needs rebasing after P2846R6 was approved in Hagenberg. It also conflicts with LWG 4066(i).
Proposed resolution:
This wording is relative to N4910.
Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);-1- Returns: An object of type
Cconstructed from the elements ofrin the following manner:
(1.1) — If
convertible_to<range_reference_t<R>, range_value_t<C>>istrue:
(1.1.1) — If
constructible_from<C, R, Args...>istrue:C(std::forward<R>(r), std::forward<Args>(args)...)(1.1.2) — Otherwise, if
constructible_from<C, from_range_t, R, Args...>istrue:C(from_range, std::forward<R>(r), std::forward<Args>(args)...)(1.1.3) — Otherwise, if
(1.1.3.1) —
common_range<R>istrue,(1.1.3.2) —
cpp17-input-iterator<iterator_t<R>>istrue, and(1.1.3.3) —
constructible_from<C, iterator_t<R>, sentinel_t<R>, Args...>istrue:C(ranges::begin(r), ranges::end(r), std::forward<Args>(args)...)(1.1.4) — Otherwise, if
(1.1.4.1) —
constructible_from<C, Args...>istrue, and(1.1.4.2) —
container-insertable<C, range_reference_t<R>>istrue:C c(std::forward<Args>(args)...); if constexpr (sized_range<R> && reservable-container<C>) { using ST = range_size_t<C>; using CT = common_type_t<ST, range_size_t<R>>; auto sz = ST(CT(ranges::size(c)) + CT(ranges::size(r))); c.reserve(szranges::size(r)); } ranges::copy(r, container-inserter<range_reference_t<R>>(c));(1.2) — Otherwise, if
input_range<range_reference_t<R>>istrue:to<C>(r | views::transform([](auto&& elem) { return to<range_value_t<C>>(std::forward<decltype(elem)>(elem)); }), std::forward<Args>(args)...);(1.3) — Otherwise, the program is ill-formed.
reverse_iterator::operator-> should not use prev for non-pointer iteratorsSection: 24.5.1.6 [reverse.iter.elem] Status: New Submitter: Hewill Kang Opened: 2022-06-26 Last modified: 2024-06-18
Priority: 3
View all other issues in [reverse.iter.elem].
View all issues with New status.
Discussion:
When the underlying iterator is not a pointer type, reverse_iterator::operator-> returns
prev(current).operator->(). However, prev only works with
Cpp17BidirectionalIterator, given that C++20 bidirectional_iterator may just be
Cpp17InputIterator, we shouldn't use prev here.
[2022-07-08; Reflector poll]
Set priority to 3 after reflector poll.
Suggested to use ranges::prev instead.
[2024-06-18; Jonathan adds a comment]
It's not clear that std::prev requires Cpp17InputIterator,
that's the subject of LWG 3197(i).
Proposed resolution:
This wording is relative to N4910.
Modify 24.5.1.6 [reverse.iter.elem] as indicated:
constexpr pointer operator->() const requires (is_pointer_v<Iterator> || requires(const Iterator i) { i.operator->(); });-2- Effects:
(2.1) — If
Iteratoris a pointer type, equivalent to:return prev(current);(2.2) — Otherwise, equivalent to:
return prev(current).operator->();Iterator tmp = current; --tmp; return tmp.operator->();
operator<=>(tuple, tuple)Section: 22.4.9 [tuple.rel] Status: New Submitter: Corentin Jabot Opened: 2022-06-28 Last modified: 2022-07-08
Priority: 4
View other active issues in [tuple.rel].
View all other issues in [tuple.rel].
View all issues with New status.
Discussion:
The specification of operator<=>(tuple, tuple) (22.4.9 [tuple.rel]) is described in
terms of imaginary tuples (ttail, utail, rtail)
which is a bit confusing. Indeed, It is not clear that these imaginary tuples need to respect the order of
elements of u and t, nor whether the value category of the elements in these imaginary
tuples can or should be conserved. It is possible to reformulate and simplify that description so that
no imaginary tuple is involved.
operator==
[2022-07-08; Reflector poll]
Set priority to 4 after reflector poll.
Some votes for NAD and preference for the current wording, adding "in order"
to clarify the order of elements in rtail.
Proposed resolution:
This wording is relative to N4910.
Modify 22.4.9 [tuple.rel] as indicated:
template<class... TTypes, class... UTypes> constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...> operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u);-4-
EffectsReturns:synth-three-way(get<i>(t), get<i>(u))for the firstifor which the result of that expression does not compare equal to0. If no suchiexists,strong_ordering::equal.Performs a lexicographical comparison betweentandu. For any two zero-length tuplestandu,t <=> ureturnsstrong_ordering::equal. Otherwise, equivalent to:if (auto c = synth-three-way(get<0>(t), get<0>(u)); c != 0) return c; return ttail <=> utail;-?- Remarks: The elementary
wherertailfor some tupleris a tuple containing all but the first element ofr.synth-three-way(get<i>(t), get<i>(u))expressions are evaluated in order from the zeroth index upwards. No element accesses are performed after the first invocation that results in a value that does not compare equal to0.-5- [Note 1: The above definition does not requirettail(orutail) to be constructed. It might not even be possible, astanduare not required to be copy constructible. Also, all comparison operator functions are short circuited; they do not perform element accesses beyond what is required to determine the result of the comparison. — end note]
std::tuple_element_t<std::ranges::subrange<I, S, K>> should remove top-level cv-qualifiersSection: 25.2 [ranges.syn] Status: New Submitter: Jiang An Opened: 2022-06-30 Last modified: 2022-07-08
Priority: 4
View other active issues in [ranges.syn].
View all other issues in [ranges.syn].
View all issues with New status.
Discussion:
std::ranges::subrange<int * volatile>> is weird but valid. The return type (deduced type for
auto) of std::ranges::get for this type is int*, because the replacing of that
(cv-unqualified) auto drops top-level cv-qualifiers. I think get is doing the
right thing, and std::tuple_element_t should be consistent with the return types.
[2022-07-08; Reflector poll]
Set priority to 4 after reflector poll. "This is just contrived, especially since lots of the iterator requirements on such a type involve deprecated operations."
Proposed resolution:
This wording is relative to N4910.
Modify 25.2 [ranges.syn], header <ranges> synopsis, as indicated:
[…]
namespace std {
[…]
template<class I, class S, ranges::subrange_kind K>
struct tuple_element<0, ranges::subrange<I, S, K>> {
using type = remove_cv_t<I>;
};
template<class I, class S, ranges::subrange_kind K>
struct tuple_element<1, ranges::subrange<I, S, K>> {
using type = remove_cv_t<S>;
};
template<class I, class S, ranges::subrange_kind K>
struct tuple_element<0, const ranges::subrange<I, S, K>> {
using type = remove_cv_t<I>;
};
template<class I, class S, ranges::subrange_kind K>
struct tuple_element<1, const ranges::subrange<I, S, K>> {
using type = remove_cv_t<S>;
};
}
[…]
std::ranges::drop_view may have different size type from its underlying viewSection: 25.7.12.2 [range.drop.view] Status: New Submitter: Jiang An Opened: 2022-07-03 Last modified: 2022-07-17
Priority: 3
View other active issues in [range.drop.view].
View all other issues in [range.drop.view].
View all issues with New status.
Discussion:
The bodies of both overloads of drop_view<V>::size are specified as:
const auto s = ranges::size(base_); const auto c = static_cast<decltype(s)>(count_); return s < c ? 0 : s - c;
Given the return type is specified with auto, the actual return type
is the promoted type of the size type of the underlying view, which may be
different from the underlying size type (e.g. if the underlying size is unsigned short).
take_view always has the same size type as its underlying view.
So I think the difference on the size types is an oversight. On the other hand, the
const used here seems redundant and inconsistent with other parts of the
standard wording, although implementations may tend to use it.
[2022-07-08; Reflector poll]
Set priority to 3 after reflector poll.
"The PR is incorrect - integer-class types do not support mixed-signedess operations, so you have to cast one of the two first."
[2022-07-17; Daniel comments]
This issue should be resolved by keeping LWG 3739(i) and 3740(i) in mind.
Proposed resolution:
This wording is relative to N4910.
Modify 25.7.12.2 [range.drop.view], class template drop_view synopsis, as indicated:
[Drafting note:
sandcount_usually have different types, but I think it's safe to perform comparison and subtraction, ascount_is non-negative as long as the behavior is well-defined.]
[…]
constexpr auto size() requires sized_range<V> {
const auto s = ranges::size(base_);
const auto c = static_cast<decltype(s)>(count_);
return static_cast<decltype(s)>(s < ccount_ ? 0 : s - ccount_);
}
constexpr auto size() const requires sized_range<const V> {
const auto s = ranges::size(base_);
const auto c = static_cast<decltype(s)>(count_);
return static_cast<decltype(s)>(s < ccount_ ? 0 : s - ccount_);
}
[…]
zip_view and adjacent_view are underconstrainedSection: 25.7.25.2 [range.zip.view], 25.7.27.2 [range.adjacent.view], 25.7.33.2 [range.cartesian.view] Status: New Submitter: Hewill Kang Opened: 2022-07-04 Last modified: 2023-08-12
Priority: 3
View all other issues in [range.zip.view].
View all issues with New status.
Discussion:
Both zip_view::iterator's (25.7.25.3 [range.zip.iterator]) and
adjacent_view::iterator's (25.7.27.3 [range.adjacent.iterator])
operator* have similar Effects: elements:
return tuple-transform([](auto& i) -> decltype(auto) { return *i; }, current_);
where tuple-transform is defined as:
template<class F, class Tuple>
constexpr auto tuple-transform(F&& f, Tuple&& tuple) { // exposition only
return apply([&]<class... Ts>(Ts&&... elements) {
return tuple-or-pair<invoke_result_t<F&, Ts>...>(
invoke(f, std::forward<Ts>(elements))...
);
}, std::forward<Tuple>(tuple));
}
That is, zip_view::iterator will invoke the operator* of each iterator of
Views and return a tuple containing its reference.
reference of iterators is actually the reference type. However,
when the operator* returns a prvalue of non-movable type, tuple-transform will
be ill-formed since there are no suitable constructors for tuple:
#include <ranges>
struct NonMovable {
NonMovable() = default;
NonMovable(NonMovable&&) = delete;
};
auto r = std::views::iota(0, 5)
| std::views::transform([](int) { return NonMovable{}; });
auto z = std::views::zip(r);
auto f = *z.begin(); // hard error
We should constrain the range_reference_t of the underlying range to be move_constructible
when it is not a reference type, which also solves similar issues in zip_view::iterator and
adjacent_view::iterator's operator[] and iter_move.
[2022-08-23; Reflector poll]
Set priority to 3 after reflector poll.
"The constraint should just be move_constructible."
Previous resolution [SUPERSEDED]:
This wording is relative to N4910.
Modify 25.2 [ranges.syn], header
<ranges>synopsis, as indicated:namespace std::ranges { […] // 25.7.25 [range.zip], zip view template<class Ref> concept tuple-constructible-reference = see below; // exposition only template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && (tuple-constructible-reference<range_reference_t<Views>> && ...) class zip_view; […] // 25.7.27 [range.adjacent], adjacent view template<forward_range V, size_t N> requires view<V> && (N > 0) && tuple-constructible-reference<range_reference_t<V>> class adjacent_view; } […]Modify 25.7.25.2 [range.zip.view] as indicated:
namespace std::ranges { template<class Ref> concept tuple-constructible-reference = // exposition only is_reference_v<Ref> || move_constructible<Ref>; […] template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && (tuple-constructible-reference<range_reference_t<Views>> && ...) class zip_view : public view_interface<zip_view<Views...>> { […] }; […] }Modify 25.7.27.2 [range.adjacent.view] as indicated:
namespace std::ranges { template<forward_range V, size_t N> requires view<V> && (N > 0) && tuple-constructible-reference<range_reference_t<V>> class adjacent_view : public view_interface<adjacent_view<V, N>> { […] }; […] }
[2022-09-25; Hewill provides improved wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N4917.
Modify 25.2 [ranges.syn], header
<ranges>synopsis, as indicated:namespace std::ranges { […] template<class R> concept has-tuplable-ref = // exposition only move_constructible<range_reference_t<R>>; // 25.7.25 [range.zip], zip view template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && (has-tuplable-ref<Views> && ...) class zip_view; […] // 25.7.27 [range.adjacent], adjacent view template<forward_range V, size_t N> requires view<V> && (N > 0) && has-tuplable-ref<V> class adjacent_view; […] // 25.7.33 [range.cartesian], cartesian product view template<input_range First, forward_range... Vs> requires (view<First> && ... && view<Vs>) && (has-tuplable-ref<First> && ... && has-tuplable-ref<Vs>) class cartesian_product_view; […] }Modify 25.7.25.2 [range.zip.view] as indicated:
namespace std::ranges { […] template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && (has-tuplable-ref<Views> && ...) class zip_view : public view_interface<zip_view<Views...>> { […] }; }Modify 25.7.25.3 [range.zip.iterator] as indicated:
namespace std::ranges { […] template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && (has-tuplable-ref<Views> && ...) template<bool Const> class zip_view<Views...>::iterator { […] }; }Modify 25.7.25.4 [range.zip.sentinel] as indicated:
namespace std::ranges { template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && (has-tuplable-ref<Views> && ...) template<bool Const> class zip_view<Views...>::sentinel { […] }; }Modify 25.7.27.2 [range.adjacent.view] as indicated:
namespace std::ranges { template<forward_range V, size_t N> requires view<V> && (N > 0) && has-tuplable-ref<V> class adjacent_view : public view_interface<adjacent_view<V, N>> { […] }; }Modify 25.7.27.3 [range.adjacent.iterator] as indicated:
namespace std::ranges { template<forward_range V, size_t N> requires view<V> && (N > 0) && has-tuplable-ref<V> template<bool Const> class adjacent_view<V, N>::iterator { […] }; }Modify 25.7.27.4 [range.adjacent.sentinel] as indicated:
namespace std::ranges { template<forward_range V, size_t N> requires view<V> && (N > 0) && has-tuplable-ref<V> template<bool Const> class adjacent_view<V, N>::sentinel { […] }; }Modify 25.7.33.2 [range.cartesian.view] as indicated:
namespace std::ranges { […] template<input_range First, forward_range... Vs> requires (view<First> && ... && view<Vs>) && (has-tuplable-ref<First> && ... && has-tuplable-ref<Vs>) class cartesian_product_view : public view_interface<cartesian_product_view<First, Vs...>> { […] }; }Modify 25.7.33.3 [range.cartesian.iterator] as indicated:
namespace std::ranges { template<input_range First, forward_range... Vs> requires (view<First> && ... && view<Vs>) && (has-tuplable-ref<First> && ... && has-tuplable-ref<Vs>) template<bool Const> class cartesian_product_view<First, Vs...>::iterator { […] }; }
[2023-08-08; Hewill provides improved wording]
Proposed resolution:
This wording is relative to N4950.
Modify 26.2 25.2 [ranges.syn], header <ranges> synopsis, as indicated:
namespace std::ranges {
[…]
// 25.5.2 [range.utility.helpers], helper concepts
template<class R>
concept range-with-movable-references = see below; // exposition only
// 25.5.3 [view.interface], class template view_interface
template<class D>
requires is_class_v<D> && same_as<D, remove_cv_t<D>>
class view_interface; // freestanding
[…]
// 25.7.24 [range.enumerate], enumerate view
template<input_rangeview V>
requires range-with-movable-references<V>view<View>
class enumerate_view; // freestanding
[…]
// 25.7.25 [range.zip], zip view
template<range-with-movable-referencesinput_range... Views>
requires (view<Views> && ...) && (sizeof...(Views) > 0)
class zip_view; // freestanding
[…]
// 25.7.27 [range.adjacent], adjacent view
template<range-with-movable-referencesforward_range V, size_t N>
requires forward_range<V> && view<V> && (N > 0)
class adjacent_view; // freestanding
[…]
// 25.7.33 [range.cartesian], cartesian product view
template<range-with-movable-referencesinput_range First,
range-with-movable-referencesforward_range... Vs>
requires (view<First> && ... && (forward_range<Vs> && view<Vs>))
class cartesian_product_view; // freestanding
[…]
}
Modify 25.7.25.2 [range.zip.view] as indicated:
namespace std::ranges {
[…]
template<range-with-movable-referencesinput_range... Views>
requires (view<Views> && ...) && (sizeof...(Views) > 0)
class zip_view : public view_interface<zip_view<Views...>> {
[…]
public:
[…]
constexpr auto begin() const requires (range-with-movable-referencesrange<const Views> && ...) {
return iterator<true>(tuple-transform(ranges::begin, views_));
}
[…]
constexpr auto end() const requires (range-with-movable-referencesrange<const Views> && ...) {
if constexpr (!zip-is-common<const Views...>) {
return sentinel<true>(tuple-transform(ranges::end, views_));
} else if constexpr ((random_access_range<const Views> && ...)) {
return begin() + iter_difference_t<iterator<true>>(size());
} else {
return iterator<true>(tuple-transform(ranges::end, views_));
}
}
[…]
};
}
Modify 25.7.25.3 [range.zip.iterator] as indicated:
namespace std::ranges {
[…]
template<range-with-movable-referencesinput_range... Views>
requires (view<Views> && ...) && (sizeof...(Views) > 0)
template<bool Const>
class zip_view<Views...>::iterator {
[…]
};
}
Modify 25.7.25.4 [range.zip.sentinel] as indicated:
namespace std::ranges {
template<range-with-movable-referencesinput_range... Views>
requires (view<Views> && ...) && (sizeof...(Views) > 0)
template<bool Const>
class zip_view<Views...>::sentinel {
[…]
};
}
Modify 25.7.27.2 [range.adjacent.view] as indicated:
namespace std::ranges {
template<range-with-movable-referencesforward_range V, size_t N>
requires forward_range<V> && view<V> && (N > 0)
class adjacent_view : public view_interface<adjacent_view<V, N>> {
[…]
public:
[…]
constexpr auto begin() const requires range-with-movable-referencesrange<const V> {
return iterator<true>(ranges::begin(base_), ranges::end(base_));
}
[…]
constexpr auto end() const requires range-with-movable-referencesrange<const V> {
if constexpr (common_range<const V>) {
return iterator<true>(as-sentinel{}, ranges::begin(base_), ranges::end(base_));
} else {
return sentinel<true>(ranges::end(base_));
}
}
[…]
};
}
Modify 25.7.27.3 [range.adjacent.iterator] as indicated:
namespace std::ranges {
template<range-with-movable-referencesforward_range V, size_t N>
requires forward_range<V> && view<V> && (N > 0)
template<bool Const>
class adjacent_view<V, N>::iterator {
[…]
};
}
Modify 25.7.27.4 [range.adjacent.sentinel] as indicated:
namespace std::ranges {
template<range-with-movable-referencesforward_range V, size_t N>
requires forward_range<V> && view<V> && (N > 0)
template<bool Const>
class adjacent_view<V, N>::sentinel {
[…]
};
}
Modify 25.7.33.2 [range.cartesian.view] as indicated:
namespace std::ranges {
[…]
template<range-with-movable-referencesinput_range First,
range-with-movable-referencesforward_range... Vs>
requires (view<First> && ... && (forward_range<Vs> && view<Vs>))
class cartesian_product_view : public view_interface<cartesian_product_view<First, Vs...>> {
[…]
public:
[…]
constexpr iterator<true> begin() const
requires (range-with-movable-referencesrange<const First> && ... &&
range-with-movable-referencesrange<const Vs>);
[…]
constexpr iterator<true> end() const
requires (range-with-movable-references<const First> && ... &&
range-with-movable-references<const Vs>) &&
cartesian-product-is-common<const First, const Vs...>;
[…]
};
}
[…]constexpr iterator<true> begin() const requires (range-with-movable-referencesrange<const First> && ... && range-with-movable-referencesrange<const Vs>);-3- Effects: Equivalent to:
return iterator<true>(*this, tuple-transform(ranges::begin, bases_));constexpr iterator<false> end() requires ((!simple-view<First> || ... || !simple-view<Vs>) && cartesian-product-is-common<First, Vs...>); constexpr iterator<true> end() const requires (range-with-movable-references<const First> && ... && range-with-movable-references<const Vs>) && cartesian-product-is-common<const First, const Vs...>;-4- Let:
[…]
Modify 25.7.33.3 [range.cartesian.iterator] as indicated:
namespace std::ranges {
[…]
template<range-with-movable-referencesinput_range First, range-with-movable-referencesforward_range... Vs>
requires (view<First> && ... && (forward_range<Vs> && view<Vs>))
class cartesian_product_view<First, Vs...>::iterator {
[…]
};
}
copy_symlink(junction, new_symlink)'s behavior is unclearSection: 31.12.13.6 [fs.op.copy.symlink] Status: New Submitter: Nicole Mazzuca Opened: 2022-07-25 Last modified: 2022-08-23
Priority: 3
View all issues with New status.
Discussion:
The specification for copy_symlink is (31.12.13.6 [fs.op.copy.symlink]):
Effects: Equivalent to
function(read_symlink(existing_symlink), new_symlink)orfunction(read_symlink(existing_symlink, ec), new_symlink, ec), respectively, where in each casefunctioniscreate_symlinkorcreate_directory_symlinkas appropriate.
The specification for read_symlink is (31.12.13.29 [fs.op.read.symlink]):
Returns: If
presolves to a symbolic link, a path object containing the contents of that symbolic link.
And finally, the definition of a "symbolic link" is (31.12.1 [fs.general]):
A symbolic link is a type of file with the property that when the file is encountered during pathname resolution (31.12.6 [fs.class.path]), a string stored by the file is used to modify the pathname resolution.
On Unix, symlink is the only kind of symbolic link. However, on Windows, there are symbolic
link files which are not symlinks (app execution aliases and junctions) — this
means that read_symlink should almost certainly get the target of these files if possible.
However, copy_symlink specifically requires creating a symlink, not whatever
type of file was there originally. IMO, copy_symlink should require its target to be a symlink.
I think the original assumption was that read_symlink would take care of that for
copy_symlink; this is clearly not the case on Windows, though.
[2022-08-23; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
common_iterator and counted_iterator' operator- are missing cast to return typeSection: 24.5.5.6 [common.iter.cmp], 24.5.7.5 [counted.iter.nav] Status: New Submitter: Hewill Kang Opened: 2022-08-01 Last modified: 2022-08-23
Priority: 3
View all issues with New status.
Discussion:
Both common_iterator and counted_iterator explicitly specify that the
return type of their operator- is iter_difference_t<I2>, however,
given that the calculated type may be iter_difference_t<I>, we should do
an explicit conversion here since the latter is not necessarily implicitly convertible to the former:
#include <ranges>
struct Y;
struct X {
X(Y);
using difference_type =
#ifdef __GNUC__
std::ranges::__detail::__max_diff_type;
#elif defined(_MSC_VER)
std::_Signed128;
#endif
int& operator*() const;
X& operator++();
void operator++(int);
};
struct Y {
using difference_type = std::ptrdiff_t;
int& operator*() const;
Y& operator++();
void operator++(int);
};
int main() {
std::counted_iterator<Y> y;
return std::counted_iterator<X>(y) - y; // hard error in stdlibc++ and MSVC-STL
}
Daniel:
This issue shouldn't we voted until a decision for LWG 3749(i) has been made, because the first part of it overlaps with LWG 3749(i)'s second part.
[2022-08-23; Reflector poll]
Set priority to 3 after reflector poll.
"I think common_iterator should reject iterators with
integer-class difference types since it can't possibly achieve the design intent
of adapting them to Cpp17Iterators, so this issue should only affect
counted_iterator."
"If the difference types of I and I2 are different
then the operator- can't be used to model
sized_sentinel_for,
since i - i2 and i2 - i would have different types.
Providing operator- under such circumstances seems
to be of dubious value."
Proposed resolution:
This wording is relative to N4910.
Modify 24.5.5.6 [common.iter.cmp] as indicated:
template<sized_sentinel_for<I> I2, sized_sentinel_for<I> S2> requires sized_sentinel_for<S, I2> friend constexpr iter_difference_t<I2> operator-( const common_iterator& x, const common_iterator<I2, S2>& y);-5- Preconditions:
-6- Returns:x.v_.valueless_by_exception()andy.v_.valueless_by_exception()are eachfalse.0ifiandjare each1, and otherwisestatic_cast<iter_difference_t<I2>>(get<i>(x.v_) - get<j>(y.v_)), whereiisx.v_.index()andjisy.v_.index().
Modify 24.5.7.5 [counted.iter.nav] as indicated:
template<common_with<I> I2> friend constexpr iter_difference_t<I2> operator-( const counted_iterator& x, const counted_iterator<I2>& y);-13- Preconditions:
-14- Effects: Equivalent to:xandyrefer to elements of the same sequence (24.5.7.1 [counted.iterator]).return static_cast<iter_difference_t<I2>>(y.length - x.length);
std::vector and std::deque
should conditionally require Cpp17CopyInsertable in their preconditionsSection: 23.3.13.3 [vector.capacity], 23.3.13.5 [vector.modifiers], 23.3.5.3 [deque.capacity], 23.3.5.4 [deque.modifiers] Status: New Submitter: Jiang An Opened: 2022-08-24 Last modified: 2022-11-06
Priority: 3
View other active issues in [vector.capacity].
View all other issues in [vector.capacity].
View all issues with New status.
Discussion:
This issue is raised from editorial issue #5776.
In order to achieve strong exception safety, some operations ofstd:vector and
std::deque may use copy insertion for relocation of old elements, if move construction
of its element type is potentially throwing and copy insertion is available. However, currently
only Cpp17MoveInsertable is mentioned in many of their Preconditions (e.g. those of
insert for rvalues), which seemly fails to cover the cases in which copy insertion is
formally invalid but the semantic requirements of Cpp17CopyInsertable are not met.
Perhaps we should create a new named requirement for these operations, which is equivalent to
Cpp17CopyInsertable when !is_nothrow_move_constructible_v<T> &&
is_copy_constructible_v<T> is true, and equivalent to Cpp17MoveInsertable
otherwise.
[2022-09-23; Reflector poll]
Set priority to 3 after reflector poll.
Jonathan: I think the point (which LWG 3758 fails to explain clearly) is that today's implementations
sometimes use copy insertion when move insertion is syntactically valid, but is potentially-throwing.
But the preconditions don't require copy insertion. If vector::resize(size_type) decides to
use copy construction, because move construction might throw and the type is copy constructible (which
is implied to be permitted by the Remarks), do we require Cpp17CopyInsertable's semantic
requirement that the new value is equivalent to the one we copied? We don't say so.
tl;dr The user is trying to resize a vector and the value type is Cpp17MoveInsertable into the vector,
but the implementation decides to copy not move. What are the preconditions on the user's type?
[2022-11-06; Daniel comments]
This issue has considerable overlap with LWG 2158(i).
Proposed resolution:
iterator_category when its
difference_type is not an integer-class type?Section: 25.6.5.3 [range.repeat.iterator], 25.7.33.3 [range.cartesian.iterator] Status: New Submitter: Hewill Kang Opened: 2022-08-27 Last modified: 2023-02-07
Priority: 3
View all issues with New status.
Discussion:
After P2259, the range adaptor' iterators only provide iterator_category member when
the underlying range models forward_range, which is mainly based on the premise that all valid C++20
forward iterators meet the C++17 input iterator requirements.
difference_type is an integer-class
type, its iterator does not conform Cpp17InputIterator.
Although iterator_traits<I>::iterator_category will still deduce the correct category in this
case since these iterators have no reference member, it might be misleading to provide these
incorrect member types.
Do we need to aggressively prohibit these iterators from providing iterator_category when their
difference type is an integer-class type?
The proposed resolution makes repeat_view::iterator conditionally provide
iterator_category, because it explicitly mentions IOTA-DIFF-T(index-type)
in the definition of difference_type, which makes it consistent with LWG 3670(i).
It also removes the reference member type of cartesian_product_view::iterator,
which prevents iterator_traits<I>::iterator_category from being aliased to its member
iterator_category, so that iterator_traits<I>::iterator_category will not always
be an input_iterator_tag when its difference_type is an integer-class type.
This is also consistent with other range adaptors, as none of them have a reference member type.
[2022-09-23; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4917.
Modify 25.6.5.3 [range.repeat.iterator] as indicated:
namespace std::ranges {
template<move_constructible W, semiregular Bound = unreachable_sentinel_t>
requires (is_object_v<W> && same_as<W, remove_cv_t<W>> &&
(is-integer-like<Bound> || same_as<Bound, unreachable_sentinel_t>))
class repeat_view<W, Bound>::iterator {
private:
using index-type = // exposition only
conditional_t<same_as<Bound, unreachable_sentinel_t>, ptrdiff_t, Bound>;
const W* value_ = nullptr; // exposition only
index-type current_ = index-type(); // exposition only
constexpr explicit iterator(const W* value, index-type b = index-type()); // exposition only
public:
using iterator_concept = random_access_iterator_tag;
using iterator_category = random_access_iterator_tag; // present only if difference_type
// is an integral type
using value_type = W;
using difference_type = conditional_t<is-signed-integer-like<index-type>,
index-type,
IOTA-DIFF-T(index-type)>;
[…]
};
}
Modify 25.7.33.3 [range.cartesian.iterator] as indicated:
namespace std::ranges { template<input_range First, forward_range... Vs> requires (view<First> && ... && view<Vs>) template<bool Const> class cartesian_product_view<First, Vs...>::iterator { public: using iterator_category = input_iterator_tag; using iterator_concept = see below; using value_type = tuple<range_value_t<maybe-const<Const, First>>, range_value_t<maybe-const<Const, Vs>>...>;using reference = tuple<range_reference_t<maybe-const<Const, First>>, range_reference_t<maybe-const<Const, Vs>>...>;using difference_type = see below; […] constexpr autoreferenceoperator[](difference_type n) const requires cartesian-product-is-random-access<Const, First, Vs...>; […] }; }[…]
constexpr autoreferenceoperator[](difference_type n) const requires cartesian-product-is-random-access<Const, First, Vs...>;-24- Effects:
return *((*this) + n);
cartesian_product_view produces an invalid range if the first range is input and one of the ranges is emptySection: 25.7.33.2 [range.cartesian.view] Status: Open Submitter: Tomasz Kamiński Opened: 2022-09-12 Last modified: 2023-02-07
Priority: 2
View all issues with Open status.
Discussion:
In case when cartesian_product_view is common and one of the inner ranges is empty,
it needs to produce equal iterators from begin/end. We currently create a
sequence of begin iterators as both begin and end iterators. This
assumes that begin iterator is copyable, which may not be the case with the input range,
even in the case if that range is common — in such case, we require that only sentinel
is semantically copy-constructible, not begin even if they are the same type.
directory_iterator) are syntactically
copy-constructible, but only default constructed object, that corresponds to sentinels are
semantically copyable — the copy produces an equivalent result. As a consequence for
directory_iterator d, and empty std::string_view sv, the
view::cartesian_product(d, sv) produces an invalid range.
To fix the problem, we need to move the logic of adjusting the first range iterator to return
[end, begin, ..., begin] for begin. This is safe, as we require the end
to be always semantically copy-constructible. This again can be done only if computing the end
can be done in 𝒪(1) i.e. the first range is common.
[2022-09-28; Reflector poll]
Set priority to 2 after reflector poll.
[2022-09-28; LWG telecon]
Discussed issue. Tim suggested to add a new semantic requirement to
sentinel_for that when S and I are the same type
then i == i is true for any non-singular i of type I.
Proposed resolution:
This wording is relative to N4910.
Modify 25.7.33.2 [range.cartesian.view] as indicated:
[Drafting note: We can optimize the comparison with
default_sentinel_tto compare only the iterator to the first range if the range is common. This is observable, as we call comparison of user-provided iterators.]
constexpr iterator<false> begin() requires (!simple-view<First> || ... || !simple-view<Vs>);
-2- Effects: Equivalent to:return iterator<false>(tuple-transform(ranges::begin, bases_));constexpr iterator<true> begin() const requires (range<const First> && ... && range<const Vs>);
-3- Effects: Equivalent to:return iterator<true>(tuple-transform(ranges::begin, bases_));constexpr iterator<false> end() requires ((!simple-view<First> || ... || !simple-view<Vs>) && cartesian-product-is-common<First, Vs...>); constexpr iterator<true> end() const requires cartesian-product-is-common<const First, const Vs...>;-4- Let:
(4.1) —
is-constbetruefor the const-qualified overloads, andfalseotherwise;(4.?) —
is-endbetruefor theendoverloads, andfalseotherwise;(4.2) —
is-emptybetrueif the expressionranges::empty(rng)istruefor anyrngamong the underlying ranges except the first one andfalseotherwise; and(4.3) —
begin-or-first-end(rng)be expression-equivalent tois-end || is-empty ? cartesian-common-arg-end(rng) : ranges::begin(rng)ifis-empty ? ranges::begin(rng) : cartesian-common-arg-end(rng)cartesian-product-common-arg<maybe-const<is-const, First>>istrueandrngis the first underlying range, andranges::begin(rng)otherwise.-5- Effects: Equivalent to:
iterator<is-const> it(tuple-transform( [](auto& rng){ return begin-or-first-end(rng); }, bases_)); return it;
Modify 25.7.33.3 [range.cartesian.iterator] as indicated:
friend constexpr bool operator==(const iterator& x, default_sentinel_t);-26- Returns:
(?.1) — If
cartesian-product-common-arg<maybe-const<Const, First>>istrue, returnsstd::get<0>(x.current_) == ranges::end(std::get<0>(x.parent_->bases_)).(?.2) — Otherwise,
iftruestd::get<i>(x.current_) == ranges::end(std::get<i>(x.parent_->bases_))istruefor any integer0 ≤ i ≤ sizeof...(Vs),; otherwise,returnsfalsetrue.(?.3) — Otherwise, returns
false.
views::common may not be a range adaptor objectSection: 24.5.5.1 [common.iterator], 24.5.5.6 [common.iter.cmp] Status: New Submitter: Hewill Kang Opened: 2022-09-18 Last modified: 2022-10-12
Priority: 3
View other active issues in [common.iterator].
View all other issues in [common.iterator].
View all issues with New status.
Discussion:
P2325R3 makes input_or_output_iterator no longer require default_initializable,
which means that the template parameter I of common_iterator no longer requires
default_initializable.
common_iterator itself cannot be default-constructed, it can never be a valid
sentinel even if it can be compared to itself. Furthermore, this also makes views::common return a
non-range even if it is well-formed (online example):
#include <ranges>
#include <vector>
int main() {
std::vector<int> v;
auto r = std::views::counted(std::back_inserter(v), 3);
auto cr = r | std::views::common;
static_assert(std::ranges::range<decltype(cr)>); // failed
}
which causes views::common to be unable to convert a range into a view,
making it not a valid range adaptor.
common_iterator should always be default_initializable,
which makes it eligible to be a legitimate sentinel.
The proposed resolution provides a default constructor for common_iterator when I is
not default_initializable, in which case constructs the variant with an alternative type
of S.
[2022-09-28; Reflector poll]
Set priority to 3 after reflector poll.
"The P/R means that sometimes the variant containers an iterator and sometimes contains a sentinel, depending on whether the iterator is default constructible. Always constructing a sentinel would be more consistent."
Proposed resolution:
This wording is relative to N4917.
Modify 24.5.5.1 [common.iterator], class template common_iterator synopsis, as indicated:
namespace std {
template<input_or_output_iterator I, sentinel_for<I> S>
requires (!same_as<I, S> && copyable<I>)
class common_iterator {
public:
constexpr common_iterator() requires default_initializable<I> = default;
constexpr common_iterator();
[…]
};
[…]
}
Modify 24.5.5.3 [common.iter.const] as indicated:
constexpr common_iterator();-?- Effects: Initializes
v_as if byv_{in_place_type<S>}.constexpr common_iterator(I i);-1- Effects: Initializes
v_as if byv_{in_place_type<I>, std::move(i)}.
Size template parameters are unclearSection: 26.6.5 [alg.foreach], 26.6.15 [alg.search], 26.7.1 [alg.copy], 26.7.6 [alg.fill], 26.7.7 [alg.generate] Status: New Submitter: Jiang An Opened: 2022-10-05 Last modified: 2022-10-12
Priority: 3
View other active issues in [alg.foreach].
View all other issues in [alg.foreach].
View all issues with New status.
Discussion:
Algorithms std::for_each_n, std::search_n, std::copy_n,
std::fill_n, and std::generate_n have similar requirements for the
Size template parameter in Mandates, requiring Size to be
convertible to an integral type.
Size is
converted before further operations. There is implementation divergence:
It is also notable that when the conversion from the source type to integral types is sufficiently ambiguous, none of these implementations accepts such a source type.
For example, currently the following program is rejected by all mainstream implementations.
#include <algorithm>
#include <cstdio>
struct BadFrom {
operator short() const { return 1; }
operator unsigned short() const { return 1; }
};
int main()
{
int arr[42]{};
std::for_each_n(arr, BadFrom{}, [&arr](int i)
{
std::printf("%d\n", i);
});
}
I think libc++'s strategy make the most sense. But is it really intended to support using a
floating-point type or a class type as Size?
Distance and for the underspecified Size template parameter in
various uninitialized_*_n and destroy_n algorithms in
26.11.2 [special.mem.concepts].
ranges::destroy_n has the luxury to simply require iter_difference_t<I>.
[2022-10-12; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
std::filesystem::path::iterator::reference should be allowed to be std::filesystem::pathSection: 31.12.6.6 [fs.path.itr] Status: New Submitter: Jiang An Opened: 2022-10-17 Last modified: 2022-11-01
Priority: 3
View all other issues in [fs.path.itr].
View all issues with New status.
Discussion:
Currently, 31.12.6.6 [fs.path.itr]/2 effectively requires std::filesystem::path::iterator::reference to be
a reference type, due to the requirements for (legacy) bidirectional iterators. However, it's reasonable for the
operator* to return path by value, which can make the iterator model std::bidirectional_iterator,
be compatible with std::reverse_iterator, and avoid complicated data structures (e.g. those in libstdc++)
for achieving such purpose.
[2022-11-01; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4917.
Modify 31.12.6.6 [fs.path.itr] as indicated:
-2- A
path::iteratoris a constant iterator meeting all the requirements of a bidirectional iterator (24.3.5.6 [bidirectional.iterators]) except that, for dereferenceable iteratorsaandbof typepath::iteratorwitha == b, there is no requirement that*aand*bare bound to the same object, and itsreferencemay bepath. Itsvalue_typeispath.
elements_view insufficiently constrainedSection: 25.7.23.2 [range.elements.view] Status: New Submitter: Hui Xie Opened: 2022-10-21 Last modified: 2022-11-01
Priority: 2
View all other issues in [range.elements.view].
View all issues with New status.
Discussion:
This issue came up when I tried to integrate the C++23 changes to tuple-like into ranges::elements_view
in libc++. Given the following test:
Using SubRange = ranges::subrange<MoveOnlyIter, Sent>;
std::vector<SubRange> srs = ...; // a vector of subranges
for(auto&& iter : srs | views::elements<0>){
}
The above code results in a hard error in deciding the iterator_category (The base is a random access range
so it should exist). The immediate hard error complains that the following expression is invalid.
std::get<N>(*current_);
Note that even if iterator_category does not complain, it will complain later when we dereference the iterator.
subrange:
template<size_t N, class I, class S, subrange_kind K> requires ((N == 0 && copyable<I>) || N == 1) constexpr auto get(const subrange<I, S, K>& r); template<size_t N, class I, class S, subrange_kind K> requires (N < 2) constexpr auto get(subrange<I, S, K>&& r);
Note that the first overload requires copyable<I> which is false and
the second overload requires an rvalue, which is also not the case. So we don't have a valid "get" in this case.
elements_view allow the instantiation in the first place? Let's look at its requirements:
template<class T, size_t N>
concept returnable-element = // exposition only
is_reference_v<T> || move_constructible<tuple_element_t<N, T>>;
template<input_range V, size_t N>
requires view<V> && has-tuple-element<range_value_t<V>, N> &&
has-tuple-element<remove_reference_t<range_reference_t<V>>, N> &&
returnable-element<range_reference_t<V>, N>
class elements_view;
It passed the "is_reference_v<range_reference_t<V>>" requirement, because it is
"subrange&". Here the logic has an assumption: if the tuple-like is a reference,
then we can always "get" and return a reference. This is not the case for subrange.
subrange's get always return by value.
[2022-11-01; Reflector poll]
Set priority to 2 after reflector poll.
"The actual issue is that P2165 broke
has-tuple-element for this case. We should unbreak it."
Proposed resolution:
This wording is relative to N4917.
[Drafting Note: Three mutually exclusive options are prepared, depicted below by Option A, Option B, and Option C, respectively.]
Option A: Properly disallow this case (preferred solution)
Modify 25.7.23.2 [range.elements.view] as indicated:
namespace std::ranges {
[…]
template<class T, size_t N>
concept returnable-element = // exposition only
requires { std::get<N>(declval<T>()); } &&
is_reference_v<T> || move_constructible<tuple_element_t<N, T>>;
[…]
}
Option B: Relax subrange's get to have more overloads. Since subrange's
non-const begin unconditionally moves the iterator (even for lvalue-reference),
[[nodiscard]] constexpr I begin() requires (!copyable<I>); Effects: Equivalent to: return std::move(begin_);
if we add more get overloads, it would work. The non-const lvalue-ref overload would work
(and it also moves because non-const lvalue begin moves). This solution would make another way
to let subrange's iterator in moved-from state, which is not good.
Modify 25.2 [ranges.syn] as indicated:
[…]
namespace std::ranges {
[…]
template<size_t N, class I, class S, subrange_kind K>
requires ((N == 0 && copyable<I>) || N == 1)
constexpr auto get(const subrange<I, S, K>& r);
template<size_t N, class I, class S, subrange_kind K>
requires (N < 2)
constexpr auto get(subrange<I, S, K>&& r);
template<size_t N, class I, class S, subrange_kind K>
requires ((N == 0 && constructible_from<I, const I&&>) || N == 1)
constexpr auto get(const subrange<I, S, K>&& r);
template<size_t N, class I, class S, subrange_kind K>
requires (N < 2)
constexpr auto get(subrange<I, S, K>& r);
}
[…]
Option C: Make subrange's get to return by reference. This seems to significantly
change the subrange's tuple protocol, which is not ideal.
<math.h> provide 3-argument ::hypot overloads?Section: 17.15.7 [support.c.headers.other] Status: New Submitter: Jiang An Opened: 2022-10-22 Last modified: 2022-11-01
Priority: 3
View other active issues in [support.c.headers.other].
View all other issues in [support.c.headers.other].
View all issues with New status.
Discussion:
See also LWG 3782(i). Like lerp, neither <math.h> nor C compatibility is mentioned in
P0030R1, and MSVC STL decides not to declare 3-argument hypot overloads in the global namespace
(perhaps so does libc++).
<math.h>.
[2022-11-01; Reflector poll]
Set priority to 3 after reflector poll.
"This affects the exports of std.compat".
Proposed resolution:
flat_foo allocator-extended constructors lack move semanticsSection: 23.6.8 [flat.map], 23.6.9 [flat.multimap], 23.6.11 [flat.set], 23.6.12 [flat.multiset] Status: New Submitter: Arthur O'Dwyer Opened: 2022-10-25 Last modified: 2023-06-16
Priority: 2
View other active issues in [flat.map].
View all other issues in [flat.map].
View all issues with New status.
Discussion:
Compare 23.6.4.2 [priqueue.cons]'s overload set
priority_queue(const Compare&, const Container&); priority_queue(const Compare&, Container&&); template<class Alloc> priority_queue(const Compare&, const Container&, const Alloc&); template<class Alloc> priority_queue(const Compare&, Container&&, const Alloc&);
against 23.6.8 [flat.map]'s overload set
flat_map(key_container_type, mapped_container_type); template<class Allocator> flat_map(const key_container_type&, const mapped_container_type&, const Allocator& a);
I see two issues here:
(A) The allocator-extended ctor of flat_map always copies the key_container and value_container,
when it should be move-enabled.
(B) Almost certainly the Allocator parameter should be named Alloc instead, and there should be a
separate "Constructors with allocators" section with wording similar to 23.6.4.3 [priqueue.cons.alloc] explaining that
these ctors don't participate in overload resolution unless
uses_allocator_v<KeyContainer, Alloc> && uses_allocator_v<MappedContainer, Alloc>.
I suggest this overload set to replace the two overloads above:
flat_map(key_container_type, mapped_container_type); template<class Alloc> flat_map(const key_container_type&, const mapped_container_type&, const Alloc& a); template<class Alloc> flat_map(const key_container_type&, mapped_container_type&&, const Alloc& a); template<class Alloc> flat_map(key_container_type&&, const mapped_container_type&, const Alloc& a); template<class Alloc> flat_map(key_container_type&&, mapped_container_type&&, const Alloc& a);
This preserves the apparent assumption that KeyContainer(std::move(kc)) is always efficient but
KeyContainer(std::move(kc), otheralloc) might not be. Similar wording changes would have to be made to all the
flat_foo containers.
template<class T, class Comp = std::less<T>, class Container = std::pmr::vector<T>>
using pmr_flat_set = std::flat_set<T, Comp, Container>;
std::pmr::vector<pmr_flat_set<int>> vs;
std::pmr::vector<int> data = {1,2,3};
vs.reserve(1);
vs.emplace_back(std::move(data));
// constructs-in-place with the argument list (std::move(data), get_allocator())
// BEFORE: copies (causes heap traffic)
// AFTER: moves (no heap traffic)
[2022-11-04; Reflector poll]
Set priority to 2 after reflector poll.
[2023-06-14 Varna]
Mentioned in P2767R0, but not resolved by it.
Proposed resolution:
flat_foo missing some allocator-extended deduction guidesSection: 23.6.8 [flat.map], 23.6.9 [flat.multimap], 23.6.11 [flat.set], 23.6.12 [flat.multiset] Status: New Submitter: Arthur O'Dwyer Opened: 2022-10-25 Last modified: 2022-11-04
Priority: 2
View other active issues in [flat.map].
View all other issues in [flat.map].
View all issues with New status.
Discussion:
Tony Table:
std::vector<int> v; std::flat_set s = std::flat_set(v, MyAllocator<int>()); std::flat_set s = std::flat_set(v, std::less(), MyAllocator<int>()); std::flat_set s = std::flat_set(v.begin(), v.end(), MyAllocator<int>()); std::flat_set s = std::flat_set(v.begin(), v.end(), std::less(), MyAllocator<int>()); // BEFORE: all fail to compile // AFTER: all compile successfully
Contrast 23.6.11.3 [flat.set.cons] with 23.6.4.2 [priqueue.cons], where most of these are okay:
std::vector<int, MyAllocator<int>> v; std::priority_queue pq1 = std::priority_queue(v, std::less(), MyAllocator<int>()); std::priority_queue pq2 = std::priority_queue(v.begin(), v.end(), MyAllocator<int>()); std::priority_queue pq3 = std::priority_queue(v.begin(), v.end(), std::less(), MyAllocator<int>()); // BEFORE AND AFTER: pq1 compiles successfully // BEFORE AND AFTER: pq2 and pq3 also compile successfully thanks to LWG 3506(i)
[2022-11-04; Reflector poll]
Set priority to 2 after reflector poll.
Proposed resolution:
Section: 22.10 [function.objects], 25.5.7.3 [range.utility.conv.adaptors], 25.7.2 [range.adaptor.object] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2022-10-26 Last modified: 2022-11-01
Priority: 4
View all other issues in [function.objects].
View all issues with New status.
Discussion:
22.10.3 [func.def] defines
-6- A call wrapper is an object of a call wrapper type.
Most importantly, a call wrapper is an object.
A number of functions in 22.10 [function.objects] and expressions in 25.5.7.3 [range.utility.conv.adaptors] and 25.7.2 [range.adaptor.object] are specified to result in a call wrapper. Most notably, the return type ofranges::to is auto, so its result is definitely a prvalue and not a "call wrapper" object.
Where a prvalue result is meant, the wording should be clarified to mean
"a prvalue whose object it initializes is a call wrapper".
[2022-11-01; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
propagate_const conversion functionSection: 6.1.2.6 [fund.ts.v3::propagate_const.const_observers] Status: New Submitter: Giuseppe D'Angelo Opened: 2022-11-04 Last modified: 2022-11-12
Priority: 3
View all issues with New status.
Discussion:
Addresses: fund.ts.v3
This issue has its origin in the discussion of gcc issue 107525.
The current draft of LFTSv3 specifies this conversion function forpropagate_const in
6.1.2.6 [fund.ts.v3::propagate_const.const_observers]:
constexpr operator const element_type*() const;-7- Returns:
-8- Remarks: This function shall not participate in overload resolution unlessget().Tis an object pointer type or has an implicit conversion toconst element_type*.
The constraint should however specify that const T (and not T) needs
to have an implicit conversion to const element_type *.
const T cannot do the conversion, then neither const propagate_const<T>
should be able to.
One can design a type X such as a const X cannot convert to const element_type *
(for instance, by =deleteing the corresponding conversion function). If now one asks whether
const propagate_const<X> is convertible to const element_type *, the answer is
(surprisingly) "yes".
[Kona 2022-11-12; Set priority to 3]
Proposed resolution:
This wording is relative to N4840.
Modify 6.1.2.6 [fund.ts.v3::propagate_const.const_observers] as indicated:
constexpr operator const element_type*() const;-7- Returns:
-8- Remarks: This function shall not participate in overload resolution unlessget().Tis an object pointer type orconst Thas an implicit conversion toconst element_type*.
std::span<volatile T, E> is made ill-formed by P2278R4 when T is a normal class typeSection: 23.7.2.2.1 [span.overview] Status: New Submitter: Jiang An Opened: 2022-11-06 Last modified: 2025-03-22
Priority: 2
View all other issues in [span.overview].
View all issues with New status.
Discussion:
This issue is discovered when implementing the
span part of P2278R4 in MSVC STL.
const_iterator member type to std::span, which required its
iterator type to model std::input_iterator (same for const_reverse_iterator and
reverse_iterator).
However, when element_type is volatile T and T is a class type, the iterator
type generally fails to satisfy input_iterator, because:
input_iterator<iterator> requires indirectly_readable<iterator>;
indirectly_readable<iterator> requires common_reference_with<iterator_reference_t<iterator>&&,
iterator_rvalue_reference_t<iterator>&&>, that is
common_reference_with<volatile T&, volatile T&&>;
common_reference_t<volatile T&, volatile T&&> is T (which is problematic),
and thus common_reference_with<volatile T&, volatile T&&> requires both
convertible_to<volatile T&, T> and convertible_to<volatile T&&, T>;
However, the class type T generally doesn't have constructors from volatile T or
const volatile T glvalues, and thus neither convertible_to<volatile T&, T> and
convertible_to<volatile T&&, T> is satisfied.
Ideally, span should not require any form of construction of element_type. Although usages of
class types provided by the standard library via volatile glvalues are generally not supported, I think
span<volatile T, E> is still useful for some user-defined class type T, and thus shouldn't be forbidden.
[Kona 2022-11-12; Set priority to 2]
[2025-03-22; Jiang An comments and provides wording]
The proposed resolution relaxes the input_iterator concept (which in turn relaxes stronger iterator concepts)
and extends iter_const_reference_t and the exposition only iter-const-rvalue-reference-t
aliases. Another approach can be just relaxing the indirectly_readable concept, but its impact may be larger
than expected.
Proposed resolution:
This wording is relative to N5008.
Modify 24.2 [iterator.synopsis], header <iterator> synopsis, as indicated:
[…] // 24.3.4.9 [iterator.concept.input], concept input_iterator template<class I> concept deref-to-value-t = see below; // freestanding template<class I> concept weakly-indirectly-readable = see below; // freestanding template<class I> concept input_iterator = see below; // freestanding […] // 24.5.3.2 [const.iterators.alias], alias templates template<indirectly_readableweakly-indirectly-readable I> using iter_const_reference_t = see below;
Modify 24.3.4.9 [iterator.concept.input], concept input_iterator synopsis, as indicated:
template<class I>
concept deref-to-value-t-impl = // exposition only
same_as<remove_cvref_t<iter_reference_t<I>>, iter_value_t<I>> &&
is_object_v<iter_value_t<I>>;
template<class I>
concept deref-to-value-t = // exposition only
deref-to-value-t-impl<remove_cvref_t<I>>;
template<class I>
concept weakly-indirectly-readable = // exposition only
deref-to-value-t<I> ||
indirectly_readable<I>;
template<class I>
concept input_iterator =
input_or_output_iterator<I> &&
indirectly_readableweakly-indirectly-readable<I> &&
requires { typename ITER_CONCEPT(I); } &&
derived_from<ITER_CONCEPT(I), input_iterator_tag>;
Modify 24.5.3.2 [const.iterators.alias] as indicated:
template<class T> using ref-add-const-t = see below;-?- Result: If
is_lvalue_reference_v<T>istrue,const remove_reference_t<T>&. Otherwise, ifis_rvalue_reference_v<T>istrue,const remove_reference_t<T>&&. Otherwise,T.template<indirectly_readableweakly-indirectly-readable It> using iter_const_reference_t = see below;common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>>;-?- Result: If
Itmodelsderef-to-value-t,ref-add-const-t<iter_reference_t<It>>. Otherwise,common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>>.
[Drafting note: The simple
deref-to-value-tcase should be detected first, which avoids unnecessary instantiations and IFNDR-ness.]
Modify 24.5.3.3 [const.iterators.iterator] as indicated:
[…] template<indirectly_readableweakly-indirectly-readable I> using iter-const-rvalue-reference-t = // exposition only common_reference_t<const iter_value_t<I>&&, iter_rvalue_reference_t<I>>; […]
as_rvalue_view::end should improve non-common caseSection: 25.7.7.2 [range.as.rvalue.view] Status: New Submitter: Hewill Kang Opened: 2022-11-13 Last modified: 2022-11-30
Priority: 3
View all issues with New status.
Discussion:
Currently, when the underlying range is not a common range, as_rvalue_view::begin and end return
move_iterator and move_sentinel respectively, this seems reasonable since move_sentinel
is a sentinel adaptor introduced by C++20 specifically for comparison with move_iterator.
#include <list>
#include <ranges>
int main() {
std::list<std::tuple<int&>> l;
auto k = std::move(l) | std::views::keys;
auto s = std::ranges::subrange(std::cbegin(k), std::end(k));
(void) std::ranges::next(s.begin(), s.end()); // constant time
auto r = s | std::views::as_rvalue;
(void) std::ranges::next(r.begin(), r.end()); // linear time
}
The above subrange is constructed by the elements_view::iterator<true> and
elements_view::iterator<false> pair, and since the former can be assigned by the latter,
when we use ranges::next to increment the begin of s to its end, the
assignable_from branch will be executed, so we get a constant-time complexity.
views::as_rvalue to s, the as_rvalue_view::end will go
into the non-common branch and return move_sentinel. And because move_iterator cannot be
assigned by move_sentinel, ranges::next will successively increment the begin of s
until its end, we get the linear-time complexity this time.
I think it is more appropriate to return move_iterator for the above case, as this preserves the
assignability, but also preserves the iterator operability that the original sentinel type has.
Another benefit of doing this is that when the sentinel type of underlying range can be subtracted from its
iterator type but does not model sized_sentinel_for, returning different move_iterator makes
them still subtractable, because its operator- only constrain the x.base() - y.base() being
well-formed.
This also solves the issue of as_rvalue_view being a valid type but does not model a range in
some cases, for example:
#include <ranges>
int main() {
std::move_iterator<int*> i;
std::move_iterator<const int*> ci;
std::ranges::subrange s(i, ci);
std::ranges::as_rvalue_view r(s); // not failed
static_assert(std::ranges::range<decltype(r)>); // failed
}
This is because currently, as_rvalue_view does not explicitly specify the template parameters of the returned
move_iterator and move_sentinel, so based on CTAD, its begin will return
move_iterator(move_iterator(...)) which is still move_iterator<int*>, and its end will
return move_sentinel<move_iterator<const int*>>. Those two types are not comparable, so r
does not constitute a valid range.
move_iterator when the sentinel type of the underlying range models
input_iterator.
[2022-11-30; Reflector poll]
Set priority to 3 after reflector poll.
"NAD, these examples seem entirely contrived. If not NAD, don't need the
common_range check if we are checking that the sentinel models
input_iterator."
Proposed resolution:
This wording is relative to N4917.
Modify 25.7.7.2 [range.as.rvalue.view] as indicated:
namespace std::ranges {
template<view V>
requires input_range<V>
class as_rvalue_view : public view_interface<as_rvalue_view<V>> {
[…]
constexpr auto begin() requires (!simple-view<V>)
{ return move_iterator(ranges::begin(base_)); }
constexpr auto begin() const requires range<const V>
{ return move_iterator(ranges::begin(base_)); }
constexpr auto end() requires (!simple-view<V>) {
if constexpr (common_range<V> || input_iterator<sentinel_t<V>>) {
return move_iterator(ranges::end(base_));
} else {
return move_sentinel(ranges::end(base_));
}
}
constexpr auto end() const requires range<const V> {
if constexpr (common_range<const V> || input_iterator<sentinel_t<const V>>) {
return move_iterator(ranges::end(base_));
} else {
return move_sentinel(ranges::end(base_));
}
}
[…]
};
[…]
}
reverse_view should not cache when ranges::next has constant time complexitySection: 25.7.21.2 [range.reverse.view] Status: New Submitter: Hewill Kang Opened: 2022-11-14 Last modified: 2022-11-30
Priority: 3
View all other issues in [range.reverse.view].
View all issues with New status.
Discussion:
In order to ensure that begin has an amortized constant time, when the underlying range is not a
common range, reverse_view always caches the result of ranges::next.
begin to end still guarantees constant time,
for example:
#include <ranges>
#include <vector>
#include <list>
int main() {
std::vector v{42};
auto x = std::ranges::subrange(std::counted_iterator(v.begin(), 1), std::default_sentinel)
| std::views::reverse;
(void) x.begin(); // still caches end iterator in MSVC-STL
std::list l{42};
auto y = std::ranges::subrange(l.cbegin(), l.end())
| std::views::reverse;
(void) y.begin(); // still caches end iterator in both libstdc++ and MSVC-STL
}
In the above example, although neither subrange is a common range, applying ranges::next
to their iterator-sentinel pairs is still constant time, in this case, there's no need to introduce a cache for
reverse_view to store the results. We shouldn't pay for things we don't need to use.
[2022-11-30; Reflector poll]
Set priority to 3 after reflector poll.
"NAD as specified, the Remarks don't need to be precise, they already
allow caching to be omitted if not needed. But we could still enable const
overloads of begin for cases like these."
"NAD, no need to complicate constraints for such contrived examples. We don't care about random access, sized, non-common ranges like the first case. Any change here should be a paper covering all adaptors, not a piecemeal issue."
Proposed resolution:
This wording is relative to N4917.
Modify 25.7.21.2 [range.reverse.view] as indicated:
constexpr reverse_iterator<iterator_t<V>> begin();-2- Returns:
make_reverse_iterator(ranges::next(ranges::begin(base_), ranges::end(base_)))-3- Remarks: In order to provide the amortized constant time complexity required by the
rangeconcept, this function caches the result within thereverse_viewfor use on subsequent calls when bothassignable_from<I&, S>andrandom_access_iterator<I> && sized_sentinel_for<S, I>arefalse, whereIisiterator_t<V>andSissentinel_t<V>.
year is ambiguousSection: 30.12 [time.format], 30.13 [time.parse] Status: New Submitter: Matt Stephanson Opened: 2022-11-18 Last modified: 2025-10-17
Priority: 3
View other active issues in [time.format].
View all other issues in [time.format].
View all issues with New status.
Discussion:
An issue has been identified regarding the two-digit formatting of negative years according to Table [tab:time.format.spec] (30.12 [time.format]):
cout << format("{:%y} ", 1976y) // "76"
<< format("{:%y}", -1976y); // also "76"?
The relevant wording is
The last two decimal digits of the year. If the result is a single digit it is prefixed by
0. The modified command%Oyproduces the locale's alternative representation. The modified command%Eyproduces the locale's alternative representation of offset from%EC(year only).
MSVC STL treats the regular modified form symmetrically. Just as %Ey is the offset from
%EC, so %y is the offset from %C, which is itself "[t]he year divided by 100
using floored division." (emphasis added). Because -1976 is the 24th year of the -20th century,
the above code will print "76 24" using MSVC STL. However, many users expect, and
libc++
gives, a result based on the literal wording, "76 76".
%C and %y as the quotient and remainder
of floored division by 100.
Howard Hinnant, coauthor of the original 30.12 [time.format] wording in P0355 adds:
On the motivation for this design it is important to remember a few things:
POSIX
strftime/strptimedoesn't handle negative years in this department, so this is an opportunity for an extension in functionality.This is a formatting/parsing issue, as opposed to a computational issue. This means that human readability of the string syntax is the most important aspect. Computational simplicity takes a back seat (within reason).
%Ccan't be truncated division, otherwise the years [-99, -1] would map to the same century as the years [0, 99]. So floored division is a pretty easy and obvious solution.
%yis obvious for non-negative years: The last two decimal digits, ory % 100.This leaves how to represent negative years with
%y. I can think of 3 options:
Use the last two digits without negating: -1976 → 76.
Use the last two digits and negate it: -1976 → -76.
Use floored modulus arithmetic: -1976 → 24.
The algorithm to convert
I discounted solution 3 as not sufficiently obvious. If the output for -1976 was 23, the human reader wouldn't immediately know that this is off by 1. The reader is expecting the POSIX spec:%Cand%yinto a year is not important to the client because these are both strings, not integers. The client will do it withparse, not100*C + y.the last two digits of the year as a decimal number [00,99].
24 just doesn't cut it.
That leaves solution 1 or 2. I discounted solution 2 because having the negative in 2 places (the%Cand%y) seemed overly complicated and more error prone. The negative sign need only be in one place, and it has to be in%Cto prevent ambiguity. That leaves solution 1. I believe this is the solution for an extension of the POSIX spec to negative years with the property of least surprise to the client. The only surprise is in%C, not%y, and the surprise in%Cseems unavoidable.
[2022-11-30; Reflector poll]
Set priority to 3 after reflector poll.
A few votes for priority 2. Might need to go to LEWG.
Previous resolution [SUPERSEDED]:
This wording is relative to N4917.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A: This is Howard Hinnant's choice (3)
Modify 30.12 [time.format], Table [tab:time.format.spec] as indicated:
Table 102 — Meaning of conversion specifiers [tab:time.format.spec] Specifier Replacement […]%yThe last two decimal digits of the yearremainder after dividing the year by 100 using floored division.
If the result is a single digit it is prefixed by0.
The modified command%Oyproduces the locale's alternative representation. The
modified command%Eyproduces the locale's alternative representation of offset from
%EC(year only).[…]Modify 30.13 [time.parse], Table [tab:time.parse.spec] as indicated:
Table 103 — Meaning of parseflags [tab:time.parse.spec]Flag Parsed value […]%yThe last two decimal digits of the yearremainder after dividing the year by 100 using floored division.
If the century is not otherwise specified (e.g.
with%C), values in the range [69,99] are presumed to refer to the years 1969 to 1999,
and values in the range [00,68] are presumed to refer to the years 2000 to 2068. The
modified command%N yspecifies the maximum number of characters to read. If N is
not specified, the default is 2. Leading zeroes are permitted but not required. The
modified commands%Eyand%Oyinterpret the locale's alternative representation.[…]Option B: This is Howard Hinnant's choice (1)
Modify 30.12 [time.format], Table [tab:time.format.spec] as indicated:
Table 102 — Meaning of conversion specifiers [tab:time.format.spec] Specifier Replacement […]%yThe last two decimal digits of the year, regardless of the sign of the year.
If the result is a single digit it is prefixed by0.
The modified command%Oyproduces the locale's alternative representation. The
modified command%Eyproduces the locale's alternative representation of offset from
%EC(year only).
[Example ?:cout << format("{:%C %y}", -1976y);prints-20 76. — end example][…]Modify 30.13 [time.parse], Table [tab:time.parse.spec] as indicated:
Table 103 — Meaning of parseflags [tab:time.parse.spec]Flag Parsed value […]%yThe last two decimal digits of the year, regardless of the sign of the year.
If the century is not otherwise specified (e.g.
with%C), values in the range [69,99] are presumed to refer to the years 1969 to 1999,
and values in the range [00,68] are presumed to refer to the years 2000 to 2068. The
modified command%N yspecifies the maximum number of characters to read. If N is
not specified, the default is 2. Leading zeroes are permitted but not required. The
modified commands%Eyand%Oyinterpret the locale's alternative representation.
[Example ?:year y; istringstream{"-20 76"} >> parse("%3C %y", y);results in
y == -1976y. — end example][…]
[2025-10-17; Jonathan provides updated wording using Option B]
Proposed resolution:
This wording is relative to N5014.
Modify 30.12 [time.format], Table [tab:time.format.spec] as indicated:
Table 133 — Meaning of conversion specifiers [tab:time.format.spec] Specifier Replacement […]%yThe last two decimal digits of the year, regardless of the sign of the year.
If the result is a single digit it is prefixed by0.
The modified command%Oyproduces the locale's alternative representation. The
modified command%Eyproduces the locale's alternative representation of offset from
%EC(year only).
[Example ?:cout << format("{:%C %y}", -1976y);prints-20 76. — end example][…]
Modify 30.13 [time.parse], Table [tab:time.parse.spec] as indicated:
Table 103 — Meaning of parseflags [tab:time.parse.spec]Flag Parsed value […]%yThe last two decimal digits of the year, regardless of the sign of the year.
If the century is not otherwise specified (e.g.
with%C), values in the range [69,99] are presumed to refer to the years 1969 to 1999,
and values in the range [00,68] are presumed to refer to the years 2000 to 2068. The
modified command%N yspecifies the maximum number of characters to read. If N is
not specified, the default is 2. Leading zeroes are permitted but not required. The
modified commands%Eyand%Oyinterpret the locale's alternative representation.
[Example ?:year y; istringstream{"-20 76"} >> parse("%3C %y", y);results in
y == -1976y. — end example][…]
element_view::iterator in LWG 3798Section: 25.7.23.3 [range.elements.iterator] Status: New Submitter: Jiang An Opened: 2022-11-23 Last modified: 2024-07-21
Priority: 3
View other active issues in [range.elements.iterator].
View all other issues in [range.elements.iterator].
View all issues with New status.
Discussion:
In LWG 3798(i) (voted into WP in November 2022), iterator types of several range adaptors may have
forward_iterator_tag or stronger iterator tag types as their iterator_category type when
their operator* returns rvalue references. However, the proposed resolution missed the similar change
for element_view::iterator.
[2022-11-30; Reflector poll]
Set priority to 3 after reflector poll.
"The proposed resolution is incorrect - just because the get
expression is an xvalue doesn't mean operator* returns by reference."
Previous resolution [SUPERSEDED]:
This wording is relative to N4917.
Modify 25.7.23.3 [range.elements.iterator] as indicated:
-2- The member typedef-name
iterator_categoryis defined if and only ifBasemodelsforward_range. In that case,iterator_categoryis defined as follows: LetCdenote the typeiterator_traits<iterator_t<Base>>::iterator_category.
(2.1) — If
std::get<N>(*current_)is anprvalue,iterator_categorydenotesinput_iterator_tag.(2.2) — Otherwise, if
Cmodelsderived_from<random_access_iterator_tag>,iterator_categorydenotesrandom_access_iterator_tag.(2.3) — Otherwise,
iterator_categorydenotesC.
[2023-01-22; Jiang An comments and provides improved wording]
The old proposed resolution was incorrect. I think the correct criteria could be that determined from the return type of
get-element.
Previous resolution [SUPERSEDED]:
This wording is relative to N4928.
Modify 25.7.23.3 [range.elements.iterator] as indicated:
-2- The member typedef-name
iterator_categoryis defined if and only ifBasemodelsforward_range. In that case,iterator_categoryis defined as follows: LetCdenote the typeiterator_traits<iterator_t<Base>>::iterator_category.
(2.1) — If
range_reference_t<Base>is a reference type andstd::get<N>(*current_)is anprvalue or neitherrange_reference_t<Base>nortuple_element_t<N, range_reference_t<Base>is a reference type,iterator_categorydenotesinput_iterator_tag.(2.2) — Otherwise, if
Cmodelsderived_from<random_access_iterator_tag>,iterator_categorydenotesrandom_access_iterator_tag.(2.3) — Otherwise,
iterator_categorydenotesC.
[2024-07-08; Hewill Kang provides improved wording]
Proposed resolution:
This wording is relative to N4986.
Modify 25.7.23.3 [range.elements.iterator] as indicated:
-2- The member typedef-name
iterator_categoryis defined if and only ifBasemodelsforward_range. In that case,iterator_categoryis defined as follows: LetCdenote the typeiterator_traits<iterator_t<Base>>::iterator_category.
(2.1) — If
std::get<N>(*current_)is_reference_v<decltype(get-element(current_))>isan rvaluefalse,iterator_categorydenotesinput_iterator_tag.(2.2) — Otherwise, if
Cmodelsderived_from<random_access_iterator_tag>,iterator_categorydenotesrandom_access_iterator_tag.(2.3) — Otherwise,
iterator_categorydenotesC.
CharT in the regex librarySection: 28.6.1 [re.general] Status: New Submitter: Xie He Opened: 2022-11-28 Last modified: 2023-01-06
Priority: 4
View all issues with New status.
Discussion:
In 28.6.1 [re.general], the character type used in the regex library (CharT),
is only required to be "char-like". This means "struct A { int m; };" satisfies the
requirements for CharT. Clearly there have to be more requirements for CharT.
Comparison operators such as "==" or "<=" must be defined between CharT
objects, because these operators are used at various places of 28.6 [re], including
28.6.2 [re.req] paragraph 14 and 16, 28.6.6 [re.traits] paragraph 12, and
28.6.12 [re.grammar] paragraph 14. Similarly, "==" between a CharT object
and integer 0 must also be defined, as it is used at 28.6.2 [re.req] paragraph 11.
std::char_traits<CharT>" comparison functions. This interpretation has the advantage
of keeping 28.6 [re] consistent with the strings library and string comparisons, which are
also used in 28.6 [re], including 28.6.2 [re.req] paragraph 7, 18, 20, and
28.6.12 [re.grammar] paragraph 14.2. Also, only with this interpretation, can
28.6.6 [re.traits] paragraph 3 be consistent with 28.6.2 [re.req] paragraph 11.There must be a way to convert between CharT and char, otherwise there is no
way to recognize regex syntactical characters, such as '*' or '+'. One way is to simply
do a type conversion from char to CharT (which requires char to be convertible
to CharT). But this doesn't allow us to convert between character encodings.
Traits class (28.6.12 [re.grammar] paragraph 2)
denoted v, we can use "use_facet<ctype<CharT>>(v.getloc()).widen" to convert
from char to CharT. This allows us to convert between character encodings. This requires
that we can actually get this facet from this locale object.
[2023-01-06; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
std::erase_if overloads for non-associative containers should move (and
not copy) their predicate objectSection: 27.4.4.5 [string.erasure], 23.3.5.5 [deque.erasure], 23.3.7.7 [forward.list.erasure], 23.3.11.6 [list.erasure], 23.3.13.6 [vector.erasure] Status: New Submitter: Giuseppe D'Angelo Opened: 2022-12-04 Last modified: 2023-01-06
Priority: 3
View all issues with New status.
Discussion:
Consistent uniform erasure added several overloads of std::erase_if.
All these overloads have fully specified effects, and are either implemented
through the erase/remove_if idiom (for string, vector and deque),
through calls to the container's own remove_if non-static member function
(for list and forward_list), or through a hand-rolled loop (for
the associative containers — map, set,
and their unordered and multi variants).
std::erase_if is always copied into the inner call
to std::remove_if or the container's remove_if (see
27.4.4.5 [string.erasure], 23.3.13.6 [vector.erasure],
23.3.11.6 [list.erasure], 23.3.7.7 [forward.list.erasure], and
23.3.5.5 [deque.erasure]).
Now, algorithms are generally allowed to take as many copies of
predicates as they want — this is 26.2 [algorithms.requirements]/9,
but cf. LWG 3049(i).
However it still feels strange/sloppy that a copy here is mandated by the Standard.
An implementation that would otherwise make no copies of a predicate in an algorithm
must make a copy in the erase_if overloads.
The copy of the predicate could be instead replaced by a move without
any change in functionality; after being passed to the underlying
algorithm, the predicate object is never used again by erase_if.
I am therefore proposing to add a std::move call for the predicate object.
One could argue that erase_if should be re-specified so that it is not
necessarily implemented in terms of underlying calls to other
facilities, and could therefore even remove the need of a move on a
high-quality implementation.
This is a design change, and so I consider it out of scope for a library issue.
Of course, moving instead of copying is a detectable change, and
"pathological" predicate types that are copyable but have disabled moves
will get broken, but I don't think those deserve to be supported.
[2023-01-06; Reflector poll]
Set priority to 3 after reflector poll.
"An alternative resolution would be to wrap the predicate in reference_wrapper."
"I'd prefer blanket wording that says the actual number of copies and/or moves is unspecified when the wording depicts a copy of a function object."
Proposed resolution:
This wording is relative to N4917.
Modify 27.4.4.5 [string.erasure] as indicated:
template<class charT, class traits, class Allocator, class Predicate> constexpr typename basic_string<charT, traits, Allocator>::size_type erase_if(basic_string<charT, traits, Allocator>& c, Predicate pred);-2- Effects: Equivalent to:
auto it = remove_if(c.begin(), c.end(), std::move(pred)); auto r = distance(it, c.end()); c.erase(it, c.end()); return r;
Modify 23.3.5.5 [deque.erasure] as indicated:
template<class T, class Allocator, class Predicate> typename deque<T, Allocator>::size_type erase_if(deque<T, Allocator>& c, Predicate pred);-2- Effects: Equivalent to:
auto it = remove_if(c.begin(), c.end(), std::move(pred)); auto r = distance(it, c.end()); c.erase(it, c.end()); return r;
Modify 23.3.7.7 [forward.list.erasure] as indicated:
template<class T, class Allocator, class Predicate> typename forward_list<T, Allocator>::size_type erase_if(forward_list<T, Allocator>& c, Predicate pred);-2- Effects: Equivalent to:
return c.remove_if(std::move(pred));
Modify 23.3.11.6 [list.erasure] as indicated:
template<class T, class Allocator, class Predicate> typename list<T, Allocator>::size_type erase_if(list<T, Allocator>& c, Predicate pred);-2- Effects: Equivalent to:
return c.remove_if(std::move(pred));
Modify 23.3.13.6 [vector.erasure] as indicated:
template<class T, class Allocator, class Predicate> constexpr typename vector<T, Allocator>::size_type erase_if(vector<T, Allocator>& c, Predicate pred);-2- Effects: Equivalent to:
auto it = remove_if(c.begin(), c.end(), std::move(pred)); auto r = distance(it, c.end()); c.erase(it, c.end()); return r;
incrementable_traits is under-constrainedSection: 24.3.2.1 [incrementable.traits] Status: New Submitter: Hewill Kang Opened: 2022-12-07 Last modified: 2023-01-06
Priority: 3
View other active issues in [incrementable.traits].
View all other issues in [incrementable.traits].
View all issues with New status.
Discussion:
The last specialization of incremental_traits requires that the result obtained
by subtracting two objects of type const T must model integral,
then apply make_signed_t to it as the difference type of type T.
bool also models integral, but is not a valid template argument
for make_signed_t, we should ban such cases to avoid unnecessary hard errors
(online example):
#include <ranges>
struct Bool {
bool operator-(Bool) const;
};
template<class T>
concept can_iota_view = requires(T t) { std::ranges::iota_view(t); };
static_assert(!can_iota_view<Bool>); // hard error
[2022-12-13; Minor wording improvements after LWG reflector discussion]
Remove remove_cv_t within nonbool-integral, because bool prvalues cannot be cv-qualified.
[2023-01-06; Reflector poll]
Set priority to 3 after reflector poll.
"I would prefer to place the new checks directly in the requires-clause
instead of introducing nonbool-integral."
Proposed resolution:
This wording is relative to N4917.
Modify 24.3.2.1 [incrementable.traits] as indicated:
namespace std {
template<class T>
concept nonbool-integral = integral<T> && !same_as<T, bool>; // exposition only
template<class T> struct incrementable_traits { };
[…]
template<class T>
requires (!requires { typename T::difference_type; } &&
requires(const T& a, const T& b) { { a - b } -> nonbool-integralintegral; })
struct incrementable_traits<T> {
using difference_type = make_signed_t<decltype(declval<T>() - declval<T>())>;
};
[…]
}
filesystem::u8path should be undeprecatedSection: D.22.1 [depr.fs.path.factory] Status: Open Submitter: Daniel Krügler Opened: 2022-12-10 Last modified: 2024-01-29
Priority: 3
View all other issues in [depr.fs.path.factory].
View all issues with Open status.
Discussion:
The filesystem::u8path function became deprecated with the adoption of
P0482R6, but the rationale for that change is rather thin:
"The C++ standard must improve support for UTF-8 by removing the existing barriers that result in redundant tagging of character encodings, non-generic UTF-8 specific workarounds like
u8path."
The u8path function is still useful if my original string source is a char
sequence and I do know that the encoding of this sequence is UTF-8.
std::u8string instead, which costs me
an additional transformation and doesn't work without reinterpret_cast.
Even in the presence of char8_t, legacy code bases often are still ABI-bound to char.
In the future we may solve this problem using the tools provided by P2626 instead,
but right now this is not part of the standard and it wasn't at the time when u8path became
deprecated.
This is in my opinion a good reason to undeprecate u8path now and decide later on the
appropriate time to deprecate it again (if it really turns out to be obsolete by alternative
functionality).
Billy O'Neal provides a concrete example where the current deprecation status causes pain:
Example: vcpkg-tool files.cpp#L21-L45
Before p0482, we could just callstd::u8pathand it would do the right thing on both POSIX and Windows. After compilers started implementing '20, we have to make assumptions about the correct 'internal'std::pathencoding because there is no longer a way to arrive tostd::pathwith acharbuffer that we know is UTF-8 encoded and get the correct results. It's one of the reasons we completely ripped out use ofstd::filesystemon most platforms from vcpkg, so you won't see this in current sources.
[2023-01-06; Reflector poll]
Set priority to 3 after reflector poll. Set status to LEWG.
[2023-05-30; status to "Open"]
LEWG discussed this in January and had no consensus for undeprecation.
Proposed resolution:
This wording is relative to N4917.
Restore the u8path declarations to 31.12.4 [fs.filesystem.syn], header
<filesystem> synopsis, as indicated:
namespace std::filesystem {
// 31.12.6 [fs.class.path], paths
class path;
// 31.12.6.8 [fs.path.nonmember], path non-member functions
void swap(path& lhs, path& rhs) noexcept;
size_t hash_value(const path& p) noexcept;
// [fs.path.factory], path factory functions
template<class Source>
path u8path(const Source& source);
template<class InputIterator>
path u8path(InputIterator first, InputIterator last);
// 31.12.7 [fs.class.filesystem.error], filesystem errors
class filesystem_error;
[…]
}
Restore the previous sub-clause [fs.path.factory] by copying the contents of D.22.1 [depr.fs.path.factory] to a new sub-clause [fs.path.factory] between 31.12.6.8 [fs.path.nonmember] and 31.12.6.10 [fs.path.hash] and without Note 1 as indicated:
[Drafting note: As additional stylistic adaption we replace the obsolete Requires element by a Preconditions element plus a Mandates element (similar to that of 31.12.6.5.1 [fs.path.construct] p5).
As a second stylistic improvement we convert the now more unusual "if […]; otherwise" construction in bullets by "Otherwise, if […]" constructions.]
? Factory functions [fs.path.factory]
template<class Source> path u8path(const Source& source); template<class InputIterator> path u8path(InputIterator first, InputIterator last);-?- Mandates: The value type of
-?- Preconditions: TheSourceandInputIteratorischarorchar8_t.sourceand[first, last)sequences are UTF-8 encoded. -?- Returns:
(?.1) — If
value_typeischarand the current native narrow encoding (31.12.6.3.2 [fs.path.type.cvt]) is UTF-8, returnpath(source)orpath(first, last).(?.2) — Otherwise, if
value_typeiswchar_tand the native wide encoding is UTF-16, or ifvalue_typeischar16_torchar32_t, convertsourceor[first, last)to a temporary,tmp, of typestring_typeand returnpath(tmp).(?.3) — Otherwise, convert
sourceor[first, last)to a temporary,tmp, of typeu32stringand returnpath(tmp).-?- Remarks: Argument format conversion (31.12.6.3.1 [fs.path.fmt.cvt]) applies to the arguments for these functions. How Unicode encoding conversions are performed is unspecified.
-?- [Example 1: A string is to be read from a database that is encoded in UTF-8, and used to create a directory using the native encoding for filenames:namespace fs = std::filesystem; std::string utf8_string = read_utf8_data(); fs::create_directory(fs::u8path(utf8_string));For POSIX-based operating systems with the native narrow encoding set to UTF-8, no encoding or type conversion occurs.
For POSIX-based operating systems with the native narrow encoding not set to UTF-8, a conversion to UTF-32 occurs, followed by a conversion to the current native narrow encoding. Some Unicode characters may have no native character set representation. For Windows-based operating systems a conversion from UTF-8 to UTF-16 occurs. — end example]
Delete sub-clause D.22.1 [depr.fs.path.factory] in its entirety.
Section: 30.12 [time.format] Status: Open Submitter: Jonathan Wakely Opened: 2022-12-20 Last modified: 2023-02-10
Priority: 3
View other active issues in [time.format].
View all other issues in [time.format].
View all issues with Open status.
Discussion:
The wording of 30.12 [time.format] p4 seems to have some unwanted consequences. It says:
The result of formatting astd::chrono::durationinstance holding a negative value, or anhh_mm_ssobjecthfor whichh.is_negative()istrue, is equivalent to the output of the corresponding positive value, with aSTATICALLY-WIDEN<charT>("-")character sequence placed before the replacement of the initial conversion specifier.
Taken literally, I think that means:
format("{:%q}", -1s) == "-s"
format("{:%t%j}", -25h) == "-\t1"
format("{:%p%I}", -11h) == "-am11"
The last one probably doesn't matter (what does -11am mean anyway?)
but some of them do matter, for example something like
"{:(%q)%t%Q}" intends to put the unit suffix first in parens
but will print "(-s)\t1" which probably isn't what the user wanted.
I think we want to place the sign before the first numeric
conversion specifier, not "the initial conversion specifier".
That is what Howard's date::format and <fmt> both do.
[Issaquah 2023-02-10; LWG issue processing]
Set priority to 3. Proposed a hybrid resolution.
Previous resolution [SUPERSEDED]:
Two alternatives are presented, choose one of Option A or Option B.
Modify 30.12 [time.format] as indicated:
Option A
-4- The result of formatting a
instance holding a negative value, or astd::chrono::durationnchrono::hh_mm_ssobjecthfor whichh.is_negative()istrue, is equivalent to the output of the corresponding positive value, with aSTATICALLY-WIDEN<charT>("-")character sequence placed before the replacement of theinitialfirst conversion specifier that is not one of %n, %p, %q, %t, or %%.Option B
-4- Among the specifiers, %H, %I, %M, %S, and %T, the
Theresult of formatting ainstance holding a negative value, or astd::chrono::durationnchrono::hh_mm_ssobjecthfor whichh.is_negative()istrue, is equivalent to the output of the corresponding positive value, with aSTATICALLY-WIDEN<charT>("-")character sequence placed before the replacement of the initial conversion specifier.
Proposed resolution:
This wording is relative to N4928.
Modify 30.12 [time.format] as indicated:
-4- The result of formatting a
instance holding a negative value, or astd::chrono::durationnchrono::hh_mm_ssobjecthfor whichh.is_negative()istrue, is equivalent to the output of the corresponding positive value, with aSTATICALLY-WIDEN<charT>("-")character sequence placed before the replacement of theinitialfirst conversion specifier that is one of %H, %I, %M, %S, or %T.
ranges::to's from_range_t tag branch has the wrong constraintSection: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: Hewill Kang Opened: 2023-01-06 Last modified: 2024-01-29
Priority: 4
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with New status.
Discussion:
In bullet (1.1.2), ranges::to checks whether the container type
C models constructible_from<from_range_t, ...>
and constructs it via C(from_range, ...).
Since from_range is a constexpr variable here,
it would be more accurate to constrain C to be constructible from
a const lvalue tag rather than an rvalue tag.
[2023-02-02; Reflector poll]
Set priority to 4 after reflector poll.
Several votes for "Tentatively Ready", but also two objections, preferring NAD.
The proposed change would appear to bless unconventional uses of
from_range, and we don't want to support or encourage that.
The current wording is simpler, and works for the intended cases.
Proposed resolution:
This wording is relative to N4917.
Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);-1- Returns: An object of type
Cconstructed from the elements ofrin the following manner:
(1.1) — If
convertible_to<range_reference_t<R>, range_value_t<C>>istrue:
(1.1.1) — If
constructible_from<C, R, Args...>istrue:C(std::forward<R>(r), std::forward<Args>(args)...)(1.1.2) — Otherwise, if
constructible_from<C, const from_range_t&, R, Args...>istrue:C(from_range, std::forward<R>(r), std::forward<Args>(args)...)- […]
iota_view::iterator::operator- is overconstrainedSection: 25.6.4.3 [range.iota.iterator] Status: New Submitter: Hewill Kang Opened: 2023-01-06 Last modified: 2023-02-01
Priority: 3
View other active issues in [range.iota.iterator].
View all other issues in [range.iota.iterator].
View all issues with New status.
Discussion:
Currently, two iota_view::iterators can be subtracted
only when the underlying W type models advanceable,
where advanceable consists of a series of syntactic and semantic
requirements similar to the random_access_iterator concept.
However, when W is an C++20 iterator type, whether it provides
subtraction is irrelevant to its iterator category.
In such cases, still requiring W to support a series of random access
iterator-like operations seems too restrictive. Consider:
#include <list>
#include <ranges>
int main() {
std::list l{1, 2, 3, 4, 5};
auto it = std::counted_iterator(l.begin(), l.size());
auto r = std::views::iota(it, std::next(it, 3));
auto sz = r.size(); // 3 as expected
auto d = r.end() - r.begin(); // error: no match for 'operator-'
}
We can get the correct size of iota_view by subtracting two
counted_iterators,
but we cannot subtract two iota_view::iterators to get their
difference, even though the underlying counted_iterator already models
sized_sentinel_for for itself, which is not satisfactory.
I think we should relax the constraints of
iota_view::iterator::operator- to allow the above case,
which also makes it compatible with iota_view::sentinel::operator-.
[2023-02-01; Reflector poll]
Set priority to 3 after reflector poll.
Several P0 votes, but an objection to P0 on the basis that we don't
define what it means to use sized_sentinel_for on non-iterators.
Others responded that we don't need to, as we only use it with iterators,
and do not intend it to be usable with anything else.
Proposed resolution:
This wording is relative to N4917.
Modify 25.6.4.3 [range.iota.iterator] as indicated:
[…]namespace std::ranges { template<weakly_incrementable W, semiregular Bound> requires weakly-equality-comparable-with<W, Bound> && copyable<W> struct iota_view<W, Bound>::iterator { private: W value_ = W(); // exposition only public: […] friend constexpr iterator operator-(iterator i, difference_type n) requires advanceable<W>; friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires advanceable<W> || sized_sentinel_for<W, W>; }; }friend constexpr difference_type operator-(const iterator& x, const iterator& y) requires advanceable<W> || sized_sentinel_for<W, W>;-23- Effects: Equivalent to:
using D = difference_type; if constexpr (is-integer-like<W>) { if constexpr (is-signed-integer-like<W>) return D(D(x.value_) - D(y.value_)); else return (y.value_ > x.value_) ? D(-D(y.value_ - x.value_)) : D(x.value_ - y.value_); } else { return x.value_ - y.value_; }
join_with_view::iterator's iter_move and iter_swap should be conditionally noexceptSection: 25.7.15.3 [range.join.with.iterator] Status: New Submitter: Hewill Kang Opened: 2023-01-06 Last modified: 2024-01-29
Priority: 3
View other active issues in [range.join.with.iterator].
View all other issues in [range.join.with.iterator].
View all issues with New status.
Discussion:
In order to preserve room for optimization, the standard always tries to
propagate the noexcept specification of custom
iter_move/iter_swap for different iterators.
But for join_with_view::iterator,
these two specializations are the only ones in the standard that do not have
a noexcept specification.
This is because both invoke visit in the function body,
and visit may throw an exception when the variant
does not hold a value.
However, implementors are not required to follow the standard practice.
Since the join_with_view::iterator's variant member
only contains two alternative types, both libstdc++ and MSVC-STL avoid
heavyweight visit calls by simply using multiple if statements.
This means that it is still possible to add a conditional noexcept
specification to these overloads, and there is already a precedent in the
standard, namely common_iterator.
All we need to do is add a Preconditions.
[2023-02-01; Reflector poll]
Set priority to 3 after reflector poll. "The iter_swap specification is wrong since we can swap Pattern and Inner. And this is something implementations can strengthen."
Proposed resolution:
This wording is relative to N4917.
Modify 25.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-ranges<range_reference_t<V>, Pattern> template<bool Const> class join_with_view<V, Pattern>::iterator { […] Parent* parent_ = nullptr; // exposition only OuterIter outer_it_ = OuterIter(); // exposition only variant<PatternIter, InnerIter> inner_it_; // exposition only […] public: […] friend constexpr decltype(auto) iter_move(const iterator& x) noexcept(see below);{ using rvalue_reference = common_reference_t< iter_rvalue_reference_t<InnerIter>, iter_rvalue_reference_t<PatternIter>>; return visit<rvalue_reference>(ranges::iter_move, x.inner_it_); }friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below) requires indirectly_swappable<InnerIter, PatternIter>;{ visit(ranges::iter_swap, x.inner_it_, y.inner_it_); }}; }friend constexpr decltype(auto) iter_move(const iterator& x) noexcept(see below);-?- Let
rvalue_referencebe:common_reference_t<iter_rvalue_reference_t<InnerIter>, iter_rvalue_reference_t<PatternIter>>-?- Preconditions:
x.inner_it_.valueless_by_exception()isfalse.-?- Effects: Equivalent to:
return visit<rvalue_reference>(ranges::iter_move, x.inner_it_);-?- Remarks: The exception specification is equivalent to:
noexcept(ranges::iter_move(declval<const InnerIter&>())) && noexcept(ranges::iter_move(declval<const PatternIter&>())) && is_nothrow_convertible_v<iter_rvalue_reference_t<InnerIter>, rvalue_reference> && is_nothrow_convertible_v<iter_rvalue_reference_t<PatternIter>, rvalue_reference>friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below) requires indirectly_swappable<InnerIter, PatternIter>;-?- Preconditions:
x.inner_it_.valueless_by_exception()andy.inner_it_.valueless_by_exception()are eachfalse.-?- Effects: Equivalent to:
visit(ranges::iter_swap, x.inner_it_, y.inner_it_).-?- Remarks: The exception specification is equivalent to:
noexcept(ranges::iter_swap(declval<const InnerIter&>(), declval<const InnerIter&>())) && noexcept(ranges::iter_swap(declval<const PatternIter&>(), declval<const PatternIter&>()))
Section: 16.4.6.14 [res.on.exception.handling] Status: New Submitter: Jiang An Opened: 2023-01-07 Last modified: 2023-02-01
Priority: 3
View other active issues in [res.on.exception.handling].
View all other issues in [res.on.exception.handling].
View all issues with New status.
Discussion:
Some standard library types, such as std::pair, std::tuple, and std::array,
are currently allowed to have a member or element type with a destructor that is noexcept(false).
In order to conform to 16.4.6.14 [res.on.exception.handling]/3, these types can't always have
implicitly declared destructors because the implicit exception specification may be noexcept(false).
[2023-01-29; Daniel comments]
This issue has very much overlap with LWG 3229(i).
[2023-02-01; Reflector poll]
Set priority to 3 after reflector poll.
Unclear who the "shall" imposes requirements on. Maybe split into constraint
on the library to not put noexcept(false) on destructors, and
constraint on users that the library can assume destructors don't throw.
Should also make it clear which parts of the subclause are normative and
which are not.
Proposed resolution:
tiny-range is not quite rightSection: 25.7.16.2 [range.lazy.split.view] Status: New Submitter: Hewill Kang Opened: 2023-01-07 Last modified: 2025-04-28
Priority: 4
View other active issues in [range.lazy.split.view].
View all other issues in [range.lazy.split.view].
View all issues with New status.
Discussion:
Currently, lazy_split_view supports input range when the element of the pattern is less than or equal to 1.
In order to ensure this condition at compile time, tiny-range constrains the type R to model
sized_range and requires (remove_reference_t<R>::size() <= 1) to be a constant expression.
sized_range does not guarantee that ranges::size will be evaluated by R::size().
For example, when disable_sized_range<R> is specialized to true or R::size() returns a non-integer-like type,
ranges::size can still compute the size by subtracting the iterator-sentinel pair when both satisfy sized_sentinel_for.
Since the lazy_split_view's iterator uses R::size() to get the constant value of the pattern,
we must ensure that this is indeed how ranges::size is calculated. Also, I think we can simplify
tiny-range with bool_constant in a way similar to LWG 3150(i), which removes the
introduction of require-constant.
[2023-02-01; Reflector poll]
Set priority to 4 after reflector poll.
Only matters for pathological types.
Maybe use requires bool_constant<ranges::size(r) <= 1>.
Previous resolution [SUPERSEDED]:
This wording is relative to N4917.
Modify 25.7.16.2 [range.lazy.split.view] as indicated:
namespace std::ranges {template<auto> struct require-constant; // exposition onlytemplate<class R> concept tiny-range = // exposition only sized_range<R> && requires { typename require-constant<remove_reference_t<R>::size()>; } && (remove_reference_t<R>::size() <= 1);template<input_range V, forward_range Pattern> requires view<V> && view<Pattern> && indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && (forward_range<V> || tiny-range<Pattern>) class lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> { […] }; […] }template<class R> concept tiny-range = // exposition only sized_range<R> && requires { requires bool_constant<(remove_reference_t<R>::size() <= 1)>::value; };-?- Given an lvalue
rof typeremove_reference_t<R>,Rmodelstiny-rangeonly ifranges::size(r)is evaluated byremove_reference_t<R>::size().constexpr lazy_split_view(V base, Pattern pattern);-1- Effects: : Initializes
base_withstd::move(base), andpattern_withstd::move(pattern).
[2025-04-27, Hewill provides alternative wording]
Proposed resolution:
This wording is relative to N5008.
Modify 25.7.16.2 [range.lazy.split.view] as indicated:
[Drafting note: This benefits from P2280 that a call to a member function of a non-constexpr object can be a constant expression if it does not actually access the member.
This would makeviews::lazy_split(r, span<int, 0>{})well-formed, which can be seen as an enhancement.]
namespace std::ranges {
template<auto> struct require-constant; // exposition only
template<class R>
concept tiny-range = // exposition only
sized_range<R> &&
requires (R& r) { requires bool_constant<ranges::size(r) <= 1>::value; }
requires { typename require-constant<remove_reference_t<R>::size()>; } &&
(remove_reference_t<R>::size() <= 1);
template<input_range V, forward_range Pattern>
requires view<V> && view<Pattern> &&
indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
(forward_range<V> || tiny-range<Pattern>)
class lazy_split_view::view_interface<lazy_split_view<V, Pattern>> {
[…]
};
[…]
}
Modify 25.7.16.5 [range.lazy.split.inner] as indicated:
[Drafting note: We can't use
if constexpr (ranges::size(i_.parent_->pattern_) == 0)here because it is not a constant expression, and it seems more intuitive to just useranges::emptycombined with runtimeifwhich is always well-formed. Note that the PR does not seek the aggressive optimization that minimizes the instantiation as this is not the intent of the current design (for example,outer-iterator& operator++()can be specialized for the case wherePattern::size() == 0to save some O(1) comparisons), library implementations are free to optimize as it pleases.]
constexpr inner-iterator& operator++();-5- Effects: Equivalent to:
incremented_ = true; if constexpr (!forward_range<Base>) { if (ranges::empty(i_.parent_->pattern_))if constexpr (Pattern::size() == 0) {return *this; } } ++i_.current; return *this;
Section: 30.12 [time.format] Status: New Submitter: Tam S. B. Opened: 2023-01-14 Last modified: 2023-05-30
Priority: 3
View other active issues in [time.format].
View all other issues in [time.format].
View all issues with New status.
Discussion:
30.12 [time.format]/3:
If the formatted object does not contain the information the conversion
specifier refers to, an exception of type format_error is thrown.
30.12 [time.format]/6:
If the type being formatted does not contain the information that the format
flag needs, an exception of type format_error is thrown.
It's unclear how to determine if a type contain the needed information, and implementations diverge.
For example, consider
#include <chrono>
#include <format>
auto f(std::chrono::month_day_last mdl) {
return std::format("{:%j}", mdl);
}
Both libstdc++ and libc++ produce a compile-time error, claiming that the
argument does not contain the information, while MSVC STL throws
format_error at run time unless mdl is January/last,
in which case the function returns "031".
Another interesting case is format("{:%d}", mdl) where the value
can be printed for all months except February, which requires a year
to know how many days it has.
A related example from Jonathan Wakely:
std::chrono::weekday_indexed wdi(Monday, 7); // 7th Monday in the month
assert( ! wdi.ok() );
assert( wdi.weekday().ok() );
std::format("{:%a}", wdi);
For %a the required information is "a valid weekday",
and arguably this does contain a valid weekday.
On the other hand, there's no 7th Monday, so this isn't valid.
Should this throw or not?
This was discussed by LWG and Howard Hinnant summarized the intended behaviour as:
"The intention of 30.12 [time.format]/6 is to address things like formatting adurationwith%F. Adurationdoesn't contain the calendrical information that%Frequires (year, month, day). Ditto for using%a(weekday name) with ayear. It is meant to address mismatching types and flags, and not meant to address values."
The type chrono::weekday does contain the information needed to print
a weekday. A specific invalid value doesn't change that.
The type chrono::month_day_last does not contain the information
needed to print the day of the year.
A specific value where the day can be known doesn't change that.
The day of month is more interesting, and might need more discussion.
Jonathan proposed adding more examples to clarify the intention that only the
type matters, and not the value. There is some redundancy between p3 and p6.
Referring to "the formatted object" in p3 seems unclear.
Saying "type" as in p6 is better.
But p6 refers to "format flag" which is not defined,
whereas p3 uses "conversion specifier" (defined at the start of that paragraph).
The two uses of "flag" in p6 look like remnants from the earlier chrono::format
feature that was replaced by integration with std::format.
[2023-02-01; Reflector poll]
Set priority to 3 after reflector poll.
[2023-05-30; Jonathan adds wording]
Proposed resolution:
This wording is relative to N4950.
Modify 30.12 [time.format] as indicated:
-3- Each conversion specifier conversion-spec is replaced by appropriate characters as described in Table 101 ([tab:time.format.spec]); the formats specified in ISO 8601:2004 shall be used where so described. Some of the conversion specifiers depend on the formatting locale. If the string literal encoding is a Unicode encoding form and the locale is among an implementation-defined set of locales, each replacement that depends on the locale is performed as if the replacement character sequence is converted to the string literal encoding. If the
formatted objecttype being formatted does not contain the information the conversion specifier refers to, an exception of typeformat_erroris thrown.[Example ?: A
durationdoes not contain enough information to format as a weekday using%w. Aweekday_indexeddoes contain enough information to format using%wandMonday[7]can be formatted as"1"even thoughMonday[7].ok()isfalse. Amonth_daydoes not contain enough information to format as the day of the year using%j, even when themonth()part isJanuary. — end example]However, if a flag refers to a "time of day" (e.g.,
%H,%I,%p, etc.), then a specialization ofdurationis interpreted as the time of day elapsed since midnight.-4- The result of formatting a
std::chrono::durationinstance holding a negative value, or anhh_mm_ssobjecthfor whichh.is_negative()istrue, is equivalent to the output of the corresponding positive value, with aSTATICALLY-WIDEN<charT>("-")character sequence placed before the replacement of the initial conversion specifier.[Example 1:
— end example]cout << format("{:%T}", -10'000s); // prints: -02:46:40 cout << format("{:%H:%M:%S}", -10'000s); // prints: -02:46:40 cout << format("minutes {:%M, hours %H, seconds %S}", -10'000s); // prints: minutes -46, hours 02, seconds 40-5- Unless explicitly requested, the result of formatting a chrono type does not contain time zone abbreviation and time zone offset information. If the information is available, the conversion specifiers
%Zand%zwill format this information (respectively).[Note 1: If the information is not available and a
%Zor%zconversion specifier appears in the chrono-format-spec, an exception of typeformat_erroris thrown, as described above. — end note]
-6- If the type being formatted does not contain the information that the format flag needs, an exception of typeformat_erroris thrown, as described above.
[Example 2: Adurationdoes not contain enough information to format as aweekday. — end example]
However, if a flag refers to a "time of day" (e.g.,%H,%I,%p, etc.), then a specialization ofdurationis interpreted as the time of day elapsed since midnight.
input_iterator guaranteed to have iter_const_reference_t?Section: 24.5.3.2 [const.iterators.alias] Status: New Submitter: Hewill Kang Opened: 2023-01-26 Last modified: 2023-02-06
Priority: 2
View all issues with New status.
Discussion:
In the C++20 iterator system, input_iterator is guaranteed to have a common reference, which
reflects the indirectly_readable requires
common_reference_t<iter_reference_t<I>&&, iter_value_t<I>&> to be a valid type.
iter_const_reference_t which with a similar form:
template<indirectly_readable It>
using iter_const_reference_t =
common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>>;
it is still theoretically possible to create an input_iterator that does not have a valid
iter_const_reference_t, for example:
#include <iterator>
struct ref {
ref(int&);
};
struct rref {
rref(const int&);
rref(const ref&);
};
struct I {
using value_type = int;
using difference_type = std::ptrdiff_t;
ref operator*() const;
I& operator++();
I operator++(int);
friend rref iter_move(const I&);
};
static_assert(std::input_iterator<I>); // pass
using CR = std::iter_const_reference_t<I>; // error: no type named 'type' in 'struct std::common_reference<const int&&, ref>'
which causes basic_const_iterator<I> to produce a hard error internally when it is instantiated.
[2023-02-06; Reflector poll]
Set priority to 2 after reflector poll. Seems contrived, should probably constrain not give a hard error. The question of whether each range needs to have const reference to elems is important.
Proposed resolution:
zip over range of reference to an abstract typeSection: 25.7.25 [range.zip] Status: New Submitter: Barry Revzin Opened: 2023-01-28 Last modified: 2023-02-06
Priority: 4
View all issues with New status.
Discussion:
Consider:
#include <ranges>
struct Abstract {
virtual ~Abstract() = default;
virtual int f() = 0;
};
struct Concrete : Abstract {
int f() override { return 42; }
};
int main() {
Concrete c[10];
auto xformed = c | std::views::transform([](Concrete& c) -> Abstract& {
return c;
});
for (Abstract& a : xformed) { } // ok
auto zipped = std::views::zip(xformed);
for (auto&& [a] : zipped) { } // error
}
Here, xformed is a range whose reference type is Abstract& and whose value_type is Abstract.
Even though you can't actually create a value of that value_type, that's okay here, because no code is actually trying to do so.
zipped is a range whose reference type is std::tuple<Abstract&> and whose
value_type is std::tuple<Abstract>. No code here is actually trying to construct a value_type either,
but this code fails because simply instantiating std::tuple<Abstract> is an error. There's no other possible
value_type for zipped to have, std::tuple<Abstract> is correct — it's just that it happens
to be an ill-formed type in this context. There are workarounds for this case — you would have to make xformed
be a range of Abstract* or, probably better, a range of reference_wrapper<Abstract> instead.
This is unfortunate because many (most?) algorithms don't actually make any use of a range's value_type. The ones that do
(like ranges::min) obviously could not work, but currently we end up rejecting all uses. Probably the only possible way to
make this work is to allow value_type to be void (or absent), but it is currently a fairly fundamental type due
to its use in indirectly_readable to identify input iterators.
[2023-02-06; Reflector poll]
Set priority to 4 after reflector poll.
Several votes for NAD. Maybe tuple<Abstract> should be
explicitly made ill-formed (currently seems underspecified) or should be
"disabled" like invalid hash specializations and formatters.
Proposed resolution:
output_iteratorSection: 26.7.5 [alg.replace], 26.7.6 [alg.fill] Status: LEWG Submitter: Hewill Kang Opened: 2023-01-29 Last modified: 2024-06-28
Priority: 4
View all other issues in [alg.replace].
View all issues with LEWG status.
Discussion:
In C++20, output_iterator is required to support *o++ = t mainly for backward compatibility,
that is to say, in addition to avoiding needless breakage, this semantic is actually not very useful, as it can
be equivalently replaced by *o = t; ++o;.
output_iterator, there is no code of the form *o++ = t in practice,
and the latter of a more generic form is used instead.
It seems to me that constrained algorithms should never require output_iterator, since there really isn't
any desirable reason to use *o++ = t in the new iterator system.
It would be more appropriate to relax output_iterator to weakly_incrementable
(or input_or_output_iterator) and indirectly_writable,
given that many constrained algorithms already do that.
[2023-02-06; Reflector poll]
Set priority to 4 after reflector poll. Send to LEWG. Several votes for NAD.
[St. Louis 2024-06-28; LWG and SG9 joint session]
Poll: SG9 and LWG believe this is not a defect, but we do agree that there are inconsistencies that need a paper to address more thoroughly
|SF| F| N| A|SA| | 6| 2| 0| 0| 0|
Proposed resolution:
This wording is relative to N4928.
Modify 26.4 [algorithm.syn], header <algorithm> synopsis, as indicated:
#include <initializer_list> // see 17.11.2 [initializer.list.syn] namespace std { […] namespace ranges { template<class I, class O> using replace_copy_result = in_out_result<I, O>; template<input_iterator I, sentinel_for<I> S, class T1, class T2, weakly_incrementableoutput_iterator<const T2&>O, class Proj = identity> requires indirectly_writable<O, const T2&> && indirectly_copyable<I, O> && indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T1*> constexpr replace_copy_result<I, O> replace_copy(I first, S last, O result, const T1& old_value, const T2& new_value, Proj proj = {}); template<input_range R, class T1, class T2, weakly_incrementableoutput_iterator<const T2&>O, class Proj = identity> requires indirectly_writable<O, const T2&> && indirectly_copyable<iterator_t<R>, O> && indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T1*> constexpr replace_copy_result<borrowed_iterator_t<R>, O> replace_copy(R&& r, O result, const T1& old_value, const T2& new_value, Proj proj = {}); template<class I, class O> using replace_copy_if_result = in_out_result<I, O>; template<input_iterator I, sentinel_for<I> S, class T, weakly_incrementableoutput_iterator<const T&>O, class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred> requires indirectly_writable<O, const T&> && indirectly_copyable<I, O> constexpr replace_copy_if_result<I, O> replace_copy_if(I first, S last, O result, Pred pred, const T& new_value, Proj proj = {}); template<input_range R, class T, weakly_incrementableoutput_iterator<const T&>O, class Proj = identity, indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred> requires indirectly_writable<O, const T&> && indirectly_copyable<iterator_t<R>, O> constexpr replace_copy_if_result<borrowed_iterator_t<R>, O> replace_copy_if(R&& r, O result, Pred pred, const T& new_value, Proj proj = {}); } […] namespace ranges { template<class T, input_or_output_iteratoroutput_iterator<const T&>O, sentinel_for<O> S> requires indirectly_writable<O, const T&> constexpr O fill(O first, S last, const T& value); template<class T, output_range<const T&> R> constexpr borrowed_iterator_t<R> fill(R&& r, const T& value); template<class T, input_or_output_iteratoroutput_iterator<const T&>O> requires indirectly_writable<O, const T&> constexpr O fill_n(O first, iter_difference_t<O> n, const T& value); } […] }
Modify 26.7.5 [alg.replace] as indicated:
[…] template<input_iterator I, sentinel_for<I> S, class T1, class T2, weakly_incrementableoutput_iterator<const T2&>O, class Proj = identity> requires indirectly_writable<O, const T2&> && indirectly_copyable<I, O> && indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T1*> constexpr ranges::replace_copy_result<I, O> ranges::replace_copy(I first, S last, O result, const T1& old_value, const T2& new_value, Proj proj = {}); template<input_range R, class T1, class T2, weakly_incrementableoutput_iterator<const T2&>O, class Proj = identity> requires indirectly_writable<O, const T2&> && indirectly_copyable<iterator_t<R>, O> && indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T1*> constexpr ranges::replace_copy_result<borrowed_iterator_t<R>, O> ranges::replace_copy(R&& r, O result, const T1& old_value, const T2& new_value, Proj proj = {}); template<input_iterator I, sentinel_for<I> S, class T, weakly_incrementableoutput_iterator<const T&>O, class Proj = identity, indirect_unary_predicate<projected<I, Proj>> Pred> requires indirectly_writable<O, const T&> && indirectly_copyable<I, O> constexpr ranges::replace_copy_if_result<I, O> ranges::replace_copy_if(I first, S last, O result, Pred pred, const T& new_value, Proj proj = {}); template<input_range R, class T, weakly_incrementableoutput_iterator<const T&>O, class Proj = identity, indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred> requires indirectly_writable<O, const T&> && indirectly_copyable<iterator_t<R>, O> constexpr ranges::replace_copy_if_result<borrowed_iterator_t<R>, O> ranges::replace_copy_if(R&& r, O result, Pred pred, const T& new_value, Proj proj = {});-6- Let E be
[…]
Modify 26.7.6 [alg.fill] as indicated:
[…] template<class T, input_or_output_iteratoroutput_iterator<const T&>O, sentinel_for<O> S> requires indirectly_writable<O, const T&> constexpr O ranges::fill(O first, S last, const T& value); template<class T, output_range<const T&> R> constexpr borrowed_iterator_t<R> ranges::fill(R&& r, const T& value); template<class T, input_or_output_iteratoroutput_iterator<const T&>O> requires indirectly_writable<O, const T&> constexpr O ranges::fill_n(O first, iter_difference_t<O> n, const T& value);-1- Let N be max(0,
[…]n) for thefill_nalgorithms, andlast - firstfor thefillalgorithms.
join_with_view's const begin is underconstrainedSection: 25.7.15.2 [range.join.with.view] Status: New Submitter: Hewill Kang Opened: 2023-02-04 Last modified: 2023-02-10
Priority: 3
View all other issues in [range.join.with.view].
View all issues with New status.
Discussion:
In order to ensure that the pattern range is compatible with the inner range,
join_with_view requires that the two range types must satisfy compatible-joinable-ranges,
which requires that the value type, reference type, and rvalue reference type of the two range types share a common type.
const-qualified, there is no guarantee that their common
reference type still exists, in which case a hard error may occur since join_with_view's const begin
does not check for this (online example):
#include <ranges>
struct S {
S(const int&);
S(int&&);
S(const int&&) = delete;
};
int main() {
const auto r = std::views::single(std::views::single(0))
| std::views::join_with(std::views::single(S{0}));
auto e = std::ranges::iter_move(r.begin()); // hard error
}
[Issaquah 2023-02-10; LWG issue processing]
Set priority to 3.
Proposed resolution:
tuple relational operators have confused friendshipsSection: 22.4.9 [tuple.rel] Status: New Submitter: Corentin Jabot Opened: 2023-02-08 Last modified: 2023-03-22
Priority: 3
View other active issues in [tuple.rel].
View all other issues in [tuple.rel].
View all issues with New status.
Discussion:
In 22.4.9 [tuple.rel]:
template<class... TTypes, tuple-like UTuple> constexpr bool operator==(const tuple<TTypes...>& t, const UTuple& u);
Is defined as a non-member non-friend function that "is to be found via argument-dependent lookup only."
The intent is that it should be defined as a hidden friend intuple.
The current specification is confusing as to which class should contain that hidden friend, or how to otherwise
implement that adl only restriction. (An hostile reading may consider it to be a hidden friend of UTuple),
and does not follow the guidance of P1601 "Recommendations for Specifying ``Hidden Friends''".
We should consider making these operator== and operator<=> overloads hidden friends of
tuple, i.e.
std::tuple {
template<class... TTypes, tuple-like UTuple>
friend constexpr bool operator==(const tuple& t, const UTuple& u);
template<class... TTypes, tuple-like UTuple>
friend constexpr see below operator<=>(const tuple&, const UTuple&);
};
[2023-02-18; Daniel provides wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N4928.
Modify 22.4.2 [tuple.syn], header
<tuple>synopsis, as indicated:namespace std { […] // 22.4.9 [tuple.rel], relational operators template<class... TTypes, class... UTypes> constexpr bool operator==(const tuple<TTypes...>&, const tuple<UTypes...>&);template<class... TTypes, tuple-like UTuple> constexpr bool operator==(const tuple<TTypes...>&, const UTuple&);template<class... TTypes, class... UTypes> constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...> operator<=>(const tuple<TTypes...>&, const tuple<UTypes...>&);template<class... TTypes, tuple-like UTuple> constexpr see below operator<=>(const tuple<TTypes...>&, const UTuple&);[…] }Modify 22.4.4 [tuple.tuple], class template
tuplesynopsis, as indicated:namespace std { template<class... Types> class tuple { public: […] template<tuple-like UTuple> constexpr tuple& operator=(UTuple&&); template<tuple-like UTuple> constexpr const tuple& operator=(UTuple&&) const; // 22.4.9 [tuple.rel], relational operators template<tuple-like UTuple> friend constexpr bool operator==(const tuple&, const UTuple&); template<tuple-like UTuple> friend constexpr see below operator<=>(const tuple&, const UTuple&); // 22.4.4.4 [tuple.swap], tuple swap constexpr void swap(tuple&) noexcept(see below); constexpr void swap(const tuple&) const noexcept(see below); }; }Modify 22.4.9 [tuple.rel] as indicated:
template<class... TTypes, class... UTypes> constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u); template<class... TTypes,tuple-like UTuple> friend constexpr bool operator==(const tuple<TTypes...>& t, const UTuple& u);-1- For the first overload let
[…] -5- Remarks:UTuplebetuple<UTypes...>. For the second overload letTTypesdenote the packTypes.
(5.1) — The elementary comparisons are performed in order from the zeroth index upwards. No comparisons or element accesses are performed after the first equality comparison that evaluates to
false.(5.2) — The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.
template<class... TTypes, class... UTypes> constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...> operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u); template<class... TTypes,tuple-like UTuple> friend constexpr common_comparison_category_t<synth-three-way-result<TTypes, Elems>...> operator<=>(const tuple<TTypes...>& t, const UTuple& u);-6- For the second overload, let
[…] -8- Remarks: The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.TTypesdenote the packTypesandElemsdenotes the pack of typestuple_element_t<0, UTuple>, tuple_element_t<1, UTuple>, ... , tuple_element_t<tuple_size_v<UTuple> - 1, UTuple>.
[2023-03-05; Daniel comments and provides improved wording]
The revised wording ensures that no ambiguity exists between the overload candidates, furthermore the additional wording about being found via argument-dependent lookup only has been eliminated, because the general conventions of 16.4.6.6 [hidden.friends] apply.
The reusage of the exposition-only conceptdifferent-from is already existing practice in various places of
22.4 [tuple]. We have also existing wording practice where we have an extra Constraints: element added even though
a prototype has already a language constraint as part of its signature.
[2023-03-22; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4928.
Modify 22.4.2 [tuple.syn], header <tuple> synopsis, as indicated:
namespace std {
[…]
// 22.4.9 [tuple.rel], relational operators
template<class... TTypes, class... UTypes>
constexpr bool operator==(const tuple<TTypes...>&, const tuple<UTypes...>&);
template<class... TTypes, tuple-like UTuple>
constexpr bool operator==(const tuple<TTypes...>&, const UTuple&);
template<class... TTypes, class... UTypes>
constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...>
operator<=>(const tuple<TTypes...>&, const tuple<UTypes...>&);
template<class... TTypes, tuple-like UTuple>
constexpr see below operator<=>(const tuple<TTypes...>&, const UTuple&);
[…]
}
Modify 22.4.4 [tuple.tuple], class template tuple synopsis, as indicated:
namespace std {
template<class... Types>
class tuple {
public:
[…]
template<tuple-like UTuple>
constexpr tuple& operator=(UTuple&&);
template<tuple-like UTuple>
constexpr const tuple& operator=(UTuple&&) const;
// 22.4.9 [tuple.rel], relational operators
template<tuple-like UTuple>
friend constexpr bool operator==(const tuple&, const UTuple&);
template<tuple-like UTuple>
friend constexpr see below operator<=>(const tuple&, const UTuple&);
// 22.4.4.4 [tuple.swap], tuple swap
constexpr void swap(tuple&) noexcept(see below);
constexpr void swap(const tuple&) const noexcept(see below);
};
}
Modify 22.4.9 [tuple.rel] as indicated:
template<class... TTypes, class... UTypes> constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u); template<class... TTypes,tuple-like UTuple> friend constexpr bool operator==(const tuple<TTypes...>& t, const UTuple& u);-1- For the first overload let
-?- Constraints: For the second overload,UTuplebetuple<UTypes...>. For the second overload letTTypesdenote the packTypes.different-from<UTuple, tuple>(25.5.2 [range.utility.helpers]) istrue. -2- Mandates: […] […] -5- Remarks:
(5.1) —The elementary comparisons are performed in order from the zeroth index upwards. No comparisons or element accesses are performed after the first equality comparison that evaluates tofalse.
(5.2) — The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.template<class... TTypes, class... UTypes> constexpr common_comparison_category_t<synth-three-way-result<TTypes, UTypes>...> operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u); template<class... TTypes,tuple-like UTuple> friend constexpr common_comparison_category_t<synth-three-way-result<TTypes, Elems>...> operator<=>(const tuple<TTypes...>& t, const UTuple& u);-6- For the second overload, let
-?- Constraints: For the second overload,TTypesdenote the packTypesandElemsdenotes the pack of typestuple_element_t<0, UTuple>, tuple_element_t<1, UTuple>, ... , tuple_element_t<tuple_size_v<UTuple> - 1, UTuple>.different-from<UTuple, tuple>(25.5.2 [range.utility.helpers]) istrue. -7- Effects: […]-8- Remarks: The second overload is to be found via argument-dependent lookup (6.5.4 [basic.lookup.argdep]) only.
Section: 17.15.7 [support.c.headers.other] Status: New Submitter: Alex Mills Opened: 2023-02-09 Last modified: 2023-03-22
Priority: 4
View other active issues in [support.c.headers.other].
View all other issues in [support.c.headers.other].
View all issues with New status.
Discussion:
In 17.15.7 [support.c.headers.other], it states that the <name.h> headers behave
"as if each name placed in the standard library namespace by the corresponding header is placed within the
global namespace scope". There are exceptions for several different definitions in the headers, but it's left
ambiguous as to whether their inclusion in the <name.h> headers is required or unspecified.
::byte is prohibited, it isn't clear whether std::byte
is required to be included at all (this applies to the other listed exceptions as well). Though example 1 makes
it appear that their inclusion requirements are meant to be left unspecified, paragraph 1 does not provide clarification.
Adding specific wording will ensure this will not be misinterpreted in the future.
To do this, we should append a sentence to [support.c.headers.other] p1:
Whether the listed exceptions are included in their corresponding
<name.h>headers under the standard library namespace is unspecified.
[2023-03-22; Reflector poll]
Set priority to 4 after reflector poll.
"The new wording is confusingly inconsistent with the existing wording
in that paragraph. Would prefer '... Whether the listed exceptions are declared
in the namespace std by their corresponding <name.h>
headers is unspecified.'."
Proposed resolution:
This wording is relative to N4928.
Modify 17.15.7 [support.c.headers.other] as indicated:
-1- Every C header other than
<complex.h>(17.15.2 [complex.h.syn]),<iso646.h>(17.15.3 [iso646.h.syn]),<stdalign.h>(17.15.4 [stdalign.h.syn]),<stdatomic.h>(32.5.12 [stdatomic.h.syn]),<stdbool.h>(17.15.5 [stdbool.h.syn]), and<tgmath.h>(17.15.6 [tgmath.h.syn]), each of which has a name of the form<name.h>, behaves as if each name placed in the standard library namespace by the corresponding<cname>header is placed within the global namespace scope, except for the functions described in 29.7.6 [sf.cmath], thestd::lerpfunction overloads (29.7.4 [c.math.lerp]), the declaration ofstd::byte(17.2.1 [cstddef.syn]), and the functions and function templates described in 17.2.5 [support.types.byteops]. It is unspecified whether these names are first declared or defined within namespace scope (6.4.6 [basic.scope.namespace]) of the namespacestdand are then injected into the global namespace scope by explicit using-declarations (9.10 [namespace.udecl]). Whether the listed exceptions are included in their corresponding<name.h>headers under the standard library namespace is unspecified.
Section: 26.11.8 [specialized.construct] Status: New Submitter: Jiang An Opened: 2023-02-17 Last modified: 2023-03-22
Priority: 3
View all other issues in [specialized.construct].
View all issues with New status.
Discussion:
LWG 3870(i) removed the support for constructing objects via cv-qualified lvalues. However, it had not changed anything in the immediate context, which means some previously permitted usages become hard errors instead of substitution failures.
Note thatranges::uninitialized_default_construct and ranges::uninitialized_default_construct_n
are underconstrained even before LWG 3870(i), because remove_reference_t<iter_reference_t<I>>
may be a const type, while only it's cv-unqualified version (iter_value_t<I>)
is required to be default_initializable.
construct_at and ranges::construct_at are also made underconstrained because the function
body is no longer valid when T is cv-qualified, which is not reflected in Constraints:.
[2023-03-22; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4928.
Modify 26.11.2 [special.mem.concepts] as indicated:
template<class I> concept nothrow-input-iterator = // exposition only input_iterator<I> && is_lvalue_reference_v<iter_reference_t<I>> && same_as<remove_cvref_t<iter_reference_t<I>>, remove_reference_t<iter_reference_t<I>> && same_as<remove_cvref_t<iter_reference_t<I>>, iter_value_t<I>>;
Modify 26.11.8 [specialized.construct] as indicated:
template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); namespace ranges { template<class T, class... Args> constexpr T* construct_at(T* location, Args&&... args); }-1- Constraints:
-2- Effects: Equivalent to:Tis a cv-unqualified type, and tThe expression::new (declval<void*>()) T(declval<Args>()...)is well-formed when treated as an unevaluated operand (7.2.3 [expr.context]).return ::new (voidify(*location)) T(std::forward<Args>(args)...);
std::(ranges::)destroy_at should destroy array elements in the decreasing index orderSection: 26.11.9 [specialized.destroy] Status: New Submitter: Jiang An Opened: 2023-02-17 Last modified: 2023-03-22
Priority: 3
View all issues with New status.
Discussion:
Currently, std::(ranges::)destroy_at is specified to destroy array elements in the increasing index order
(26.11.9 [specialized.destroy]/1.1), which is inconsistent with the decreasing order specified in the core language
(11.4.7 [class.dtor]/13) and the order for arrays created by std::make_shared and
std::allocate_shared (mandated by LWG 3005(i)).
Previous resolution [SUPERSEDED]:
This wording is relative to N4928.
Modify 26.11.9 [specialized.destroy] as indicated:
template<class T> constexpr void destroy_at(T* location); namespace ranges { template<destructible T> constexpr void destroy_at(T* location) noexcept; }-1- Effects:
(1.1) — If
Tis an array type, equivalent todestroy(rbegin.begin(*location), rendend(*location))(1.2) — Otherwise, equivalent to
location->~T().
[2023-02-26; Daniel comments and provides alternative wording]
The suggested fix indeed corrects an inconsistency, but also implies a silent behaviour change at runtime, since at least MSVC STL and
libstdc++ implement the array destruction order as specified (others not tested).
The below wording therefore suggests to introduce a specific feature macro for this, so that user code can potentially react on this,
regardless of potential vendor API breakage hesitations.
The natural feature macro to increase would be that which introduced the specific array destruction behavior of destroy_at,
which was P0896R4, and which introduced __cpp_lib_ranges, on the other hand the specification change affects
both the std::ranges and the std forms of destroy_at, so it seems plausible to suggest a new, specific feature
macro for both destroy_at function templates. This is what the proposed wording does.
[2023-03-22; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4928.
Modify 17.3.2 [version.syn], header <version> synopsis, as indicated and replace
the placeholder YYYYMML by the year and month of adoption of this issue:
[…] #define __cpp_lib_coroutine 201902L // also in <coroutine> #define __cpp_lib_destroy_at YYYYMML // also in <memory> #define __cpp_lib_destroying_delete 201806L // also in <new> […]
Modify 26.11.9 [specialized.destroy] as indicated:
template<class T> constexpr void destroy_at(T* location); namespace ranges { template<destructible T> constexpr void destroy_at(T* location) noexcept; }-1- Effects:
(1.1) — If
Tis an array type, equivalent todestroy(rbegin.begin(*location), rendend(*location))(1.2) — Otherwise, equivalent to
location->~T().
Section: 24.3.4.4 [iterator.concept.winc] Status: New Submitter: Jiang An Opened: 2023-02-19 Last modified: 2023-03-22
Priority: 3
View all other issues in [iterator.concept.winc].
View all issues with New status.
Discussion:
Currently integer-class types are required to be wider than every integer type (24.3.4.4 [iterator.concept.winc]/3). As a result, if compiler provides extended integer types later whose width are not less than that of an integer-class type, then integer-class types with that width must be abandoned, which seems ABI-breaking (see also LWG 3828(i)).
I think we should allow some extended integer types to be wider than some integer-class types.[2023-03-22; Reflector poll]
Set priority to 3 after reflector poll.
"Proposed resolution is insufficient. A lot of the wording depends on the fact
that the range of widths of integer-class types and that of integer types are
disjoint. And it also disallows signed integer -> unsigned integer-class
conversion and by extension mixed ops, which is seriously breaking (e.g., it
disallows x += 1)."
Proposed resolution:
This wording is relative to N4928.
Modify 24.3.4.4 [iterator.concept.winc] as indicated:
-3- The range of representable values of an integer-class type is the continuous set of values over which it is defined. For any integer-class type, its range of representable values is either
[…] -6- Expressions of integer-class type are explicitly convertible to any integer-like type, and implicitly convertible to any integral type or integer-class type of equal or greater width and the same signedness. Expressions of integral type are-2N-1to2N-1 - 1(inclusive) for some integerN, in which case it is a signed-integer-class type, or0to2N - 1(inclusive) for some integerN, in which case it is an unsigned-integer-class type. In both cases,Nis called the width of the integer-class type. The width of an integer-class type is greater than that of every standard integral type of the same signedness.both implicitly andexplicitly convertible to any integer-class type, and implicitly convertible to any integer-class type that can represent all values of the source type. Conversions between integral and integer-class types and between two integer-class types do not exit via an exception. The result of such a conversion is the unique value of the destination type that is congruent to the source modulo2N, whereNis the width of the destination type.
std::expected<cv T, E>Section: 22.8.6.1 [expected.object.general] Status: New Submitter: Jiang An Opened: 2023-02-19 Last modified: 2024-10-02
Priority: 2
View all other issues in [expected.object.general].
View all issues with New status.
Discussion:
Currently the value_type of std::expected can be a cv-qualified type, which is possibly intended.
However, LWG 3870(i) disallows std::construct_at to construct objects via cv T*, which
breaks std::expected<cv T, E> because some operations are specified with std::construct_at
(22.8.6.4 [expected.object.assign], 22.8.6.5 [expected.object.swap]).
T is cv-qualified, it would be better to store std::remove_cv_t<T> subobject
while sometimes (other than construction/destruction) access it via a cv-qualified glvalue, which can also avoid UB
associated with const/volatile objects.
[2023-03-22; Reflector poll]
Set priority to 2 after reflector poll.
"Not clear if all these wording changes are needed or desired."
"Unconvinced that the mixed-value-error swap should use value(),
source is destroyed immediately anyway. The else branch should use
remove_cv_t too."
Previous resolution [SUPERSEDED]:
This wording is relative to N4928.
[Drafting note: When assignment and
swapneed to backup the old value by move construction, the source should be considered cv-unqualified, as the backup mechanism is only used internally.]
Modify 22.8.6.1 [expected.object.general] as indicated:
[…] bool has_val; // exposition only union { remove_cv_t<T> val; // exposition only E unex; // exposition only }; […]Modify 22.8.6.4 [expected.object.assign] as indicated:
constexpr expected& operator=(const expected& rhs);[…]-2- Effects:
(2.1) — If
this->has_value() && rhs.has_value()istrue, equivalent tovalue().val= *rhs[…]
constexpr expected& operator=(expected&& rhs) noexcept(see below);[…][…]
-6- Effects:
(6.1) — If
this->has_value() && rhs.has_value()istrue, equivalent tovalue().val= std::move(*rhs)[…]
template<class U = T> constexpr expected& operator=(U&& v);[…]
-10- Effects:
(10.1) — If
has_value()istrue, equivalent tovalue().val= std::forward<U>(v)[…]
Modify Table 64:
swap(expected&)effects [tab:expected.object.swap] as indicated:
Table 64 — swap(expected&)effects [tab:expected.object.swap]this->has_value()!this->has_value()rhs.has_value()equivalent to: using std::swap;
swap(value()val, rhs.value()val);calls rhs.swap(*this)[…]Modify 22.8.6.5 [expected.object.swap] as indicated:
constexpr void swap(expected& rhs) noexcept(see below);-1- Constraints: […]
-2- Effects: See Table 64 [tab:expected.object.swap]. For the case whererhs.value()isfalseandthis->has_value()istrue, equivalent to:if constexpr (is_nothrow_move_constructible_v<E>) { E tmp(std::move(rhs.unex)); destroy_at(addressof(rhs.unex)); try { construct_at(addressof(rhs.val), std::move(value()val)); destroy_at(addressof(val)); construct_at(addressof(unex), std::move(tmp)); } catch(...) { construct_at(addressof(rhs.unex), std::move(tmp)); throw; } } else { T tmp(std::move(val)); destroy_at(addressof(val)); try { construct_at(addressof(unex), std::move(rhs.unex)); destroy_at(addressof(rhs.unex)); construct_at(addressof(rhs.val), std::move(tmp)); } catch (...) { construct_at(addressof(val), std::move(tmp)); throw; } } has_val = false; rhs.has_val = true;
[2024-10-02; Jonathan provides improved wording]
Removed the use of value() in the [expected.object.swap] p2 Effects:
and added remove_cv_t to the local T in the else-branch.
Proposed resolution:
This wording is relative to N4988.
[Drafting note: When assignment and
swapneed to backup the old value by move construction, the source should be considered cv-unqualified, as the backup mechanism is only used internally.]
Modify 22.8.6.1 [expected.object.general] as indicated:
[…]
bool has_val; // exposition only
union {
remove_cv_t<T> val; // exposition only
E unex; // exposition only
};
[…]
Modify 22.8.6.4 [expected.object.assign] as indicated:
constexpr expected& operator=(const expected& rhs);[…]-2- Effects:
(2.1) — If
this->has_value() && rhs.has_value()istrue, equivalent tovalue().val= *rhs[…]
constexpr expected& operator=(expected&& rhs) noexcept(see below);[…][…]
-6- Effects:
(6.1) — If
this->has_value() && rhs.has_value()istrue, equivalent tovalue().val= std::move(*rhs)[…]
template<class U = T> constexpr expected& operator=(U&& v);[…]
-10- Effects:
(10.1) — If
has_value()istrue, equivalent tovalue().val= std::forward<U>(v)[…]
Modify Table 64: swap(expected&) effects [tab:expected.object.swap] as indicated:
Table 64 — swap(expected&)effects [tab:expected.object.swap]this->has_value()!this->has_value()rhs.has_value()equivalent to: using std::swap;
swap(value()val, rhs.value()val);calls rhs.swap(*this)[…]
Modify 22.8.6.5 [expected.object.swap] as indicated:
constexpr void swap(expected& rhs) noexcept(see below);-1- Constraints: […]
-2- Effects: See Table 64 [tab:expected.object.swap]. For the case whererhs.value()isfalseandthis->has_value()istrue, equivalent to:if constexpr (is_nothrow_move_constructible_v<E>) { E tmp(std::move(rhs.unex)); destroy_at(addressof(rhs.unex)); try { construct_at(addressof(rhs.val), std::move(val)); destroy_at(addressof(val)); construct_at(addressof(unex), std::move(tmp)); } catch(...) { construct_at(addressof(rhs.unex), std::move(tmp)); throw; } } else { remove_cv_t<T> tmp(std::move(val)); destroy_at(addressof(val)); try { construct_at(addressof(unex), std::move(rhs.unex)); destroy_at(addressof(rhs.unex)); construct_at(addressof(rhs.val), std::move(tmp)); } catch (...) { construct_at(addressof(val), std::move(tmp)); throw; } } has_val = false; rhs.has_val = true;
Section: 18.3 [concepts.syn], 24.2 [iterator.synopsis] Status: New Submitter: blacktea hamburger Opened: 2023-02-25 Last modified: 2023-03-22
Priority: 3
View all issues with New status.
Discussion:
std::indirect_equivalence_relation and std::indirect_strict_weak_order have default values
for the second template parameters:
template<class F, class I1, class I2 = I1> concept indirect_equivalence_relation = see below; template<class F, class I1, class I2 = I1> concept indirect_strict_weak_order = see below;
But std::relation, std::equivalence_relation, std::strict_weak_order, and
std::indirect_binary_predicate are missing such default values:
template<class R, class T, class U> concept relation = see below; template<class R, class T, class U> concept equivalence_relation = see below; template<class R, class T, class U> concept strict_weak_order = see below; template<class F, class I1, class I2> concept indirect_binary_predicate = see below;
That makes them inconsistent and it should not be expected.
[2023-03-22; Reflector poll]
Set priority to 3 after reflector poll.
"Borderline design change."
"Should not change indirect_binary_predicate."
"NAD, write a paper."
"NAD, default argument would make typos compile. Explicit is good."
Proposed resolution:
This wording is relative to N4928.
Modify 18.3 [concepts.syn], header <concepts> synopsis, as indicated:
// all freestanding
namespace std {
[…]
// 18.7.5 [concept.relation], concept relation
template<class R, class T, class U = T>
concept relation = see below;
// 18.7.6 [concept.equiv], concept equivalence_relation
template<class R, class T, class U = T>
concept equivalence_relation = see below;
// 18.7.7 [concept.strictweakorder], concept strict_weak_order
template<class R, class T, class U = T>
concept strict_weak_order = see below;
}
Modify 18.7.5 [concept.relation] as indicated:
template<class R, class T, class U = T>
concept relation =
predicate<R, T, T> && predicate<R, U, U> &&
predicate<R, T, U> && predicate<R, U, T>;
Modify 18.7.6 [concept.equiv] as indicated:
template<class R, class T, class U = T> concept equivalence_relation = relation<R, T, U>;
Modify 18.7.7 [concept.strictweakorder] as indicated:
template<class R, class T, class U = T> concept strict_weak_order = relation<R, T, U>;
Modify 24.2 [iterator.synopsis], header <iterator> synopsis, as indicated:
[…]
namespace std {
[…]
// 24.3.6.3 [indirectcallable.indirectinvocable], indirect callables
[…]
template<class F, class I1, class I2 = I1>
concept indirect_binary_predicate = see below; // freestanding
template<class F, class I1, class I2 = I1>
concept indirect_equivalence_relation = see below; // freestanding
[…]
}
Modify 24.3.6.3 [indirectcallable.indirectinvocable] as indicated:
-1- The indirect callable concepts are used to constrain those algorithms that accept callable objects (22.10.4 [func.require]) as arguments.
[…] template<class F, class I1, class I2 = I1> concept indirect_binary_predicate = indirectly_readable<I1> && indirectly_readable<I2> && copy_constructible<F> && predicate<F&, iter_value_t<I1>&, iter_value_t<I2>&> && predicate<F&, iter_value_t<I1>&, iter_reference_t<I2>> && predicate<F&, iter_reference_t<I1>, iter_value_t<I2>&> && predicate<F&, iter_reference_t<I1>, iter_reference_t<I2>> && predicate<F&, iter_common_reference_t<I1>, iter_common_reference_t<I2>>; […]
viewable_range is not quite rightSection: 25.4.6 [range.refinements] Status: New Submitter: Hewill Kang Opened: 2023-02-27 Last modified: 2023-03-22
Priority: 4
View all other issues in [range.refinements].
View all issues with New status.
Discussion:
The requirements of viewable_range for view type is view<remove_cvref_t<T>> &&
constructible_from<remove_cvref_t<T>, T>, that is, when the decayed type of T models view,
it must be constructible from T.
This part of the constraint corresponds to first bullet of views::all (25.7.6.1 [range.all.general]),
which returns decay-copy(E) if the decayed type of E models view.
However, decay-copy(E) constraints convertible_to<T, decay_t<T>> which is a
stronger requirement than constructible_from, which is reflected in its rejection of types with explicit
copy constructors.
This inconsistency is such that the following causes the range adapter to produce a hard error when invoked (online example):
#include <ranges>
struct View : std::ranges::view_base
{
View();
explicit View(const View&); // explicit copy constructor
View& operator=(const View&);
View(View&&);
int* begin();
int* end();
};
int main()
{
View v;
auto r = std::views::take(v, 5); // hard error
}
[2023-03-22; Reflector poll]
Set priority to 4 after reflector poll. "About as contrived as it gets." "Add generic front matter telling users the library doesn't support types with explicit copy constructors."
Proposed resolution:
This wording is relative to N4928.
Modify 25.4.6 [range.refinements] as indicated:
template<class T>
concept viewable_range =
range<T> &&
((view<remove_cvref_t<T>> && convertible_to<T, remove_cvref_t<T>>constructible_from<remove_cvref_t<T>, T>) ||
(!view<remove_cvref_t<T>> &&
(is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))));
std::barrierSection: 32.9.3.3 [thread.barrier.class] Status: New Submitter: Jiang An Opened: 2023-03-02 Last modified: 2023-03-22
Priority: 3
View all issues with New status.
Discussion:
32.9.3.3 [thread.barrier.class]/5 currently says:
[…]is_nothrow_invocable_v<CompletionFunction&>shall betrue.
This requirement introduces a kind of undefined behavior and permits implementation divergence. Currently MSVC STL enforces the requirement, while libstdc++ and libc++ don't.
If implementation divergence is not intended, I don't think it makes much sense to introduce UB in this way. I guess we should either strengthen the requirement to require well-formedness affection or relax it.[2023-03-22; Reflector poll]
Set priority to 3 after reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N4928.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A: Effectively impose a Mandates: requirement.
Modify 32.9.3.3 [thread.barrier.class] as indicated:
-5-
CompletionFunctionshall meet the Cpp17MoveConstructible (Table 32) and Cpp17Destructible (Table 36) requirements. Instantiation ofbarrier<CompletionFunction>is ill-formed ifis_nothrow_invocable_v<CompletionFunction&>is nottrue.is_nothrow_invocable_v<CompletionFunction&>shall betrueOption B: Clarify that we impose a no-throw precondition here, whose violation causes UB.
Modify 32.9.3.3 [thread.barrier.class] as indicated:
-3- The phase completion step that is executed at the end of each phase has the following effects:
(3.1) — Invokes the completion function, equivalent to
completion(). If any invocation to the completion function throws an exception, the behavior is undefined.(3.2) — Unblocks all threads that are blocked on the phase synchronization point.
[…]
-5-CompletionFunctionshall meet the Cpp17MoveConstructible (Table 32) and Cpp17Destructible (Table 36) requirements.is_nothrow_invocable_v<CompletionFunction&>shall betrue.
[2023-03-22; Jonathan provides improved wording]
Proposed resolution:
This wording is relative to N4928.
Modify 32.9.3.3 [thread.barrier.class] as indicated:
-3- The phase completion step that is executed at the end of each phase has the following effects:
(3.1) — Invokes the completion function, equivalent to
completion(); if that invocation exits via an exception, the functionstd::terminateis invoked.(3.2) — Unblocks all threads that are blocked on the phase synchronization point.
[…]
-5-
CompletionFunctionshall meet the Cpp17MoveConstructible (Table 32) and Cpp17Destructible (Table 36) requirements.A program that instantiatesis_nothrow_invocable_v<CompletionFunction&>shall betrue.barrier<CompletionFunction>is ill-formed ifis_invocable_v<CompletionFunction&>isfalse.
std::declval<cv void> should be (cv-unqualified) voidSection: 22.2.6 [declval] Status: New Submitter: Jiang An Opened: 2023-03-07 Last modified: 2023-03-22
Priority: 4
View other active issues in [declval].
View all other issues in [declval].
View all issues with New status.
Discussion:
Currently libc++ and libstdc++ determine the return type of std::declval like this:
template<class _Tp> _Tp&& __declval_ret(int); // selected when _Tp is a referenceable type template<class _Tp> _Tp __declval_ret(long); // selected when _Tp is cv void template<class _Tp> decltype(__declval_ret<_Tp>(0)) declval() noexcept;
This strategy avoids instantiation of class templates. But it also drops cv-qualifiers of the return type
when the type is cv void, which is different from the standard requirements. Such difference has
no impact in normal use of std::declval, but is observable via decltype(std::declval<const void>)
and its friends.
[2023-03-22; Reflector poll]
Set priority to 4 after reflector poll.
"The testcase isn't even valid with the previous 'conforming' libstdc++ implementation."
"declval isn't an addressable function, so would prefer if this
was ill-formed rather than complicating the definition for this case."
Proposed resolution:
This wording is relative to N4928.
Modify 22.2.1 [utility.syn], header <utility> synopsis, and 22.2.6 [declval] as indicated:
template<class T> remove_cv_t<add_rvalue_reference_t<T>> declval() noexcept; // as unevaluated operand
Section: 32.5.8.5 [atomics.types.pointer], 32.5.7.5 [atomics.ref.pointer] Status: New Submitter: Jiang An Opened: 2023-03-18 Last modified: 2023-05-24
Priority: 3
View other active issues in [atomics.types.pointer].
View all other issues in [atomics.types.pointer].
View all issues with New status.
Discussion:
This was originally editorial issue #6185.
The term "undefined address" is used in 32.5.8.5 [atomics.types.pointer]/8 and 32.5.7.5 [atomics.ref.pointer]/6, however, it seems not properly defined and the intended meaning is unclear. The term originally appeared in the paper N2393 and was reused by P0019R8, but no explanation can be found in these papers. Perhaps "undefined address" is related to the undefined behavior specified in 7.6.6 [expr.add].[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 25.6 [range.factories], 25.7 [range.adaptors] Status: New Submitter: Jiang An Opened: 2023-03-18 Last modified: 2023-06-01
Priority: 3
View all issues with New status.
Discussion:
[For the term SCARY see N2911 and N2913.]
In 25.6 [range.factories] and 25.7 [range.adaptors], many iterator types are specified as exposition-only nested classes or nested class templates. This has some observable differences from specifying them as class templates declared in the namespace scope:value types are generally not associated entities of iterators or sentinels during ADL, which prevents unintended overloads to be found;
two different range adaptor/factory types, e.g. iota_view<I, S1> and iota_view<I, S2>,
have different iterator types.
The ADL reduction seems preferable. However, is it intended to required to any two different range adaptor/factory types to have two different iterator types, even when the same iterator type can work as expected?
Sentinel types don't seem able to be SCARY as they are dependent on iterator types.[2023-06-01; Reflector poll]
Set priority to 3 after reflector poll.
"The ADL hiding stuff is a red herring - SCARY-ness doesn't prevent ADL hiding. Shouldn't require them to be different types (and similarly should not require const/non-const iterators to be implemented as class templates with a single bool template parameter)."
Proposed resolution:
<iostream> on initialization are not yet precisely specifiedSection: 31.4.2 [iostream.objects.overview] Status: New Submitter: Jiang An Opened: 2023-03-27 Last modified: 2023-05-24
Priority: 4
View all other issues in [iostream.objects.overview].
View all issues with New status.
Discussion:
LWG 3878(i) specified the effects of importing standard library modules on initialization.
However, the effects of including <iostream> are not yet precisely specified.
A hostile reading may consider this to allow the ios_base::Init variable to be
a function-local static variable or an inline variable, which defeats the purpose.
[2023-05-24; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
This wording is relative to N4944.
Change 31.4.2 [iostream.objects.overview]/5 as indicated:
-5- The results of including
<iostream>in a translation unit shall be as if<iostream>defined aninstance ofios_base::Initwithstatic storage durationordered initialization (6.10.3.3 [basic.start.dynamic]). Each C++ library module (16.4.2.4 [std.modules]) in a hosted implementation shall behave as if it contains an interface unit that defines an unexportedios_base::Initvariable with ordered initialization(6.10.3.3 [basic.start.dynamic]).
unique_ptr's operator* is missing a mandateSection: 20.3.1.3.5 [unique.ptr.single.observers], 20.3.1.4.4 [unique.ptr.runtime.observers] Status: New Submitter: Brian Bi Opened: 2023-03-27 Last modified: 2023-06-01
Priority: 3
View other active issues in [unique.ptr.single.observers].
View all other issues in [unique.ptr.single.observers].
View all issues with New status.
Discussion:
The return type of std::unique_ptr<T>::operator* is
std::add_lvalue_reference_t<T>,
but there is no mandate stating that *get() is convertible to that type.
There also does not appear to be a mandate that *get() is a valid expression;
dereferenceability is not part of the Cpp17NullablePointer requirements.
A similar issue appears to exist for std::unique_ptr<T[]>::operator[].
[2023-03-28; Reflector poll]
Set priority to 3 after reflector poll.
"It would be nice to Mandate !reference_converts_from_temporary_v<add_lvalue_reference_t<T>, decltype(*get())>."
"noexcept-specifier isn't quite right, conversion from *get() to T& can throw."
Previous resolution [SUPERSEDED]:
This wording is relative to N4944.
Add the following bullet before 20.3.1.3.5 [unique.ptr.single.observers] paragraph 1:
constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));-?- Mandates:
*get()is a valid expression that is convertible toadd_lvalue_reference_t<T>.-1- Preconditions:
get() != nullptr.-2- Returns:
*get().Add the following bullet before 20.3.1.4.4 [unique.ptr.runtime.observers] paragraph 1:
constexpr T& operator[](size_t i) const;-?- Mandates:
get()[i]is a valid expression that is convertible toT&.-1- Preconditions:
i< the number of elements in the array to which the stored pointer points.-2- Returns:
get()[i].
[2023-04-03; Jonathan provides new wording as requested by LWG]
Proposed resolution:
This wording is relative to N4944.
Add the following bullet before 20.3.1.3.5 [unique.ptr.single.observers] paragraph 1:
constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));-1- Preconditions:
get() != nullptr.-2-
ReturnsEffects: Equivalent to:return*get();.
Add the following bullet before 20.3.1.4.4 [unique.ptr.runtime.observers] paragraph 1:
constexpr T& operator[](size_t i) const;-1- Preconditions:
get() != nullptr.i< the number of elements in the array to which the stored pointer points-2-
ReturnsEffects: Equivalent to:returnget()[i];.
allocator, polymorphic_allocator, and containers should forbid cv-qualified typesSection: 20.2.10 [default.allocator], 20.5.3.1 [mem.poly.allocator.class.general], 23.2 [container.requirements] Status: New Submitter: Stephan T. Lavavej Opened: 2023-04-02 Last modified: 2023-05-24
Priority: 3
View other active issues in [default.allocator].
View all other issues in [default.allocator].
View all issues with New status.
Discussion:
LWG 2447(i) adjusted what is now N4944 16.4.4.6.1 [allocator.requirements.general]/2.1 so that the Cpp17Allocator requirements are specified for cv-unqualified object types only.
However, nothing in 20.2.10 [default.allocator] restricts whatT in allocator<T> can be,
except where 20.2.10.2 [allocator.members]/2 and /6 require it to be complete for allocate() and
allocate_at_least() (and by implication, for deallocate()). (Long ago, allocator had member functions
whose signatures implied that const-qualified types were forbidden, but those signatures were deprecated and
removed. This explains the phrasing of an MSVC static assertion.)
20.5.3.1 [mem.poly.allocator.class.general] says a bit more about Tp in polymorphic_allocator<Tp>
but doesn't forbid anything. It says that if Tp is a cv-unqualified object type, then
polymorphic_allocator<Tp> meets the Cpp17Allocator requirements and allocator completeness requirements,
but that's all.
There's some implementation variation here. libstdc++ and MSVC reject allocator<const int>. libc++
(as of LLVM 16) accepts it but this appears to have been an extension that they're trying to get rid of (their maintainers
may be able to comment further; see
llvm-project/commit/a54d028895c91da356a4aaf30e27a5a5b90dd313).
These 3 implementations all reject allocator<volatile int>, polymorphic_allocator<const int>,
and polymorphic_allocator<volatile int> with varying messages.
The Standard should provide clarity here, by mandating that only cv-unqualified object types be given to allocator,
polymorphic_allocator, and containers. (allocator<void> must also be allowed, of course. I forget
if polymorphic_allocator is supposed to accept void.) This would simply Standardize existing/desired practice.
While it may seem arcane, attempting to form vector<const T> is a common novice mistake — so common that
MSVC had to add a static_assert to emit a clear error message.
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
allocator<void> and possibly polymorphic_allocator<void> should be clarifiedSection: 20.2.10 [default.allocator], 20.5.3 [mem.poly.allocator.class] Status: New Submitter: Daniel Krügler Opened: 2023-04-08 Last modified: 2023-05-24
Priority: 3
View other active issues in [default.allocator].
View all other issues in [default.allocator].
View all issues with New status.
Discussion:
Before P0174 had been approved for the working paper, the validity of using void as template argument for
std::allocator was obvious due to the existing specification of the explicit specialization allocator<void>.
std::allocator to support void as template argument
any more. We fall now under the constraints for template "components" specified in 16.4.5.8 [res.on.functions] bullet 2.5.
-2- In particular, the behavior is undefined in the following cases:
[…]
(2.5) — If an incomplete type (6.9.1 [basic.types.general]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.
But no such allowance wording exists for allocator<void> nor for polymorphic_allocator<void>, more
to the contrary, 16.4.4.6.1 [allocator.requirements.general] only refers to cv-unqualified object types as value types
and void is not an object type.
operator new members of std::generator
mention a fall-back of using allocator<void>.
20.2.10.1 [default.allocator.general] says that all specializations of the default allocator meet the allocator completeness
requirements (16.4.4.6.2 [allocator.requirements.completeness]), but albeit this specification does not specifically exclude
the existence of an incomplete value type, the wording here does also not provide a definite statement, that it is valid (The
wording originally was provided when we started adding support for (yet) incomplete value types that at some point later will become
complete, but void can never be completed), since it is mostly focused on the completeness requirement for the allocator
type itself.
The situation is similar (albeit maybe not that strong) for polymorphic_allocator<void>, since
20.5.3 [mem.poly.allocator.class] p1 has some unusual wording form that says
-1- A specialization of class template
pmr::polymorphic_allocatormeets the Cpp17Allocator requirements (16.4.4.6.1 [allocator.requirements.general]) if its template argument is a cv-unqualified object type.
and says then in p2:
-2- A specialization of class template
pmr::polymorphic_allocatormeets the allocator completeness requirements (16.4.4.6.2 [allocator.requirements.completeness]) if its template argument is a cv-unqualified object type.
Again, this wording is not conclusive, whether void is intended to be supported, it is certainly not completely ruled
out, but that is not strong enough to counterpart 16.4.5.8 [res.on.functions] (2.5). It is maybe worth pointing out that
for a while we were considering to use void as default template argument for pmr::polymorphic_allocator,
see e.g. P0339R0, but that thought was later replaced by deciding for std::byte instead, which
is a complete object type.
std::allocator is intended to support incomplete types, maybe also
for polymorphic_allocator. If polymorphic_allocator is intended to support incomplete types as well,
we should also amend 20.5.3.3 [mem.poly.allocator.mem] p1 and p8 with a Mandates: element similarly as we did
for std::allocator via LWG 3307(i).
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4944.
Add the following new paragraph at the end of 20.2.10.1 [default.allocator.general] as indicated:
-2-
-?- The template parameterallocator_traits<allocator<T>>::is_always_equal::valueistruefor anyT.Tofallocatormay be an incomplete type.
Add the following new paragraph at the end of 20.5.3.1 [mem.poly.allocator.class.general] (possibly just after the class template synopsis) as indicated:
-?- The template parameter
Tpofpolymorphic_allocatormay be an incomplete type.
Modify 20.5.3.3 [mem.poly.allocator.mem] as indicated:
[Drafting note: The reference to
sizeof(Tp)gives indirect evidence that we want to exclude incomplete types here, but we cannot rely on the "equivalent to" magic formula, because that is defined conditionally]
[[nodiscard]] Tp* allocate(size_t n);-?- Mandates:
-1- Effects: IfTpis not an incomplete type (6.9.1 [basic.types.general]).numeric_limits<size_t>::max() / sizeof(Tp) < n, throwsbad_array_new_length. Otherwise equivalent to:return static_cast<Tp*>(memory_rsrc->allocate(n * sizeof(Tp), alignof(Tp)));[Drafting note: We don't need extra wording for the member templates
Forallocate_object,deallocate_object,new_object,delete_object, orconstruct, because their semantics does not depend on template parameterTpand the general wording of 16.4.5.8 [res.on.functions] (2.5) again requires the completeness ofThere.deallocatewe also omit the completeness requirement (as we did so forallocator::deallocate), because this is indirectly implied by the existing precondition. ]
Section: 16.4.5.3.4 [extern.names] Status: New Submitter: Alisdair Meredith Opened: 2023-04-12 Last modified: 2023-05-24
Priority: 3
View all issues with New status.
Discussion:
16.4.5.3.4 [extern.names] p1 and p2 reserve certain names with external linkage to the implementation.
Each paragraph has a footnote, stating that such names includeerrno, setjmp(jmpbuf), and
va_end(va_list). These names are all defines as macros, not entities with that name and external linkage.
Are these footnotes intended by a normative extension to the list of reserved names with external linkage?
If so, they should be promoted to the main text, as they are no longer a note.
If they intend to serve as examples, it is not clear to me what principle is being shown, as there are many
other macros that might be in a similar position, and I do not see how to follow the a principle that is being
noted, and presumably follows from the normative text.
I think this is intended to be a normative extension to the set of reserved names with external linkage,
is limited to exactly those three named macros, so should be promoted as a non-note into the main text, but want
to hear what LWG consensus is before drafting wording.
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
std::chrono::duration<std::int64_t, std::ratio<INT64_MAX - 1, INT64_MAX>>{40}
required to be correctly formatted?Section: 30.12 [time.format] Status: New Submitter: Jiang An Opened: 2023-04-14 Last modified: 2023-05-24
Priority: 3
View other active issues in [time.format].
View all other issues in [time.format].
View all issues with New status.
Discussion:
Currently none of MSVC STL, libc++, and libstdc++ correctly formats
duration<int64_t, ratio<INT64_MAX -1, INT64_MAX>>{40}
(Godbolt link). For MSVC and libstdc++,
hh_mm_ss is used when formatting duration and thus duration_cast
is also involved. And it's extremely easy for duration_cast to cause UB when the
source type is duration<int64_t, ratio<INT64_MAX -1, INT64_MAX>>.
hh_mm_ss is equivalently used when
formatting duration. And it seems that the current wording require duration (whose
rep type is an arithmetic type) to be correctly formatted without UB, even if it is
of a weird type or extremely large (e.g. when it is
duration<int64_t, ratio<INT64_MAX, 1>>{INT64_MAX}).
So, if it is intended that hh_mm_ss is used for formatting duration,
perhaps we should explicitly so specify in 30.12 [time.format]. And we may need to
fix initialization of hh_mm_ss to reduce UB whenever suitable.
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
"NAD. A compile-time error is required when ratio arithmetic overflows, 21.5.4 [ratio.arithmetic] p2"
Proposed resolution:
numeric_limits can be specialized by usersSection: 17.3.5.1 [numeric.limits.general] Status: New Submitter: Christopher Di Bella Opened: 2023-04-14 Last modified: 2024-07-31
Priority: 3
View other active issues in [numeric.limits.general].
View all other issues in [numeric.limits.general].
View all issues with New status.
Discussion:
16.4.5.2.1 [namespace.std]/p2 notes that "unless explicitly prohibited", a user may conditionally specialize
any class template for program-defined types. 17.3.5.1 [numeric.limits.general]/p1 doesn't explicitly prohibit
this, but it does create a bit of a grey area with its wording because it describes numeric_limits as a
class template describing "the implementation's representation" of "the arithmetic types".
ns::int256. ns::int256 isn't an
arithmetic type (despite resembling one), so one can interpret 16.4.5.2.1 [namespace.std]/p2's (b) condition to
mean that numeric_limits<ns::int256> needs to be explicitly specialized as if it were the primary
template.
Daniel:
This issue has overlap with LWG 3923(i).
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
Several votes for Tentatively Ready with Option A, but a request to consider LWG 3923(i) first/instead of this one.
Proposed resolution:
This wording is relative to N4944.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A: (This assumes that the above described scenario is intended to be supported)
Add the following new paragraph at the end of 17.3.5.1 [numeric.limits.general] as indicated:
[Drafting Note: The particular wording form "emulating an arithmetic type" has been borrowed from Table 99 — Cpp17Clock requirements [tab:time.clock] and 30.5.1 [time.duration.general] p2, respectively.]
-?- The
numeric_limitstemplate may be specialized for program-defined types emulating arithmetic types.
Option B: (This assumes that the above described scenario is not intended to be supported)
Add the following new paragraph at the end of 17.3.5.1 [numeric.limits.general] as indicated:
[Drafting Note: The particular wording form has been borrowed from 17.13.4.1 [coroutine.handle.general] p2 and 20.2.9.1 [allocator.traits.general] p1, respectively.]
-?- If a program declares an explicit or partial specialization of
numeric_limits, the program is ill-formed, no diagnostic required.
numeric_limits doesn't clearly distinguish between implementation requirements
and user requirementsSection: 17.3.5.1 [numeric.limits.general] Status: New Submitter: Daniel Krügler Opened: 2023-04-15 Last modified: 2023-05-24
Priority: 3
View other active issues in [numeric.limits.general].
View all other issues in [numeric.limits.general].
View all issues with New status.
Discussion:
The wording of 17.3.5.1 [numeric.limits.general] seemingly has not been gone through a similar thorough rewording review which we performed in the past to clean-up the working draft as we did via the series of "Mandating" papers by Marshall Clow (P1458 - P1465 and even more).
17.3.5.1 [numeric.limits.general] contains several nowadays inappropriate wording forms, which don't distinguish well enough between requirements imposed on implementations (Where we shouldn't use "shall" wording in the ambiguous form of "Specializations shall be provided for each arithmetic type") and requirements imposed on user types, this has also caused confusion as expressed in LWG 3922(i). It is "obvious" that a program is intended to be allowed to provide program-defined specializations, but as LWG 3922(i) points out, it is unclear how such a specialization is able to meet the requirement "(b) the specialization meets the standard library requirements for the original template" specified in 16.4.5.2.1 [namespace.std] p2. Another problem is the usage of the unclear wording "Non-arithmetic standard types", which should be replaced by a more precise wording form. An additional problem is that we actually already do require an implementation to provide specializations for the (library-provided) integer-class types (24.3.4.4 [iterator.concept.winc]), so contrary to what p6 says, we already have at least one exception, where the library is required to specializenumeric_limits
for a non-arithmetic type. We should make that a bit clearer here as well.
This issue is related to LWG 3922(i) and paper P1841.
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4944.
[Drafting Note: This wording would also solve LWG issue 3922(i) under the assumption that option A is intended]
Modify 17.3.3 [limits.syn], header <limits> synopsis, as indicated:
[…] // 17.3.5.1 [numeric.limits.general], class template numeric_limits template<class T> class numeric_limits; // 17.3.5.3 [numeric.special], numeric_limits specializations template<class T> class numeric_limits<const T>; template<class T> class numeric_limits<volatile T>; template<class T> class numeric_limits<const volatile T>; template<> class numeric_limits<bool>; […]
Modify 17.3.5.1 [numeric.limits.general] as indicated:
-1- The
[…]numeric_limitsclass template provides a C++ program with information about various properties of the implementation's representation of the arithmetic types.[Drafting Note: It is unclear whether the requirement in the following paragraph 2 is intended to apply to program-defined specializations as well. Consider as an example a user-defined arithmetic-like type that provides arbitrary precision arithmetic which may require dynamic memory for certain object constructions. Is it invalid to specialize
If we want to make this restriction relaxed for program-defined specializations, further wording would be needed to give that permission]numeric_limitsfor such a type or may the program-defined specialization deviate from this requirement for at least some of its members?-2- For all members declared
-3- For thestatic constexprin thenumeric_limitstemplate, specializations shall define these values in such a way that they are usable as constant expressions.numeric_limitsprimary template, all data members are value-initialized and all member functions return a value-initialized object. [Note 1: This means all members have zero orfalsevalues unlessnumeric_limitsis specialized for a type. — end note] -4- An implementation shall provide sSpecializationsshall be providedfor each arithmetic type, both floating-point and integer, includingbool. The memberis_specializedshall beistruefor all such specializations ofnumeric_limits.-5- The value of each member of a specialization ofnumeric_limitson a cv-qualified typecv Tshall be equal to the value of the corresponding member of the specialization on the unqualified typeT.[Drafting Note: If we had introduced
Interestingly currently there doesn't exist a specification that defines under which situations the static membernumeric_limitstoday we would likely have only allowed to provide specializations for cv-unqualified program-defined types, but that ship has sailed long ago.is_specializedshould be definedtrueorfalse(This touches LWG 205(i)). The wording below does not attempt to improve that situation, but at least clarifies that its value may be different from that of the primary template. Note that this proposed wording does — opposed to the approach of LWG 3922(i) — not restrict that specializations can only be provided by program-defined types "emulating an arithmetic type", because that would break existing specializations and is also problematic in the light of the non-existing definition of that term. The below wording strategy gives permission to specializenumeric_limitsonly for non-array object types. An alternative approach could make it a precondition instead to instantiate the template for non-array object types, for example.]-?- A program may specialize the
[Note: It still has to meet the general requirements specified in subclause 17.3.5.1 [numeric.limits.general] and subclause 17.3.5.3 [numeric.special] — end note].numeric_limitstemplate for a program-defined non-array object type. Such a specialization is permitted to define a value for any static member that differs from what the primary template would have defined, as appropriate for that type.[Drafting Note: The following restriction is carefully drafted to ensure that a library has the freedom to provide such specializations for "extended" types (That are not necessarily integer-class types). The restriction is intended to apply only to "official" (strict) C++ standard library types]
-6- An implementation shall not provide specializations for n
Non-arithmeticstandardtypes of the C++ standard library, such ascomplex<T>(29.4.3 [complex]), unless specified otherwise (e.g. for integer-class types, 24.3.4.4 [iterator.concept.winc]), shall not have specializations.
Modify 17.3.5.3 [numeric.special] as indicated:
[Drafting Note: I have left the "shall" usage in p1, because this seems to be a requirement for program-defined specializations as well. The second sentence of p1 is one of the funny ones which partially look like introductory wording, but also seems to indicate requirements, albeit specified in an unusual way ("meaningful").
The extra wording added after p2 below attempts to improve the wording situation caused by LWG 559(i) and does that by following a similar approach as done in 22.4.7 [tuple.helper]. ]
-1- All members shall be provided for all specializations. However, many values are only required to be meaningful under certain conditions (for example,
-2- [Example 1: […] — end note]epsilon()is only meaningful ifis_integerisfalse). Any value that is not "meaningful" shall be set to0orfalse.template<class T> class numeric_limits<const T>; template<class T> class numeric_limits<volatile T>; template<class T> class numeric_limits<const volatile T>;-?- Let
-3- The specialization forNLdenotenumeric_limits<T>of the cv-unqualified typeT. Then the value of each member of these specializations ofnumeric_limitsis equal to the value of the corresponding member of the specializationNL.boolshall beis provided as follows: […]
Section: 32.3.1 [thread.stoptoken.intro] Status: New Submitter: Brian Bi Opened: 2023-04-16 Last modified: 2023-05-24
Priority: 3
View all issues with New status.
Discussion:
The first sentence of 32.3.1 [thread.stoptoken.intro]/5 says:
Calls to the functions
request_stop,stop_requested, andstop_possibledo not introduce data races.
This could be read as saying that if you have a program that doesn't contain data races,
and you change it by adding a call to any of these three functions, such a change does not
"introduce" data races into the program. In other words, it could be read as saying that
these three functions don't race with any other member functions on the same
stop_token or stop_source object.
stop_token::operator= were also required to not race. (Also, none of these functions
can avoid racing with the destructor.)
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
"What about get_token? operator==?"
"Other functions like get_stop_token can also be called
concurrently. I think the text is actually correct."
Proposed resolution:
This wording is relative to N4944.
Modify 32.3.1 [thread.stoptoken.intro] as indicated:
-5- Calls to the functions
request_stop,stop_requested, andstop_possibledo not introduce data races with each other. A call torequest_stopthat returnstruesynchronizes with a call tostop_requestedon an associatedstop_tokenorstop_sourceobject that returnstrue. Registration of a callback synchronizes with the invocation of that callback.
std is the mentioned one?Section: 16.4.5.2.1 [namespace.std] Status: New Submitter: jim x Opened: 2023-04-19 Last modified: 2023-05-24
Priority: 4
View other active issues in [namespace.std].
View all other issues in [namespace.std].
View all issues with New status.
Discussion:
16.4.5.2.1 [namespace.std] p1 says
Unless otherwise specified, the behavior of a C++ program is undefined if it adds declarations or definitions to namespace
stdor to a namespace within namespacestd.
Which std namespace does the rule intend to refer to? Does the text refer to any namespace named std?
Consider this case:
namespace A{
namespace std{ // UB or not?
void show(){}
namespace B{
void fun(){} // UB or not?
}
}
}
int main(){
}
A resemble usage like the above example can be seen in a libcudacxx github code, for example.
Suggested resolution: We may want to say the namespacestd only refers to the namespace whose declaration inhabits the global scope.
16.4.5.2.1 [namespace.std] p2, p7 that refers to namespace std have a similar issue.
[2023-04-24; Ville provides wording]
[2023-05-24; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
This wording is relative to N4944.
Modify 16.4.5.2.1 [namespace.std] as indicated:
-?- The restrictions on the use of namespace
-1- Unless otherwise specified, the behavior of a C++ program is undefined if it adds declarations or definitions to namespacestdapply only to a top-level namespace namedstd. Namespaces namedstdnested in program-defined namespaces are not restricted this way or otherwise reserved.stdor to a namespace within namespacestd.
posix shouldn't be reservedSection: 16.4.5.2.2 [namespace.posix] Status: New Submitter: Jiang An Opened: 2023-04-29 Last modified: 2023-05-24
Priority: 3
View all issues with New status.
Discussion:
The namespace posix is made reserved by N2667.
::posix is intendedly reserved,
but the current normative wording is not clear enough on this (see also LWG 3926(i)).
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4944.
Modify 16.4.5.2.2 [namespace.posix] as indicated:
-1- The behavior of a C++ program is undefined if it adds declarations or definitions to top-level namespace
posixor to a namespace within that namespaceunless otherwise specified. The top-level namespaceposixposixis reserved for use by ISO/IEC/IEEE 9945 and other POSIX standards.
Section: 21.3.6.4 [meta.unary.prop], 21.3.8 [meta.rel] Status: New Submitter: Alisdair Meredith Opened: 2023-05-01 Last modified: 2023-06-12
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with New status.
Discussion:
Since we have adopted the Constraints/Mandates/Preconditions form of wording, "preconditions" refer to runtime requirements, not compile-time. As such, the column labeled "preconditions" in Table 47: Type property predicates [tab:meta.unary.prop] would be better labeled as "Mandates".
This is an LWG issue and not editorial, and "Mandates" would require the library to diagnose violations, but after reviewing all traits in this table, I believe that is reasonable. Table 48: Type property queries [tab:meta.unary.prop.query] shows how Mandates: elements can be integrated into the "Value" column if we preferred that approach, but for the number of entries in the first table seems like an aggressive change for consistency. Similarly, for Table 49: Type relationship predicates [tab:meta.rel] the "Comments" column serves as a "Mandates" feature without using that term, so I suggest changing that column title too. The other tables and type traits wording already appear to be adapted to the Mandates wording style.[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
See also issue 2939(i). We should not turn the preconditions into Mandates without fixing them first.
[2023-06-12; Varna]
During the review P2874R1 the group decided to not change the now decided for Preconditions: element in D.13 [depr.meta.types] p3 into a Mandates: element but would like to solve this by this issue.
Proposed resolution:
This wording is relative to N4944.
Change in 21.3.6.4 [meta.unary.prop], Table 47: Type property predicates [tab:meta.unary.prop] the column title "Preconditions" to "Mandates".
Change in 21.3.8 [meta.rel], Table 49: Type relationship predicates [tab:meta.rel] the column title "Comments" to "Mandates".
__cpp_lib_rangesSection: 17.3.2 [version.syn] Status: New Submitter: Jiang An Opened: 2023-05-03 Last modified: 2024-07-27
Priority: 3
View other active issues in [version.syn].
View all other issues in [version.syn].
View all issues with New status.
Discussion:
Currently MSVC STL implements P2602R2 and P2609R3 in C++20 mode
as if they were defect reports. However, since P2387R3 and P2494R2,
which are possibly considered pure functionality extensions in C++23, also bump __cpp_lib_ranges,
it's impossible to detect the status of P2602R2 and P2609R3 in C++20 mode (see
the discussion in MSVC STL repo).
__cpp_lib_ranges_mechanism) for them.
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
"Needs a more descriptive name than mechanism."
Previous resolution [SUPERSEDED]:
This wording is relative to N4944.
Modify 17.3.2 [version.syn] as indicated:
[Drafting note: It is proposed to restore
__cpp_lib_rangesto the value denoting P2494R2.][…] #define __cpp_lib_ranges202302L202207L […] #define __cpp_lib_ranges_join_with 202202L // also in <ranges> #define __cpp_lib_ranges_mechanism 202302L // also in <algorithm>, <functional>, <iterator>, <memory>, <ranges> #define __cpp_lib_ranges_repeat 202207L // also in <ranges> […]
[2024-07-24; Jiang An comments and provides improved wording]
All utilities changed by P2602R2, P2609R3, and P2997R1 are now freestanding, so these proposed macros should also be freestanding.
Proposed resolution:
This wording is relative to N4986.
Modify 17.3.2 [version.syn] as indicated:
[Drafting notes:
It is proposed to restore
__cpp_lib_rangesto the value denoting P2494R2.
__cpp_lib_ranges_accessorswill cover P2602R2.
__cpp_lib_ranges_indirect_invocationwill cover P2609R3 and P2997R1.None of P2602R2, P2609R3, and P2997R1 really affected utilities in
<functional>, so these two proposed macros are not required to be also in<functional>.Proposed macros are freestanding because utilities changed by these papers are all freestanding now.
]
[…] #define __cpp_lib_ranges202406L202207L // also in <algorithm>, <functional>, <iterator>, <memory>, <ranges> #define __cpp_lib_ranges_accessors 202211L // freestanding, also in <algorithm>, <iterator>, <memory>, <ranges> […] #define __cpp_lib_ranges_generate_random 202403L // also in <random> #define __cpp_lib_ranges_indirect_invocation 202406L // freestanding, also in <algorithm>, <iterator>, <memory>, <ranges> #define __cpp_lib_ranges_iota 202202L // also in <numeric> […]
Section: 17.12.6 [cmp.alg] Status: New Submitter: Jiang An Opened: 2023-05-04 Last modified: 2023-05-24
Priority: 3
View other active issues in [cmp.alg].
View all other issues in [cmp.alg].
View all issues with New status.
Discussion:
Currently, comparison CPOs may call ADL-found strong_order, weak_order, and partial_order
functions. It might be impossible to meet the expression-equivalent requirement if one passed argument is a prvalue
and an ADL-found function is selected, because temporary materialization must take place before entering the CPO's
operator().
E or F is a prvalue expression of an object type, temporary
materialization takes place first, and then an xvalue referring to the temporary object is used instead of the original
E or F.
There is currently implementation divergence in these CPOs: when performing internal comparison, libstdc++ and libc++
perfect-forward arguments, while MSVC STL always treats arguments as lvalues.
[2023-05-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
std::complexSection: 29.4.3 [complex] Status: New Submitter: Jiang An Opened: 2023-05-16 Last modified: 2023-06-01
Priority: 4
View other active issues in [complex].
View all other issues in [complex].
View all issues with New status.
Discussion:
In C++20 and earlier revisions, there are constructors taking two floating-point numbers by value in explicit
specializations of std::complex for standard floating-point types. Since P1467R9 has
removed these explicit specializations, the corresponding constructor in the primary template that takes arguments
by const T& are used instead. As a result, the following program becomes ill-formed after the changes.
#include <complex>
int main()
{
volatile double x = 0.0;
std::complex<double> z{x, x}; // ill-formed due to P1467R9 because const double& cannot be bound to a volatile double lvalue
}
Currently, libstdc++ has implemented complex specializations for extended floating-point types, but the corresponding constructors of these specializations takes two arguments by value, which is consistent with old specializations.
It seems that it's unintended to change the signatures of these constructors. Perhaps we should restore the signatures for required specializations.Daniel:
Not only constructors are affected, but also all assignment operators taking thevalue_type as
parameter and I suggest that LEWG should have a look at this issue.
[2023-05-20; Daniel comments and suggests wording]
The wording below attempts to restore the exact previous behaviour: For all floating-point types the
function parameter types are "by value" and for other types are "by const reference". The wording adds for
specification purposes a dependency to the concept std::floating_point, but that doesn't mean that an
implementation couldn't realize the required effects without usage of concepts or the <type_traits>
header.
iterator_traits specialization for
pointers (to object) or the additional constraints of set's member function
iterator erase(const_iterator position) or the constraint for reverse_iterator::operator->(),
just to name a few.
One alternative approach could be to switch to "by-value" signatures only for the affected signatures. This could
affect user-defined floating-point-like types such as those with an arbitrary precision, therefore I started with
the most conservative approach restoring the original effects that was present in the
working draft N4910 and older ones. It might we worth pointing out that the existing "setter"
functions imag and real have always been using "by-value" signatures for all specializations.
There exists also the possible argument to close this issue as NAD based on the argument that all existing
non-member operators taking a value_type argument had always been defined to use const T&
as parameter (such as the operator@(const T& lhs, const complex<T>& rhs) forms).
My main argument to solve this issue as shown below is based on the ground that the refactoring done by
P1467R9 was mainly inspired to simplify the existing wording and to make it more easy to
integrate the addition of the extended floating-point types here, as quoted from
P1467R9 section 6.6. <complex>:
[…] The explicit specializations of std::complex<T> are removed. The only differences between
the explicit specializations was the explicit-ness of the constructors that take a complex number of a different type.
This issue has some overlap with LWG 3934(i), which suggests a yet missing specification for
the assignment operator taking the value_type as parameter.
[2023-06-01; Reflector poll]
Set priority to 4 after reflector poll.
Several votes for NAD, as this only affects volatile arguments,
so this might even be an accidental improvement.
Proposed resolution:
This wording is relative to N4950.
Modify 29.4.3 [complex], class template complex synopsis, as indicated:
namespace std {
template<class T> class complex {
public:
using value_type = T;
constexpr complex(T re = T(), T im = T()) requires floating_point<T>;
constexpr complex(const T& re = T(), const T& im = T()) requires (!floating_point<T>);
[…]
constexpr complex& operator= (T) requires floating_point<T>;
constexpr complex& operator= (const T&) requires (!floating_point<T>);
constexpr complex& operator+=(T) requires floating_point<T>;
constexpr complex& operator+=(const T&) requires (!floating_point<T>);
constexpr complex& operator-=(T) requires floating_point<T>;
constexpr complex& operator-=(const T&) requires (!floating_point<T>);
constexpr complex& operator*=(T) requires floating_point<T>;
constexpr complex& operator*=(const T&) requires (!floating_point<T>);
constexpr complex& operator/=(T) requires floating_point<T>;
constexpr complex& operator/=(const T&) requires (!floating_point<T>);
[…]
};
}
Modify 29.4.4 [complex.members] as indicated:
constexpr complex(T re = T(), T im = T()) requires floating_point<T>; constexpr complex(const T& re = T(), const T& im = T()) requires (!floating_point<T>);-1- Postconditions:
real() == re && imag() == imistrue.
Modify 29.4.5 [complex.member.ops] as indicated:
[Drafting note: We have an pre-existing specification hole that the effects of the non-compound assignment operator taking the
value_typeas parameter are nowhere specified. This is going to be submitted as a separate issue, see LWG 3934(i).]
constexpr complex& operator+=(T rhs) requires floating_point<T>; constexpr complex& operator+=(const T& rhs) requires (!floating_point<T>);-1- Effects: Adds the scalar value
-2- Returns:rhsto the real part of the complex value*thisand stores the result in the real part of*this, leaving the imaginary part unchanged.*this.constexpr complex& operator-=(T rhs) requires floating_point<T>; constexpr complex& operator-=(const T& rhs) requires (!floating_point<T>);-3- Effects: Subtracts the scalar value
-4- Returns:rhsfrom the real part of the complex value*thisand stores the result in the real part of*this, leaving the imaginary part unchanged.*this.constexpr complex& operator*=(T rhs) requires floating_point<T>; constexpr complex& operator*=(const T& rhs) requires (!floating_point<T>);-5- Effects: Multiplies the scalar value
-6- Returns:rhsby the complex value*thisand stores the result in*this.*this.constexpr complex& operator/=(T rhs) requires floating_point<T>; constexpr complex& operator/=(const T& rhs) requires (!floating_point<T>);-7- Effects: Divides the scalar value
-8- Returns:rhsinto the complex value*thisand stores the result in*this.*this.
std::complex<T>::operator=(const T&) has no specificationSection: 29.4.3 [complex] Status: New Submitter: Daniel Krügler Opened: 2023-05-20 Last modified: 2023-06-01
Priority: 3
View other active issues in [complex].
View all other issues in [complex].
View all issues with New status.
Discussion:
The class template complex synopsis in 29.4.3 [complex] shows the following member function:
constexpr complex& operator= (const T&);
but does not specify its semantics. This affects a code example such as the following one:
#include <complex>
#include <iostream>
int main()
{
std::complex<double> z(1, 1);
z = 2;
std::cout << z << std::endl;
}
This problem exists since the 1998 version of the standard (at that time this was declared in subclause [lib.complex]),
but fortunately the three major implementations all behave consistently by assigning the provided value to the real
part and nullifying the imaginary part, causing the output (2, 0), which is consistent with the expected
behaviour of usual mathematical convention and that of C's built-in complex types. We should specify this.
[2023-06-01; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4950.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
[Drafting note: The wording forms used below intentionally deviate from the rest of the [complex.member.ops] wording forms, because it seems much simpler and clearer to follow the wording forms used that specify the effects of
imagandrealfunctions. I decided to use "part" instead of "component", which is shorter and more often used in the rest of the specification]
Option A: This assumes that LWG 3933(i) is considered as NAD and just adds the missing prototype specification assuming that the parameter style of the current working draft is intended.
Add a new prototype specification at the very beginning of 29.4.5 [complex.member.ops] as indicated:
constexpr complex& operator=(const T& rhs);-?- Effects: Assigns the value
-?- Returns:rhsto the real part and the valueT()to the imaginary part of the complex value*this.*this.constexpr complex& operator+=(const T& rhs);[…]
Option b: This assumes that LWG 3933(i) will be resolved as initially presented and just adds the missing prototype specification assuming that the parameter style suggesting two mutually excluded overloads is intended. The wording delta is presented against the proposed wording of LWG 3933(i).
Add a new prototype specification at the very beginning of 29.4.5 [complex.member.ops] as indicated:
constexpr complex& operator=(T rhs) requires floating_point<T>; constexpr complex& operator=(const T& rhs) requires (!floating_point<T>);-?- Effects: Assigns the value
-?- Returns:rhsto the real part and the valueT()to the imaginary part of the complex value*this.*this.constexpr complex& operator+=(T) requires floating_point<T>; constexpr complex& operator+=(const T&) requires (!floating_point<T>);[…]
Section: 31.7.7 [std.manip], 31.7.8 [ext.manip], 31.7.9 [quoted.manip] Status: New Submitter: Jonathan Wakely Opened: 2023-05-25 Last modified: 2023-06-01
Priority: 3
View other active issues in [std.manip].
View all other issues in [std.manip].
View all issues with New status.
Discussion:
All the manipulators in 31.7.7 [std.manip] and 31.7.8 [ext.manip] use language of the form:
An object of unspecified type such that ifoutis an object of typebasic_ostream<charT, traits>then the expressionout << resetiosflags(mask)behaves as if [...]
This needs to work for any object with a public and unambiguous base class
of type basic_ostream<charT, traits>, not only objects
of exactly that type.
In C++98 and C++03, setfill did actually get this right
("if out is (or is derived from)
basic_ostream<charT,traits>"),
but that was "fixed" for C++11 to be consistent with the other manipulators.
31.7.9 [quoted.manip] is slightly different, talking about
"an instance of basic_ostream", but that's not right either.
[2023-06-01; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
char is not formatted as a character when charT is wchar_tSection: 28.5.2.2 [format.string.std] Status: New Submitter: S. B. Tam Opened: 2023-05-26 Last modified: 2023-06-01
Priority: 3
View other active issues in [format.string.std].
View all other issues in [format.string.std].
View all issues with New status.
Discussion:
(See discussion at microsoft/STL/pull/3723)
28.5.2.2 [format.string.std] p21 says:The available integer presentation types for integral types other than
boolandcharTare specified in Table 68.
When charT is wchar_t, the ordinary character type char falls into this category, and thus a
char gets formatted as an integer by default, not as a character.
std::format family, because they are specified in terms of make_format_args,
which calls the basic_format_arg constructor, which converts char to wchar_t
(28.5.8.1 [format.arg] (6.2)). But it does affect the std::formatter<char, wchar_t> specialization,
which isn't specified to use basic_format_arg.
This is especially problematic after P2286R8, which makes std::formatter<char, wchar_t>
debug-enabled, but there's no debug format for integral types other than charT.
Perhaps [format.string.std] should say that the formatting arguments are converted as if through the basic_format_arg
constructor.
At the time of writing, on libstdc++ and libc++, std::formatter<char, wchar_t> formats the argument as
a character when no specifier is given (godbolt.org/z/nnsEcvna3), while
MSVC STL's std::formatter<char, wchar_t> outputs the integer value. But I'm about to change MSVC STL to
match the other implementations.
[2023-06-01; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 32.5.4 [atomics.order] Status: Open Submitter: Hans Boehm Opened: 2023-05-27 Last modified: 2025-02-10
Priority: 3
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with Open status.
Discussion:
[Discovered by Ori Lahav, lead author of Repairing Sequential Consistency in C/C++11, which P0668 more or less tried to implement. This description also includes observations from Olivier Giroux and David Goldblatt.]
Currently, we require that the sequential consistency order S is consistent with the extended coherence order, which includes "reads before" and "reads from". This may require sc reads to be consistently ordered even if they see non-sc writes. This is not enforced by some "fence-after-store"/"trailing fence" sc implementations, notably on X86. It may also be violated by compiler transformations that eliminate a sequentially consistent load after a store into the same location, e.g. on sequentially consistent hardware. As Ori pointed out, this is exhibited by their (SB + rfis) example on x86 reproduced below. I would expect this also happens on other platforms. The (SB + rfis) example:Thread 1: x.store(1, mo_release); r1 = x.load() /* sees 1 */; r2 = y.load() /* sees 0 */ Thread 2: y.store(1, mo_release); r1 = y.load() /* sees 1 */; r2 = x.load() /* sees 0 */
This is unintentionally and unexpectedly disallowed by the current standard. We can see this as follows:
We have the following coherence-ordered-before relationships:By 3.1 in 32.5.4 [atomics.order] (reads-from): The store in each thread is coherence-ordered-before the immediately following load.
By 3.3 (reads-before): The final load in each thread is coherence-ordered before the store in the other thread.
By 3.4 (transitivity): The final load in each thread is coherence-ordered-before the initial load in the other thread.
On the other hand, the initial load in each thread is sequenced-before and thus strongly-happens-before 6.10.2.2 [intro.races] p12.1 the final load in that thread. Thus coherence-ordered-before union strongly-happens-before is cyclic. But the S relation 32.5.4 [atomics.order] p4 must order all the loads, consistent with both strongly-happens-before (p4) and coherence-ordered-before (p4.1), which is clearly impossible.
With the standard x86 implementation, no fences are inserted. Thread 1 and Thread 2 see the stores tox and
y in inconsistent orders, which is allowed by TSO, since each thread can see its own write early. Thus the
implementation allows the outcome in question, as expected.
The problem here is an error during the attempt to simplify and translate
plv.mpi-sws.org/scfix/paper.pdf to standardese. There is no known
issue with the underlying paper.
I have not yet wrapped my head around this sufficiently to be able to suggest a solution, or to understand how difficult
that will be. This happened as a consequence of attempting to strengthen SC fences, and not fixing the Power
compilation problem (P0668). So we could probably improve matters by reverting the strengthening of sc fences.
Clearly, we'd prefer to avoid that. We could just adopt the formulation in the paper, though that seems even harder to motivate
informally. Fundamentally, it feels like we inadvertently strengthened (and broke) the semantics of ordinary sc operations,
when we intended to only affect fences. The original paper makes more of a distinction there. The question is how we can
reformulate that suitably here.
[ Meta-observation from a small off-line discussion that may be relevant to how we phrase the resolution here: The fact that
nobody noticed this for a very long time, and implementers were not bothered by it, suggests that the audience for this part
of the standard is nearly empty. We conjecture that implementers actually rely on atomics mappings generated by memory model
experts, who are more interested in formal models than standardese. A more formal description is likely to increase the size
of the audience, and would definitely ease verification and reduce the probability of mistakes like this.]
[2023-06-01; Reflector poll]
Set priority to 3 after reflector poll. Send to SG1.
[2025-02-10; SG1 reviewed the issue in March 2024 ]
"we want to address LWG3941 in a way that preserves existing implementations like X86" - unanimous consent.
Proposed resolution:
const char_type& in standard specializations of std::char_traitsSection: 27.2.4 [char.traits.specializations] Status: New Submitter: Jiang An Opened: 2023-05-27 Last modified: 2023-06-01
Priority: 3
View all other issues in [char.traits.specializations].
View all issues with New status.
Discussion:
In the standard specializations of std::char_traits (std::char_traits<char> etc.), there
are a few member functions take a single character via a const char_type& parameter, while other functions
take a single character by value.
const char_type&. N2349 changed this in
C++11 by making some, but not all of them take char_type. It is unclear whether the inconsistency is
intended, and it seems better for these standard specializations to take a character by value.
However, libstdc++ hasn't implemented the signature changes, perhaps due to ABI concerns. So it might be better
to loose the restrictions for the purpose of standardization.
[2023-06-01; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4950.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A: This assumes that "by-value" arguments are intended.
Modify the class template char_traits specialization synopses in 27.2.4.2 [char.traits.specializations.char],
27.2.4.3 [char.traits.specializations.char8.t], 27.2.4.4 [char.traits.specializations.char16.t],
27.2.4.5 [char.traits.specializations.char32.t], and 27.2.4.6 [char.traits.specializations.wchar.t]
as indicated:
[…]
static constexpr void assign(char_type& c1, const char_type& c2) noexcept;
[…]
static constexpr const char_type* find(const char_type* s, size_t n,
const char_type& a);
[…]
Option B: This assumes that implementation-freedom to keep ABI stability is intended.
[Drafting Note: It is intended to keep the
assign(s, n, a)taking the character by value, because the argument may be a character in[s, s + n).]
Add a paragraph at the end of 27.2.4.1 [char.traits.specializations.general] as indicated:
-?- For each occurrence of the placeholder
const-char-tin the synopsis of each of these specializations, it is unspecified whether it denoteschar_typeorconst char_type&. Likewise, for each occurrence of the placeholderconst-int-tin the synopsis of each of these specializations, it is unspecified whether it denotesint_typeorconst int_type&
Modify the class template char_traits specialization synopses in 27.2.4.2 [char.traits.specializations.char],
27.2.4.3 [char.traits.specializations.char8.t], 27.2.4.4 [char.traits.specializations.char16.t],
27.2.4.5 [char.traits.specializations.char32.t], and 27.2.4.6 [char.traits.specializations.wchar.t]
as indicated:
[…]
static constexpr void assign(char_type& c1, const char_type&const-char-t c2) noexcept;
static constexpr bool eq(char_typeconst-char-t c1, char_typeconst-char-t c2) noexcept;
static constexpr bool lt(char_typeconst-char-t c1, char_typeconst-char-t c2) noexcept;
[…]
static constexpr const char_type* find(const char_type* s, size_t n,
const char_type&const-char-t a);
[…]
static constexpr int_type not_eof(int_typeconst-int-t c) noexcept;
static constexpr char_type to_char_type(int_typeconst-int-t c) noexcept;
static constexpr int_type to_int_type(char_typeconst-char-t c) noexcept;
static constexpr bool eq_int_type(int_typeconst-int-t c1, int_typeconst-int-t c2) noexcept;
[…]
Section: 28.5.6.3 [format.formattable] Status: New Submitter: Mark de Wever Opened: 2023-06-01 Last modified: 2023-06-08
Priority: 3
View other active issues in [format.formattable].
View all other issues in [format.formattable].
View all issues with New status.
Discussion:
A bug has been
filed against libc++'s format implementation. The question arose whether
the parsed chrono-specs should still be available during executing the
formatter's format member function. Libc++'s implementation requires
this MSVC STL's implementation does not.
format function may use elements in the range [pc.begin(),
pc.end()) that have not been parsed. This does not seem to be in the
spirit of the formatting library. The wording change proposes to
restrict this range to the parsed range.
[2023-06-08; Reflector poll]
Set priority to 3 after reflector poll.
The proposed resolution is missing a guarantee that the parsed portion of the
range is unchanged between the calls to parse and
format. It must remain valid and unchanged.
Proposed resolution:
This wording is relative to N4950.
Modify BasicFormatter requirements [tab:formatter.basic] as indicated:
Table 73: BasicFormatter requirements [tab:formatter.basic] Expression Return type Requirement …f.format(u, fc)FC::iteratorFormats uaccording to the specifiers stored in
*this, writes the output tofc.out(), and returns
an iterator past the end of the output range.
The output shall only depend onu,fc.locale(),
fc.arg(n)for any valuenof typesize_t, and
the elements in the parsed range of[pc.begin(), pc.end())
from the last call tof.parse(pc). [Note ?: Using elements in the parsed range of[pc.begin(),allows the formatter to store references, pointers, or
pc.end())
iterators to elements in the parsed range. For example,
formatter<chrono::day>might store the parsed chrono-specs
(30.12 [time.format]) in abasic_string_view. — end note]
Modify Formatter requirements [tab:formatter] as indicated:
Table 74: Formatter requirements [tab:formatter] Expression Return type Requirement f.format(t, fc)FC::iteratorFormats taccording to the specifiers stored in
*this, writes the output tofc.out(), and returns
an iterator past the end of the output range.
The output shall only depend ont,fc.locale(),
fc.arg(n)for any valuenof typesize_t, and
the elements in the parsed range of[pc.begin(), pc.end())
from the last call tof.parse(pc). [Note ?: Using elements in the parsed range of[pc.begin(),allows the formatter to store references, pointers, or
pc.end())
iterators to elements in the parsed range. For example,
formatter<chrono::day>might store the parsed chrono-specs
(30.12 [time.format]) in abasic_string_view. — end note]…
Section: 17.14.2 [cstdarg.syn] Status: New Submitter: Lukas Barth Opened: 2023-06-08 Last modified: 2023-06-12
Priority: 3
View other active issues in [cstdarg.syn].
View all other issues in [cstdarg.syn].
View all issues with New status.
Discussion:
Subclause 17.14.2 [cstdarg.syn] p1 bullet (1.2) states (among other things):
[…] If the parameter
parmNis of a reference type, or of a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.
Unlike the C standard, the C++ standard has no notion of 'compatible types', thus the meaning of this sentence unclear.
[2023-06-12; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
iter_common_reference_t does not conform to the definition of indirectly_readableSection: 24.2 [iterator.synopsis], 24.5.3.2 [const.iterators.alias], 24.5.3.3 [const.iterators.iterator] Status: New Submitter: Hewill Kang Opened: 2023-06-28 Last modified: 2023-10-30
Priority: 3
View all other issues in [iterator.synopsis].
View all issues with New status.
Discussion:
The indirectly_readable concept (24.3.4.2 [iterator.concept.readable]) requires
iter_reference_t<In>&& and iter_value_t<In>& to model
common_reference_with, which ensures that the input iterator always has a common reference type.
iter_common_reference_t for computing such types is defined as
common_reference_t<iter_reference_t<T>, indirect-value-t<T>>.
It is unclear why the formula here drop the && part of iter_reference_t<In>,
but theoretically it is not completely equivalent to the former, for example:
#include <iterator>
struct Ref {
Ref(const Ref&) = delete;
};
struct Val {
operator const Ref&() const &;
};
struct I {
using value_type = Val;
using difference_type = int;
Ref operator*() const;
I& operator++();
I operator++(int);
};
static_assert(std::input_iterator<I>);
using reference = std::iter_reference_t<I>;
using value_type = std::iter_value_t<I>;
static_assert(std::same_as<std::common_reference_t<reference&&, value_type&>, const Ref&>);
static_assert(std::same_as<std::common_reference_t<reference , value_type&>, Ref >);
std::iter_value_t<I> val;
std::iter_common_reference_t<I> cr = val; // failed
In the above example, input_iterator ensures that the iterator's lvalue value type and rvalue reference type
can be bound to its common reference type, namely const Ref&, but the type calculated by
iter_common_reference_t is Ref, which cannot be bound by both.
&& to iter_reference_t<In> in formulas of similar
form to conform to the definition of indirectly_readable.
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll.
"NAD - This can easily lead to dangling references. This only matters if
iter_reference_t isn't a language reference type,
and the change causes common_reference to produce
a language reference type. So binding to the common reference requires
a temporary. That's not going to work if the type is used as a return type
(as the const-cases are).
As written I think it also causes significant damage to constant-iterator."
Proposed resolution:
This wording is relative to N4950.
Modify 24.2 [iterator.synopsis], header <iterator> synopsis, as indicated:
[…]
template<indirectly_readable T>
using iter_common_reference_t = // freestanding
common_reference_t<iter_reference_t<T>&&, indirect-value-t<T>>;
[…]
Modify 24.5.3.2 [const.iterators.alias] as indicated:
template<indirectly_readable It>
using iter_const_reference_t =
common_reference_t<const iter_value_t<It>&&, iter_reference_t<It>&&>;
Modify 24.5.3.3 [const.iterators.iterator] as indicated:
namespace std {
template<class I>
concept not-a-const-iterator = see below; // exposition only
template<indirectly_readable I>
using iter-const-rvalue-reference-t = // exposition only
common_reference_t<const iter_value_t<I>&&, iter_rvalue_reference_t<I>&&>;
[…]
};
<stddef.h> etc.)Section: 17.15.1 [support.c.headers.general] Status: New Submitter: Jiang An Opened: 2023-07-06 Last modified: 2024-02-22
Priority: 3
View all issues with New status.
Discussion:
Several feature-test macros are available in C++ versions of C headers (e.g. __cpp_lib_byte is available in
<cstddef>). However, the current standard wording doesn't seem to make the distinction between C++ library
feature-test macros and macros from C, so it's not very clear that whether <stddef.h> and its friends are
required to provide __cpp_lib_* macros in C++.
name.h should provide macros shown in the synopsis of its corresponding cname
(as required in C), but should not be required to provide C++ library feature-test macros because such requirement would
generally require implementations of the C standard library to change. I think we should make clarification in
17.15.1 [support.c.headers.general].
[2024-02-22; Reflector poll]
Set priority to 3 after reflector poll in July 2023.
[Jonathan commented]
The issue says that "such requirement would generally require implementations
of the C standard library to change" but this is not true.
A conforming C++ library already needs a C++-aware <stdlib.h> and <math.h>, and has done so since C++98! We should be cautious about expanding the set of C headers that need to be C++-aware, but if we require <stdlib.h> to define these functions:
constexpr long abs(long);
constexpr long long abs(long long);
then it can also define __cpp_lib_constexpr_math.
We have these <cxxx> headers providing feature test macros:
<cmath>: __cpp_lib_constexpr_cmath __cpp_lib_hypot __cpp_lib_interpolate __cpp_lib_math_special_functions <cstddef>: __cpp_lib_byte <cstdlib>: __cpp_lib_constexpr_cmath
For <stdlib.h> and <math.h>
the implementation already needs a C++-specific version of the header,
because ::abs is required to be overloaded (and constexpr) in
<stdlib.h> and <math.h>
(and all the math functions have to be constexpr even if you include
<math.h>).
So I see no issue here: the <xxx.h> headers should obviously
define the same macros as the <cxxx> headers.
We do not require ::byte to be in <stddef.h>,
so maybe we should not require the macro there either.
Except that std::byte is permitted to be in
<stddef.h>,
just not in the global namespace (see LWG 3883(i)).
So maybe SD-6 should simply clarify that the macro indicates the presence
of std::byte, not ::byte,
and whether std::byte and its macro are defined
by <stddef.h> is unspecified.
Also related to LWG 3484(i).
[Jens commented]
It seems 17.15 [support.c.headers] is silent on which macros
from the <cxxx> headers are made available via the <xxx.h> headers,
given that 17.15.7 [support.c.headers.other] talks about names placed in
the standard library namespace (macros don't fit that description).
[Ben noted some additional macros]
#define __cpp_lib_freestanding_cstdlib new-val // freestanding, also in <cstdlib>, <cmath>
#define __cpp_lib_freestanding_cstring new-val // freestanding, also in <cstring>
#define __cpp_lib_freestanding_cwchar new-val // freestanding, also in <cwchar>
#define __cpp_lib_freestanding_errc new-val // freestanding, also in <cerrno>, <system_error>
Proposed resolution:
noexcept to several repeat_view[::iterator] member functionsSection: 25.6.5.2 [range.repeat.view], 25.6.5.3 [range.repeat.iterator] Status: New Submitter: Hewill Kang Opened: 2023-07-06 Last modified: 2023-10-30
Priority: 3
View all other issues in [range.repeat.view].
View all issues with New status.
Discussion:
Several member functions of repeat_view::iterator only operate on its integer member, indicating that
they do not throw.
repeat_view::size should also be noexcept since it just performs the integer conversion.
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll. Some votes for NAD. "The iterator changes violate the Lakos rule."
Proposed resolution:
This wording is relative to N4950.
Modify 25.6.5.2 [range.repeat.view], class template repeat_view synopsis, as indicated:
namespace std::ranges { […] template<move_constructible T, semiregular Bound = unreachable_sentinel_t> requires (is_object_v<T> && same_as<T, remove_cv_t<T>> && (integer-like-with-usable-difference-type<Bound> || same_as<Bound, unreachable_sentinel_t>)) class repeat_view : public view_interface<repeat_view<T, Bound>> { private: […] public: […] constexpr auto size() const noexcept requires (!same_as<Bound, unreachable_sentinel_t>); }; […] }[…]
constexpr auto size() const noexcept requires (!same_as<Bound, unreachable_sentinel_t>);-9- Effects: Equivalent to:
return to-unsigned-like(bound_);
Modify 25.6.5.3 [range.repeat.iterator], class repeat_view::iterator synopsis, as indicated:
namespace std::ranges { template<move_constructible T, semiregular Bound> requires (is_object_v<T> && same_as<T, remove_cv_t<T>> && (integer-like-with-usable-difference-type<Bound> || same_as<Bound, unreachable_sentinel_t>)) class repeat_view<T, Bound>::iterator { private: using index-type = // exposition only conditional_t<same_as<Bound, unreachable_sentinel_t>, ptrdiff_t, Bound>; const T* value_ = nullptr; // exposition only index-type current_ = index-type(); // exposition only constexpr explicit iterator(const T* value, index-type b = index-type()); // exposition only public: […] constexpr iterator& operator++() noexcept; constexpr iterator operator++(int) noexcept; constexpr const T& operator[](difference_type n) const noexcept; friend constexpr bool operator==(const iterator& x, const iterator& y) noexcept; friend constexpr auto operator<=>(const iterator& x, const iterator& y) noexcept; friend constexpr difference_type operator-(const iterator& x, const iterator& y) noexcept; }; }[…]
constexpr iterator& operator++() noexcept;-5- Effects: Equivalent to:
++current_; return *this;constexpr iterator operator++(int) noexcept;-6- Effects: Equivalent to:
auto tmp = *this; ++*this; return tmp;[…]
friend constexpr bool operator==(const iterator& x, const iterator& y) noexcept;-15- Effects: Equivalent to:
return x.current_ == y.current_;friend constexpr auto operator<=>(const iterator&x, const iterator& y) noexcept;-16- Effects: Equivalent to:
return x.current_ <=> y.current_;[…]
friend constexpr difference_type operator-(const iterator& x, const iterator& y) noexcept;-19- Effects: Equivalent to:
return static_cast<difference_type>(x.current_) - static_cast<difference_type>(y.current_);
std::flat_map/std::flat_multimap be copied twice in some operations?Section: 23.6.8 [flat.map], 23.6.9 [flat.multimap] Status: New Submitter: Jiang An Opened: 2023-07-23 Last modified: 2025-10-21
Priority: 3
View other active issues in [flat.map].
View all other issues in [flat.map].
View all issues with New status.
Discussion:
Currently, some operations of std::flat_map are specified to construct a key_equiv
object for calling ranges::unique to remove duplicated elements. It seems that the comparator
is required to be copied twice before calling ranges::unique.
key_equiv to an aggregate class, then the comparator will be only copied once.
Furtherly, if we use a std::reference_wrapper<const key_compare> as the member of
key_equiv, then we will be able to avoid copying. Such differences seem to be observable.
Should we require to avoid copying, or explicitly say that implementations can either make copies or avoid copying?
Likewise, std::flat_map::value_comp and std::flat_multimap::value_comp seem to be
required to copy the comparator twice due to the exposition-only constructor of value_compare.
Should we make the constructor take a const reference?
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
We should provide blanket wording to specify that comparators, hashers, allocators, etc. can be copied as often or as little as needed. Consider generalizing 3049(i).
Proposed resolution:
chrono::parse handle duplicated data?Section: 30.13 [time.parse] Status: New Submitter: Jonathan Wakely Opened: 2023-07-28 Last modified: 2023-11-03
Priority: 3
View other active issues in [time.parse].
View all other issues in [time.parse].
View all issues with New status.
Discussion:
A call to chrono::parse can encounter redundant or contradictory
data, e.g.
or even simpler:stringstream("2023 2022") >> chrono::parse("%Y %C%y", yr);
These cases can both be successfully parsed, as the input stream contains the expected values in the expected format. But what value willstringstream("2023 2022") >> chrono::parse("%Y %Y", yr);
yr have
after it returns?
[2023-11-02; Reflector poll]
Set priority to 3 after reflector poll.
"Should allow setting failbit if an implementation does detect
inconsistent values."
"Yes, that's what MSVC does, so the wording needs something like:
it is unspecified whether setstate(ios_base::failbit)
is called on the basic_istream or whether an unspecified value
is assigned to the p argument."
Proposed resolution:
This wording is relative to N4950.
Modify 30.13 [time.parse] as indicated:
-17- If the
from_streamoverload fails to parse everything specified by the format string, or if insufficient information is parsed to specify a complete duration, time point, or calendrical data structure,setstate(ios_base::failbit)is called on thebasic_istream.-?- A format string can contain multiple flags that correspond to the same information, (e.g.,
"%Y %C%y"or"%R %H:%M"). If such a format string is successfully parsed but the extracted dates or times are not consistent, the value assigned to thetpargument is unspecified.[Example:
The value ofchrono::year y; stringstream("1905 1915") >> parse("%Y %C%y", y);yis unspecified;y.ok()can befalse. — end example]
chrono::parse check format strings?Section: 30.13 [time.parse] Status: New Submitter: Jonathan Wakely Opened: 2023-07-28 Last modified: 2023-11-03
Priority: 3
View other active issues in [time.parse].
View all other issues in [time.parse].
View all issues with New status.
Discussion:
30.13 [time.parse] p16 says:
"If the type being parsed cannot represent the information that the format flag refers to, is.setstate(ios_base::failbit) is called."
Note it says "the format flag" singular. I had interpreted this as meaning that if the entire format string doesn't provide the info
needed by the type, it fails. But that's not what it says.
It says that parsing fails if the type cannot represent
"the information that the format flag refers to". Which format flag?
Presumably this rule applies to each of them in turn.
So "Monday 13:00" >> parse("%a %R", sec) is required to fail,
rather than ignore the %a part and set sec to 13h.
I don't think that is the intended design.
I think the phrasing of this rule is backwards. It shouldn't be focused on what info is provided by the format flag,
but what info is needed by the type.
What should happen when chrono::parse is given a meaningless
format string like "%", or "%E", or "%Ea" or "%99a"?
Presumably that should set failbit, but I don't think we actually say so.
If the implementation should set failbit, is it conforming to do so before
extracting any characters?
Is it conforming to set failbit before extracting any characters if the format string can never succeed for the parsable type? Consider:
The type being parsed (std::chrono::seconds sec; std::cin >> parse("%a", sec);
seconds) cannot represent the information that
%a (a weekday) refers to, so we should set cin.setstate(ios_base::failbit).
But should that happen before extracting any characters, or after extracting
a weekday string? If it's unspecified, should we say so explicitly?
Can a conforming implementation validate the format string before extracting
any characters, and fail early if parsing the actual istream contents can never
succeed? Or is parse("%a", sec) required to try to parse a valid
weekday name before setting failbit?
[2023-11-02; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4950.
Modify 30.13 [time.parse] as indicated:
-15- All
from_streamoverloads behave as unformatted input functions, except that they have an unspecified effect on the value returned by subsequent calls to. Each overload takes a format string containing ordinary characters and flags which have special meaning. Each flag begins with abasic_istream<>::is.gcount()%. Some flags can be modified byEorO. During parsing each flag interprets characters as parts of date and time types according to Table 102. Some flags can be modified by a width parameter given as a positive decimal integer called out asNbelow which governs how many characters are parsed from the stream in interpreting the flag. All characters in the format string that are not represented in Table 102, except for whitespace, are parsed unchanged from the stream. A whitespace character matches zero or more whitespace characters in the input stream.-?- If a format string contains a
%character that is not part of a flag shown in Table 102, or a modifier that is not allowed for a flag,is.setstate(ios_base::failbit)is called. It is unspecified how many characters (if any) are extracted before the call tois.setstate(ios_base::failbit).-16- If a value cannot be determined for the type being parsed from the flags in the format string
cannot represent the information that the format flag refers tois.setstate(ios_base::failbit)is called. It is unspecified how many characters (if any) are extracted before the call tois.setstate(ios_base::failbit).[Example: A
durationcannot represent aweekday, soweekdayparse("%a", dur)will always fail if the type ofduris a specialization ofduration. Implementations can check the format string and setfailbitbefore extracting any characters. — end example]
Section: 30.13 [time.parse] Status: New Submitter: Jonathan Wakely Opened: 2023-07-28 Last modified: 2024-01-29
Priority: 3
View other active issues in [time.parse].
View all other issues in [time.parse].
View all issues with New status.
Discussion:
The %j flag talks about "the type being parsed" and %S talks about
"the input time" and "the input", but doesn't really define which types
those are.
[2023-11-02; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4950.
Modify 30.13 [time.parse] as indicated:
-?- In Table 102 the type
Parsabledenotes the type of the second argument tofrom_stream.
%jIf the type being parsedParsableis a specialization ofduration, a decimal number of days.%SThe seconds as a decimal number. The modified command %NSspecifies the maximum number of characters to read. IfNis not specified, the default is 2 ifParsableis a calendrical type (30.8 [time.cal])if the input time has a precision convertible to seconds. Otherwise the default width is determined by the decimal precision ofthe inputParsable, determined in the same manner ashh_mm_ss::fractional_width(30.9 [time.hms]). If the number of fractional decimal digits for the type is zero, then the default forNis 2. Otherwise, it is 3 + W, where W is the number of fractional decimal digits. IfNis less than 3, the field is interpreted as a decimal integer, otherwiseandthe field is interpreted as along doublein a fixed format. If encountered, the locale determines the decimal point character. Leading zeroes are permitted but not required. The modified command%OSinterprets the locale's alternative representation.
std::flat_map/std::flat_multimap specializations should be able to share same nested classesSection: 23.6.8.2 [flat.map.defn], 23.6.9.2 [flat.multimap.defn] Status: New Submitter: Jiang An Opened: 2023-07-24 Last modified: 2023-11-03
Priority: 3
View all other issues in [flat.map.defn].
View all issues with New status.
Discussion:
Per current wording, std::flat_map<K, V, C1, KCont, VCont>::containers and
std::flat_multimap<K, V, C2, KCont, VCont>::containers need to be different types
since they are specified as nested classes. Likewise,
std::flat_map<K, V, C, KCont1, VCont1>::value_compare and
std::flat_multimap<K, V, C, KCont2, VCont2>::value_compare also need to be different types.
std::flat_map/std::flat_multimap
specializations share same nested classes.
[2023-11-02; Reflector poll]
Set priority to 3 after reflector poll.
"This would mean we have to care about whether the template parameters are
associated entities of the containers type, for ADL purposes."
"That only matters if you're doing silly things, we shouldn't design the
library with silly people in mind."
"Nobody asked to be able to do this for value_compare in
associative containers, why do we think vendors would take advantage of this
freedom here? NAD."
Proposed resolution:
This wording is relative to N4950.
Add the new paragraph after 23.6.8.2 [flat.map.defn] p1 and 23.6.9.2 [flat.multimap.defn] p1 (twice):
[Drafting note: The intent is to keep the property that key/mapped types, key/mapped containers, and the comparator type are not the associated types of these nested classes during ADL. ]
-?- Implementations may define member types
containersandvalue_compareas nested classes of another templated class and introduce them as typedef-names. Implementations shall ensure that these nested classes are not themselves class templates and have the specified names.
std::atan2 and std::pow overloads that take two std::valarray parameters
should require the arguments to have the same lengthSection: 29.6.3.3 [valarray.transcend] Status: New Submitter: Jiang An Opened: 2023-07-28 Last modified: 2023-10-30
Priority: 4
View all issues with New status.
Discussion:
All binary operation between std::valarrays expect them to have the same length.
But std::atan2 and std::pow lack such Preconditions.
[2023-10-30; Reflector poll]
Set priority to 4 after reflector poll. "Also need to say what those functions return." "Paragraph 1 needs a correction for the functions that takes two operands." "And those are arguments not operands."
Proposed resolution:
This wording is relative to N4950.
Modify 29.6.3.3 [valarray.transcend] as indicated:
template<class T> valarray<T> abs (const valarray<T>&); template<class T> valarray<T> acos (const valarray<T>&); template<class T> valarray<T> asin (const valarray<T>&); template<class T> valarray<T> atan (const valarray<T>&); template<class T> valarray<T> atan2(const valarray<T>&, const valarray<T>&); template<class T> valarray<T> atan2(const valarray<T>&, const typename valarray<T>::value_type&); template<class T> valarray<T> atan2(const typename valarray<T>::value_type&, const valarray<T>&); template<class T> valarray<T> cos (const valarray<T>&); template<class T> valarray<T> cosh (const valarray<T>&); template<class T> valarray<T> exp (const valarray<T>&); template<class T> valarray<T> log (const valarray<T>&); template<class T> valarray<T> log10(const valarray<T>&); template<class T> valarray<T> pow (const valarray<T>&, const valarray<T>&); template<class T> valarray<T> pow (const valarray<T>&, const typename valarray<T>::value_type&); template<class T> valarray<T> pow (const typename valarray<T>::value_type&, const valarray<T>&); template<class T> valarray<T> sin (const valarray<T>&); template<class T> valarray<T> sinh (const valarray<T>&); template<class T> valarray<T> sqrt (const valarray<T>&); template<class T> valarray<T> tan (const valarray<T>&); template<class T> valarray<T> tanh (const valarray<T>&);-1- Mandates: A unique function with the indicated name can be applied (unqualified) to an operand of type
-?- Preconditions: ForT. This function returns a value of typeTor which can be unambiguously implicitly converted to typeT.atan2andpowoverloads that take twovalarrayparameters, the corresponding array arguments have the same length.
value_type and reference members of std::flat_(multi)map::(const_)iterator are unclearSection: 23.6.8.1 [flat.map.overview], 23.6.9.1 [flat.multimap.overview] Status: New Submitter: Jiang An Opened: 2023-08-04 Last modified: 2023-10-30
Priority: 3
View all other issues in [flat.map.overview].
View all issues with New status.
Discussion:
23.6.8.1 [flat.map.overview] and 23.6.9.1 [flat.multimap.overview] currently only require that the iterator types
meet Cpp17InputIterator requirements and model the std::random_access_iterator concept.
the operator*() returns proxy references of type std::pair<const K&, V&> or
std::pair<const K&, const V&> (same as the reference or const_reference
types of the container adaptors), and
the value_type of these iterators is std::pair<K, V> (same as the value_type
of the container adaptors).
However, such intent doesn't seem to be clear.
Moreover, given such possibly intended iterator types don't meet Cpp17ForwardIterator requirements, we may need to mention the exceptions in 23.2.2.2 [container.reqmts] or 23.2.7.1 [associative.reqmts.general].[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
std::is_nothrow_* traits may be ambiguous in some cases involving noexcept(false)Section: 21.3.6.4 [meta.unary.prop] Status: New Submitter: Jiang An Opened: 2023-08-04 Last modified: 2025-10-18
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with New status.
Discussion:
std::is_nothrow_* traits are currently specified in the form "(some operation) is known not to throw any exceptions".
Under the following circumstance, the current specification may be ambiguous:
a trait detects an explicitly defaulted special member function, and
the implicit except specification of that function would be noexcept(true), but
the function is explicitly marked with noexcept(false).
It seems that an implementation may decompose the defaulted special member function and then conclude that the trait
should give a true result, or may just use the exception specification which gives a false result.
[2025-10-17; Reflector poll.]
Set priority to 3 after reflector poll.
"NAD, this was just a GCC bug."
[2025-10-18; Arthur provides wording]
Proposed resolution:
This wording is relative to N5014.
Modify [tab:meta.unary.prop], Table 54 Type property predicates, as indicated:
| Template | Condition | Preconditions |
|---|---|---|
| ... | ... | ... |
template<class T, class... Args> struct is_nothrow_constructible; |
is_constructible_v<T, Args...> is true and
the variable definition for is_constructible, as defined below,
contains no potentially-throwing expression (14.5 [except.spec])
|
T and all types in the template parameter pack Args shall be complete types,
cv void, or arrays of unknown bound.
|
| ... | ... | ... |
std::endian::native value should be more specific about object representationsSection: 22.11.8 [bit.endian] Status: New Submitter: Brian Bi Opened: 2023-08-06 Last modified: 2024-02-22
Priority: 4
View all issues with New status.
Discussion:
22.11.8 [bit.endian] says that "big-endian" and "little-endian" refer to whether bytes are stored
in descending or ascending order of significance. In other words, when std::endian::native is either
std::endian::big or std::endian::little, we are told something about the object representations o
f multi-byte scalar types. However, the guarantee provided in this case is not strong enough to fully specify
the object representation, even in the common situation where padding bits are not present. It would be more
useful to provide a stronger guarantee.
char is 8 bits and there is an uint32_t type on the current platform.
If std::endian::native is std::endian::little, then the program should be able to rely on the
fact that if a uint32_t object is copied into an array of 4 unsigned char, then the value of
the first element of that array actually equals the original value modulo 256. However, because
P1236R1 removed the core language specification of the value representation of unsigned integer
types, the program cannot actually rely on this. It is conceivable (though unlikely), for example, that
std::endian::native could be std::endian::little but the first byte in a uint32_t
object is actually the least significant 8 bits flipped, or the least significant 8 bits permuted, or something
like that.
[2024-02-22; Reflector poll]
Set priority to 4 after reflector poll in August 2023.
[Jonathan expressed shock that P1236R1 remove portability guarantees that were previously present.]
[Jens explained that no observable guarantees were ever present anyway, which is why Core removed the wording.]
I agree with the thrust of the issue (i.e. the special values for
std::endian should permit reliance on a particular object
representation), but I disagree with the wording chosen. The
"pure binary" phrasing that is sort-of defined in a footnote
is bad. I think we want to say that all scalar types have no
padding bits and that the base-2 representation of
an unsigned integer type is formed by the bit concatenation
of the base-2 representations of the "unsigned char" values that
comprise the object representation of that unsigned integer type.
"bit concatenation" should best be phrased in math, e.g.
given a value x of some unsigned integer type and the
sequence of unsigned char values cj (each having width M)
comprising the object representation of x,
the coefficients of the base-2 representation of x are
xi = c⌊i/M⌋i mod M
or somesuch. See 7.6.11 [expr.bit.and] for some phrasing in this area.
Proposed resolution:
This wording is relative to N4950.
Modify the 22.11.8 [bit.endian] as indicated; using removed wording from C++17:
-2-
If all scalar types have size 1 byte, then all ofendian::little,endian::big, andendian::nativehave the same value. Otherwise,endian::littleis not equal toendian::big. If all scalar types are big-endian,endian::nativeis equal toendian::big. If all scalar types are little-endian,endian::nativeis equal toendian::little. Otherwise,endian::nativeis not equal to eitherendian::bigorendian::little.endian::littleis equal toendian::bigif and only if all scalar types have size 1 byte. If the value representation (6.9 [basic.types]) of every unsigned integer type uses a pure binary numeration systemfootnote ?, then:
If all scalar types have size 1 byte, then
endian::nativeis equal to the common value ofendian::littleandendian::big.Otherwise, if all scalar types are big-endian,
endian::nativeis equal toendian::big.Otherwise, if all scalar types are little-endian,
endian::nativeis equal toendian::little.Otherwise,
endian::nativeis not equal to eitherendian::bigorendian::little.Otherwise,
endian::nativeis not equal to eitherendian::bigorendian::little.footnote ?) A positional representation for integers that uses the binary digits 0 and 1, in which the values represented by successive bits are additive, begin with 1, and are multiplied by successive integral powers of 2, except perhaps for the bit with the highest position. (Adapted from the American National Dictionary for Information Processing Systems.)
std::ranges::fold_left_first_with_iter should be more ADL-proofSection: 26.6.18 [alg.fold] Status: New Submitter: Jiang An Opened: 2023-08-10 Last modified: 2023-11-03
Priority: 3
View other active issues in [alg.fold].
View all other issues in [alg.fold].
View all issues with New status.
Discussion:
The following program is currently ill-formed, because 26.6.18 [alg.fold]/10 requires evaluating
*init, where init is an object of an optional specialization, which triggers
ADL and finds unwanted overloads.
#include <algorithm>
#include <optional>
namespace myns {
struct Foo {};
void operator*(std::optional<Foo>&);
void operator*(const std::optional<Foo>&);
}
int main()
{
myns::Foo x[1]{};
std::ranges::fold_left_first_with_iter(x, []<class T>(T lhs, T) { return lhs; });
}
I think only the member operator* overload is intendedly used.
[2023-11-03; Reflector poll]
Many votes for NAD.
"Yuck, can we just use .value() instead?"
"The example is not good motivation, but we should ADL-proof to avoid
attempting to complete incomplete associated classes."
Proposed resolution:
This wording is relative to N4950.
Modify 26.6.18 [alg.fold] as indicated:
template<input_iterator I, sentinel_for<I> S, indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr see below ranges::fold_left_first_with_iter(I first, S last, F f); template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr see below ranges::fold_left_first_with_iter(R&& r, F f);-9- Let
Ubedecltype(ranges::fold_left(std::move(first), last, iter_value_t<I>(*first), f))-10- Effects: Equivalent to:
if (first == last) return {std::move(first), optional<U>()}; optional<U> init(in_place, *first); for (++first; first != last; ++first)*initinit.operator*() = invoke(f, std::move(*initinit.operator*()), *first); return {std::move(first), std::move(init)};
join_with_view::iterator's iter_swapSection: 25.7.15.3 [range.join.with.iterator] Status: New Submitter: Hewill Kang Opened: 2023-09-04 Last modified: 2023-11-03
Priority: 2
View other active issues in [range.join.with.iterator].
View all other issues in [range.join.with.iterator].
View all issues with New status.
Discussion:
The iter_swap customization for join_with_view::iterator allows swapping iterators
with different element types, potentially leading to unsafe behavior, for example:
vector<vector<string>> x{{"a"}, {"b"}, {"c"}};
vector<string_view> y{"-"};
auto r = x | views::join_with(y);
auto i = r.begin();
auto j = ranges::next(i);
ranges::iter_swap(j, i);
for (auto&& elem : r)
cout << elem << " "; // AddressSanitizer: stack-use-after-return on address
The above swaps two iterators whose reference are string_view& and string& respectively,
which ultimately results in string_view being referenced to a local variable and left dangling.
[2023-11-02; Reflector poll]
Set priority to 2 after reflector poll.
Proposed resolution:
Section: 23.2.2.5 [container.alloc.reqmts] Status: New Submitter: Alisdair Meredith Opened: 2023-08-14 Last modified: 2025-10-16
Priority: 4
View all other issues in [container.alloc.reqmts].
View all issues with New status.
Discussion:
Trying to interpret the specification for allocator-aware containers, 23.2.2.5 [container.alloc.reqmts].
P1 establishes thatstd::array is not an allocator-aware container, but all other containers in
the standard are.
P2 then adds vocabulary, with ruling that if a container type X is not allocator-aware then
the specification is as-if the allocator were std::allocator<T>. I cannot find a specification
for allocator-aware, and do not believe we should treat this as a recursive definition, as
the only standard container that would be affected would be std::array, which absolutely
does not want to apply all the terms in this subclause.
It looks like we might mean types that do not have everything specified by the first
sentence, such as an allocator_type member. Perhaps we want to say a container
type X is allocator-aware using allocator A if […], and then that basic_string and
containers that are not otherwise allocator aware are are treated as-if they were
allocator-aware using std::allocator<typename X::value_type> (where value_type
is already guaranteed to exist by the container requirements, 23.2.2.2 [container.reqmts] p1).
[2025-10-16; Reflector poll]
Set priority to 4 after reflector poll.
"NAD. 'If X is not allocator-aware' is equivalent to 'if X is not an allocate-aware container type'. Note that 23.2.2.5 [container.alloc.reqmts]/2 is not defining the requirements on an allocator-aware container; it's defining the related Cpp17Meow requirements. The allocator-aware container requirements are defined in paragraphs 3 and later, so this isn't a recursive definition."
Proposed resolution:
constexpr and noexcept for operators for bitmask typesSection: 16.3.3.3.3 [bitmask.types] Status: New Submitter: Jiang An Opened: 2023-08-19 Last modified: 2024-02-22
Priority: 3
View other active issues in [bitmask.types].
View all other issues in [bitmask.types].
View all issues with New status.
Discussion:
Currently, no operator in 16.3.3.3.3 [bitmask.types]/2 is specified as noexcept, and the compound assignment
operators are not specified as constexpr.
constexpr and noexcept (given MSVC STL doesn't support pre-C++14 modes), while libstdc++'s
compound
assignment operators for match_flag_type are constexpr since C++14 but lack noexcept, and
the
operators for launch are noexcept but not constexpr.
I think it's better to ensure more consistency be integer types and non-integer bitmask types, i.e., require the
compound assignment operators to be constexpr (only available in C++14 and later) and all operators to be noexcept.
[2024-02-22; Reflector poll]
Set priority to 3 after reflector poll in September 2023.
[Jonathan commented]
"The proposed change only affects an example showing a possible way to implement a made-up example type. It doesn't change any requirements on bitmask types, or change anything for any of the bitmask types defined in the standard library. It doesn't say that implementing them without noexcept and constexpr would be invalid. This change has no normative effect and certainly doesn't achieve the stated aim of requiring these assignments to be constexpr and non-throwing."
[Casey agreed]
"We should strike paragraph two completely and write up the actual requirements that a bitmask type is required to meet, but that's a lot of work for someone."
Proposed resolution:
This wording is relative to N4958.
Modify 16.3.3.3.3 [bitmask.types] as indicated:
-2- The bitmask type
bitmaskcan be written:// For exposition only. // int_type is an integral type capable of representing all values of the bitmask type. enum bitmask : int_type { V0 = 1 << 0, V1 = 1 << 1, V2 = 1 << 2, V3 = 1 << 3, … }; inline constexpr bitmask C0(V0); inline constexpr bitmask C1(V1); inline constexpr bitmask C2(V2); inline constexpr bitmask C3(V3); […] constexpr bitmask operator&(bitmask X, bitmask Y) noexcept { return static_cast<bitmask>( static_cast<int_type>(X) & static_cast<int_type>(Y)); } constexpr bitmask operator|(bitmask X, bitmask Y) noexcept { return static_cast<bitmask>( static_cast<int_type>(X) | static_cast<int_type>(Y)); } constexpr bitmask operator^(bitmask X, bitmask Y) noexcept { return static_cast<bitmask>( static_cast<int_type>(X) ^ static_cast<int_type>(Y)); } constexpr bitmask operator~(bitmask X) noexcept { return static_cast<bitmask>(~static_cast<int_type>(X)); } constexpr bitmask& operator&=(bitmask& X, bitmask Y) noexcept { X = X & Y; return X; } constexpr bitmask& operator|=(bitmask& X, bitmask Y) noexcept { X = X | Y; return X; } constexpr bitmask& operator^=(bitmask& X, bitmask Y) noexcept { X = X ^ Y; return X; }
std::bind_front<42>() and its friends?Section: 22.10.13 [func.not.fn], 22.10.14 [func.bind.partial] Status: New Submitter: Jiang An Opened: 2023-08-22 Last modified: 2024-02-22
Priority: 4
View all other issues in [func.not.fn].
View all issues with New status.
Discussion:
std::bind_front<42>() seems to be currently well-formed, but the result isn't invocable with any arguments.
Given we are already detecting types and values of the NTTP for new overloads of std::bind_front,
std::bind_back, and std::not_fn, it might be better to diagnose such cases when NTTP is of a scalar type.
[2024-02-22; Reflector poll]
Set priority to 4 after reflector poll in September 2023.
"Not convinced it's worth adding a small amount of future maintenance burden to catch a tiny amount of nonsense-but-not-dangerous code slightly earlier (at the point of call instead of point of use)."
"NAD. We reject bind_front<null_f>() because
bind_front(null_f)() compiles but gives UB.
Neither bind_front(42)() nor bind_front<42>()
compiles."
Proposed resolution:
This wording is relative to N4958.
Modify 22.10.13 [func.not.fn] as indicated:
template<auto f> constexpr unspecified not_fn() noexcept;-6- […]
-7- Mandates: Ifis_pointer_v<F> || is_member_pointer_v<F>istrue, thenf != nullptristrueis_scalar_v<F>istrue, then eitheris_pointer_v<F> && is_function_v<remove_pointer_t<F>>istrueoris_member_pointer_v<F>istrue, andf != nullptristruein either case.
Modify 22.10.14 [func.bind.partial] as indicated:
template<auto f, class... Args> constexpr unspecified bind_front(Args&&... args); template<auto f, class... Args> constexpr unspecified bind_back(Args&&... args);-6- […]
-7- Mandates:
(7.1) — […]
(7.2) — […]
(7.3) — if
is_pointer_v<F> || is_member_pointer_v<F>istrue, thenf != nullptristrueis_scalar_v<F>istrue, then eitheris_pointer_v<F> && is_function_v<remove_pointer_t<F>>istrueoris_member_pointer_v<F>istrue, andf != nullptristruein either case.
ranges::to adaptors are underconstrainedSection: 25.5.7.3 [range.utility.conv.adaptors] Status: New Submitter: Hewill Kang Opened: 2023-08-23 Last modified: 2023-11-03
Priority: 3
View all issues with New status.
Discussion:
The ranges::to adaptor returns a range adaptor closure object that stores a copy of the decayed parameter pack.
#include <ranges>
#include <vector>
struct NonMovable {
NonMovable() = default;
NonMovable(NonMovable&&) = delete;
};
int main() {
auto r = std::ranges::to<std::vector>(NonMovable{}); // hard error in MSVC-STL and libc++
}
Previous resolution [SUPERSEDED]:
This wording is relative to N4958.
Modify 25.2 [ranges.syn], header
<ranges>synopsis, as indicated:#include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] #include <iterator> // see [iterator.synopsis] namespace std::ranges { […] // 25.5.7 [range.utility.conv], range conversions template<class C, input_range R, class... Args> requires (!view<C>>) constexpr C to(R&& r, Args&&... args); // freestanding template<template<class...> class C, input_range R, class... Args> constexpr auto to(R&& r, Args&&... args); // freestanding template<class C, class... Args> requires (!view<C>) && (constructible_from<decay_t<Args>, Args> && ...) constexpr auto to(Args&&... args); // freestanding template<template<class...> class C, class... Args> requires (constructible_from<decay_t<Args>, Args> && ...) constexpr auto to(Args&&... args); // freestanding […] }Modify 25.5.7.3 [range.utility.conv.adaptors] as indicated:
template<class C, class... Args> requires (!view<C>) && (constructible_from<decay_t<Args>, Args> && ...) constexpr auto to(Args&&... args); template<template<class...> class C, class... Args> requires (constructible_from<decay_t<Args>, Args> && ...) constexpr auto to(Args&&... args);-1- Mandates: For the first overload,
-2- Returns: A range adaptor closure object (25.7.2 [range.adaptor.object])Cis a cv-unqualified class type.fthat is a perfect forwarding call wrapper (22.10.4 [func.require]) with the following properties: […]
[2023-11-03; Reflector poll]
Set priority to 3 after reflector poll. Votes split between NAD and using Mandates instead of constraints.
[2023-09-28; Hewill provides alternative wording]
The new wording form is consistent with the current wording, that is, it has a similar structure with 25.7.2 [range.adaptor.object] p1 and 25.7.2 [range.adaptor.object] p8.
Proposed resolution:
This wording is relative to N4958.
Modify 25.5.7.3 [range.utility.conv.adaptors] as indicated:
template<class C, class... Args> requires (!view<C>) constexpr auto to(Args&&... args); template<template<class...> class C, class... Args> constexpr auto to(Args&&... args);-1- Mandates: For the first overload,
-2- Returns: A range adaptor closure object (25.7.2 [range.adaptor.object])Cis a cv-unqualified class type.fthat is a perfect forwarding call wrapper (22.10.4 [func.require]) with the following properties:
(2.1) — It has no target object.
(2.2) — Its bound argument entities
bound_argsconsist of objects of typesdecay_t<Args>...direct-non-list-initialized withstd::forward<Args>(args)..., respectively.(2.3) — Its call pattern is
to<C>(r, bound_args...), whereris the argument used in a function call expression off.-?- Remarks: The expression
to(args...)is well-formed if and only if the initialization of the bound argument entities of the result, as specified above, are all well-formed.
ranges::to should Mandates C not to be viewSection: 25.5.7.2 [range.utility.conv.to], 25.5.7.3 [range.utility.conv.adaptors] Status: New Submitter: Hewill Kang Opened: 2023-08-25 Last modified: 2023-10-30
Priority: 3
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with New status.
Discussion:
In order to solve issues in LWG 3787(i) and LWG 3847(i) that the template parameter C
can be specified as an unreasonable type, ranges::to adds a Mandates that requires C to be a
cv-unqualified class type.
However, the earliest requirement that C not be a view was still imposed by the constraints of function signatures,
although it is unclear why Constraints were used in the first place, such a way of kicking out the function candidate
does bring undesirable hard errors and poor diagnostics (demo):
#include <ranges>
int main() {
auto iota = std::views::iota(0, 10);
auto take = std::ranges::to<std::ranges::take_view>(iota, 5); // hard error in function body
auto drop = iota | std::ranges::to<std::ranges::drop_view>(5); // poor diagnostics
}
I think consistent use of Mandates for template parameter C is more appropriate, as static_assert
provide clearer and more readable diagnostics.
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4958.
Modify 25.2 [ranges.syn] as indicated:
#include <compare> // see 17.12.1 [compare.syn] #include <initializer_list> // see 17.11.2 [initializer.list.syn] #include <iterator> // see 24.2 [iterator.synopsis] namespace std::ranges { […] // 25.5.7 [range.utility.conv], range conversions template<class C, input_range R, class... Args>requires (!view<C>)constexpr C to(R&& r, Args&&... args); // freestanding template<template<class...> class C, input_range R, class... Args> constexpr auto to(R&& r, Args&&... args); // freestanding template<class C, class... Args>requires (!view<C>)constexpr auto to(Args&&... args); // freestanding template<template<class...> class C, class... Args> constexpr auto to(Args&&... args); // freestanding […] }
Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args>requires (!view<C>)constexpr C to(R&& r, Args&&... args);-1- Mandates:
[…]Cis a cv-unqualified class type and does not satisfyview.
Modify 25.5.7.3 [range.utility.conv.adaptors] as indicated:
template<class C, class... Args>requires (!view<C>)constexpr auto to(Args&&... args); template<template<class...> class C, class... Args> constexpr auto to(Args&&... args);-1- Mandates: For the first overload,
[…]Cis a cv-unqualified class type and does not satisfyview.
basic_const_iterator doesn't work with optionalSection: 24.5.3 [const.iterators] Status: New Submitter: Hewill Kang Opened: 2023-08-26 Last modified: 2023-10-30
Priority: 3
View other active issues in [const.iterators].
View all other issues in [const.iterators].
View all issues with New status.
Discussion:
The heterogeneous comparison operators of basic_const_iterator only constrain type I to be
comparable with the underlying iterator type, which allows non-iterator types such as optional, and
the heterogeneous comparison operators of the latter would make the situation worse
(demo):
#include <iterator>
#include <optional>
int main() {
std::basic_const_iterator<const int*> it;
std::optional opt{it};
auto c = opt < it; // infinite meta-recursion
auto d = it - opt; // infinite meta-recursion
}
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
as_const_view and basic_const_iterator provide base()?Section: 24.5.3 [const.iterators], 25.7.22.2 [range.as.const.view] Status: Open Submitter: Hewill Kang Opened: 2023-08-28 Last modified: 2024-03-15
Priority: 3
View other active issues in [const.iterators].
View all other issues in [const.iterators].
View all issues with Open status.
Discussion:
Currently, both as_const_view and basic_const_iterator provide base() members to
return the underlying range and iterator, which seems to expose vulnerabilities in modifying them:
#include <vector>
#include <ranges>
int main() {
std::vector v{1, 2, 3};
auto f = [](std::span<int>::const_iterator i) {
*i.base() = 4;
};
f(std::span{v}.cbegin());
auto g = [](const std::ranges::constant_range auto& r) {
r.begin().base()[1] = 5;
r.base()[2] = 6;
};
g(std::ranges::as_const_view(v));
// v now becomes [4, 5, 6]
}
I don't think such a shortcut should be provided as it doesn't seem to be the intention and could be harmful.
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll. Send to SG9.
[Kona 2023-11-07; move to Ready]
[2024-03-15; move back to Open following LEWG feedback]
SG9 approved the proposed change but then LEWG had no consensus for change. LWG should revisit in Tokyo.
Proposed resolution:
This wording is relative to N4958.
Modify 24.5.3.3 [const.iterators.iterator], class template basic_const_iterator synopsis, as indicated:
namespace std {
[…]
template<input_iterator Iterator>
class basic_const_iterator {
[…]
constexpr const Iterator& base() const & noexcept;
constexpr Iterator base() &&;
[…]
};
}
Modify 24.5.3.5 [const.iterators.ops] as indicated:
constexpr const Iterator& base() const & noexcept;
-4- Effects: Equivalent to:return current_;constexpr Iterator base() &&;
-5- Effects: Equivalent to:return std::move(current_);
Modify 25.7.22.2 [range.as.const.view] as indicated:
namespace std::ranges {
template<view V>
requires input_range<V>
class as_const_view : public view_interface<as_const_view<V>> {
[…]
constexpr V base() const & requires copy_constructible<V> { return base_; }
constexpr V base() && { return std::move(base_); }
[…]
};
}
std::span or std::basic_string_view is not clearSection: 27.3 [string.view], 23.7.2.2 [views.span] Status: New Submitter: Jiang An Opened: 2023-08-29 Last modified: 2023-10-30
Priority: 3
View other active issues in [string.view].
View all other issues in [string.view].
View all issues with New status.
Discussion:
It is unclear whether the following program has undefined behavior:
#include <cassert>
#include <span>
#include <string_view>
int main()
{
int arr[2]{42, 84};
std::span<int> sp1{arr, 1};
std::span<int> sp2{arr + 1, 1};
assert(sp2.begin() - sp1.begin() == 1); // Is this well-defined?
assert(sp2.begin() == sp1.end()); // ditto
assert(*sp1.end() == 84); // ditto
const char str[]{"string"};
std::string_view sv1{str, 3};
std::string_view sv2{str + 3, 3};
assert(sv2.begin() - sv1.begin() == 3); // Is this well-defined?
assert(sv2.begin() == sv1.end()); // ditto
assert(*sv1.end() == 'i'); // ditto
}
Currently MSVC STL strictly diagnose the arithmetic between different spans/basic_string_views,
even the views are on the same underlying range (see this Github issue).
libstdc++ and libc++ silently accept these operations.
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll.
Libc++ diagnoses the example with the right macros defined.
"Should substr and remove_suffix tighten the
bounds or copy them from the original view?"
Proposed resolution:
variant's move assignment should not be guaranteed to produce a valueless by exception stateSection: 22.6.3.4 [variant.assign] Status: New Submitter: Brian Bi Opened: 2023-08-29 Last modified: 2023-10-30
Priority: 3
View other active issues in [variant.assign].
View all other issues in [variant.assign].
View all issues with New status.
Discussion:
22.6.3.4 [variant.assign] bullet 8.4 states that an alternative-changing move assignment on std::variant
is equivalent to a call to emplace. However, bullet 10.1 also states that if the construction of the new
alternative exits via an exception, then the destination of the assignment is guaranteed to become valueless
by exception. This is inconsistent with the specification of emplace, 22.6.3.5 [variant.mod]/11,
which permits (but does not require) the variant to become valueless.
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll.
"The remark is normatively redundant with the spec of emplace,
strike it."
Proposed resolution:
This wording is relative to N4958.
Modify 22.6.3.4 [variant.assign] as indicated:
constexpr variant& operator=(variant&& rhs) noexcept(see below);-6- Let
[…] -8- Effects:jberhs.index().
(8.1) — If neither
*thisnorrhsholds a value, there is no effect.(8.2) — Otherwise, if
*thisholds a value butrhsdoes not, destroys the value contained in*thisand sets*thisto not hold a value.(8.3) — Otherwise, if
index() == j, assignsget<j>(std::move(rhs))to the value contained in*this.(8.4) — Otherwise, equivalent to
emplace<j>(get<j>(std::move(rhs))).[…]
-10- Remarks: […]
(10.1) — If an exception is thrown during the call toTj's move construction (withjbeingrhs.index()), thevariantwill hold no value.
(10.2) —If an exception is thrown during the call toTj's move assignment, the state of the contained value is as defined by the exception safety guarantee ofTj's move assignment;index()will bej.
parse function of a BasicFormatter type needs to be constexprSection: 28.5.6.1 [formatter.requirements] Status: New Submitter: Jiang An Opened: 2023-10-10 Last modified: 2023-10-30
Priority: 3
View all other issues in [formatter.requirements].
View all issues with New status.
Discussion:
Format strings need to be checkable at compile-time since P2216R3. In order to check a
compile-time format string, the parse functions of the formatters need to be called. However,
currently there is seemingly no requirement for the parse function to be constexpr,
and hence whether a format string is correct for formatted types may be not well-defined at compile-time.
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll.
"Should be OK to provide a non-constexpr parse if you only use it
with std::vformat and not std::format."
"Only the std::formatter specializations in the standard library
should be required to have constexpr parse."
"No need to require this, but a note with this text explaining when a call
to parse can be a constant expression might be nice."
Proposed resolution:
This wording is relative to N4958.
Modify 28.5.6.1 [formatter.requirements], Table 73, BasicFormatter requirements [tab:formatter.basic] as indicated:
Table 73: BasicFormatter requirements [tab:formatter.basic] Expression Return type Requirement g.parse(pc)PC::iteratorParses format-spec (28.5.2 [format.string]) for type Tin the
range[pc.begin(), pc.end())until the first unmatched
character. Throwsformat_errorunless
the whole range is parsed or the unmatched
character is}.
[Note 1: This allows formatters to emit meaningful
error messages. — end note]
Stores the parsed format specifiers in*thisand
returns an iterator past the end of the parsed
range. The expression shall be a core constant expression ifg,
pc.begin(),pc.end(), and lvalue-to-rvalue conversion
for each element in[pc.begin(), pc.end())are core constant
expressions, and no exception is thrown.…
adaptor(args...)(r) is not equivalent to std::bind_back(adaptor, args...)(r)Section: 25.7.2 [range.adaptor.object] Status: New Submitter: Hewill Kang Opened: 2023-10-11 Last modified: 2023-11-03
Priority: 4
View other active issues in [range.adaptor.object].
View all other issues in [range.adaptor.object].
View all issues with New status.
Discussion:
25.7.2 [range.adaptor.object] p8 specifies that:
The expression
adaptor(args...)produces a range adaptor closure objectfthat is a perfect forwarding call wrapper (22.10.4 [func.require]) with the following properties:
According to the subsequent description, it can be inferred that the behavior is similar to
std::bind_back(adaptor, args...) which also returns a perfect forwarding call wrapper.
Every call wrapper (22.10.3 [func.def]) meets the Cpp17MoveConstructible and Cpp17Destructible requirements.
In order to conform with the specification, standard functions that return perfect forwarding call wrappers such as
std::bind_front/back and std::not_fn all Mandates that
(is_constructible_v<BoundArgs, Args> && ...) and
(is_move_constructible_v<BoundArgs> && ...) are each true,
the former condition corresponds to 25.7.2 [range.adaptor.object] p8:
The expression
adaptor(args...)is well-formed if and only if the initialization of the bound argument entities of the result, as specified above, are all well-formed.
However, the latter does not have a corresponding description in <ranges>. In other words, range
adaptor objects do not explicitly indicate that the bound argument must be move-constructible.
This results in implementation divergence for some uncommon types (demo):
#include <ranges>
#include <string_view>
constexpr struct WeirdFive {
WeirdFive() = default;
WeirdFive(const WeirdFive&) = default;
constexpr operator int() const { return 5; }
WeirdFive(WeirdFive&&) = delete;
} five;
constexpr std::string_view sv{"hello"};
static_assert(sv == std::views::take(five)(sv)); // libstdc++/libc++ reject, MSVC-STL accepts
Above, libstdc++ always moves arguments into internal members, which leads to hard errors in the member initializer list;
libc++ uses std::bind_back for argument binding, which also leads to hard errors in the function body as the
former requires arguments to be move-constructible; MSVC-STL is the most compliant with current wording.
[2023-11-02; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
<mdspan>Section: 23.7.3 [views.multidim] Status: New Submitter: Hewill Kang Opened: 2023-10-11 Last modified: 2023-10-30
Priority: 3
View all issues with New status.
Discussion:
Currently, std::layout_meow::mapping::operator() has the following definition (23.7.3.4.5.3 [mdspan.layout.left.obs]):
template<class... Indices> constexpr index_type operator()(Indices... i) const noexcept;-2- Constraints:
(2.1) —
sizeof...(Indices) == extents_type::rank()istrue,(2.2) —
(is_convertible_v<Indices, index_type> && ...)istrue, and(2.3) —
(is_nothrow_constructible_v<index_type, Indices> && ...)istrue.Preconditions:
Effects: Letextents_type::index-cast(i)is a multidimensional index inextents_(23.7.3.1 [mdspan.overview]).Pbe a parameter pack such thatis_same_v<index_sequence_for<Indices...>, index_sequence<P...>>is
true. Equivalent to:return ((static_cast<index_type>(i) * stride(P)) + ... + 0);
Above, is_convertible_v<Indices, index_type> implies that index_type can be constructed through
rvalue-qualified conversion operators. However, we cast the lvalue i in the return statement, which makes the
expression possibly ill-formed. The same goes for extents_type::index-cast(i).
However, if we use std::move before casting, this will result in the rvalue-qualified conversion operator
being called in Preconditions via extents_type::index-cast(i) before the mapping index is actually calculated,
so that the expression may no longer be valid. And such an issue already exists in mdspan::operator[].
In addition, the variadic version of mdspan::operator[] constraints
is_convertible_v<OtherIndexTypes, index_type>, but its array/span version constraints
is_convertible_v<const OtherIndexType&, index_type>.
mdspan[arr] may not necessarily guarantee mdspan[arr[i]...].
I think we should unanimously require that custom indexes can be converted to index_type via const
lvalue references, which eliminates the worry of conversion expiration.
[2023-10-30; Reflector poll]
Set priority to 3 after reflector poll.
"P4 - doesn't affect 'normal' uses of custom index types.
Only affects expert users that interface with the mapping directly,
because mdspan does the conversions."
Proposed resolution:
std::formatter specializations should be consistently restricted to supported character typesSection: 28.5.1 [format.syn], 30.2 [time.syn], 32.4.3.2 [thread.thread.id] Status: New Submitter: Jiang An Opened: 2023-10-15 Last modified: 2023-11-04
Priority: 4
View other active issues in [format.syn].
View all other issues in [format.syn].
View all issues with New status.
Discussion:
28.5.6.4 [format.formatter.spec]/2 requires some std::formatter specializations are only enabled
for character types char and wchar_t. But for specializations that are explicitly shown in
the synopses (28.5.1 [format.syn], 30.2 [time.syn], and 32.4.3.2 [thread.thread.id]),
there is no such restrictions for charT. The difference may be observable via the std::formattable
concept, e.g. std::formattable<std::chrono::second, char8_t> is currently satisfied, even though
there would be a hard error during formatting due to STATICALLY-WIDEN.
char and
wchar_t for now, and constrain the charT template parameter of all std::formatter
specializations that are explicitly mentioned in synopses with that concept. This is what libc++ currently
implements (e.g. the
__fmt_char_type
concept and its
usage in <chrono>).
[2023-11-03; Reflector poll]
Set priority to 4 after reflector poll. "Problem looks theoretical, all entry points to formatters are constrained."
Proposed resolution:
std::regex_constants should be allowed to be enumeratorsSection: 28.6.4 [re.const] Status: New Submitter: Jiang An Opened: 2023-10-18 Last modified: 2024-02-22
Priority: 3
View all other issues in [re.const].
View all issues with New status.
Discussion:
Currently, MSVC STL and libc++ implement constants in std::regex_constants as
enumerators of unscoped enumerations, while the standard specify them to be constexpr variables.
syntax_option_type,
match_flag_type, or error_type) if the corresponding type is an enumeration.
Notes: since C++20, we can even make the enumeration types scoped and expose the enumerators to the
namespace by using-declaration or using-enum-declaration. P0439R0 might be related,
since it changed former enumerators to constexpr variables.
[2024-02-22; Reflector poll]
Set priority to 3 after reflector poll in October 2023.
[Jonathan commented]
"Why only change the regex_constants bitmask constants,
and not those for ctype_base::mask,
ios_base::fmtflags, ios_base::iostate,
and ios_base::openmode?"
Proposed resolution:
Section: 32.5.4 [atomics.order] Status: New Submitter: Jiang An Opened: 2023-10-18 Last modified: 2023-11-03
Priority: 4
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with New status.
Discussion:
P0439R0 made std::memory_order an scoped enumeration type. However, it also
changed former enumerators (std::memory_order_seq_cst) to constexpr variables.
using enum (P1099R5) in C++20, it may be better to keep
these constants being prvalues.
[2023-11-02; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
This wording is relative to N4964.
Modify 32.5.2 [atomics.syn], header <atomic> synopsis, as indicated:
namespace std {
// 32.5.4 [atomics.order], order and consistency
enum class memory_order : unspecified; // freestanding
inline constexpr memory_order memory_order_relaxed = memory_order::relaxedusing memory_order::memory_order_relaxed; // freestanding
inline constexpr memory_order memory_order_consume = memory_order::consumeusing memory_order::memory_order_consume; // freestanding
inline constexpr memory_order memory_order_acquire = memory_order::acquireusing memory_order::memory_order_acquire; // freestanding
inline constexpr memory_order memory_order_release = memory_order::releaseusing memory_order::memory_order_release; // freestanding
inline constexpr memory_order memory_order_acq_rel = memory_order::acq_relusing memory_order::memory_order_acq_rel; // freestanding
inline constexpr memory_order memory_order_seq_cst = memory_order::seq_cstusing memory_order::memory_order_seq_cst; // freestanding
[…]
}
[…]
Modify 32.5.4 [atomics.order] as indicated:
namespace std {
enum class memory_order : unspecified {
relaxed, consume, acquire, release, acq_rel, seq_cst,
memory_order_relaxed = relaxed, memory_order_consume = consume, memory_order_acquire = acquire,
memory_order_release = release, memory_order_acq_rel = acq_rel, memory_order_seq_cst = seq_cst
};
}
flat_map::insert_range's Effects is not quite rightSection: 23.6.8.7 [flat.map.modifiers] Status: New Submitter: Hewill Kang Opened: 2023-10-23 Last modified: 2025-09-05
Priority: 3
View other active issues in [flat.map.modifiers].
View all other issues in [flat.map.modifiers].
View all issues with New status.
Discussion:
flat_map::insert_range adds elements to the containers member via:
for (const auto& e : rg) {
c.keys.insert(c.keys.end(), e.first);
c.values.insert(c.values.end(), e.second);
}
which is incorrect because rg's value type may not be a pair (tuple, for instance),
which means that .first and .second are not valid in such cases.
[2024-02-22; Reflector poll]
Set priority to 3 after reflector poll in October 2023.
"This is P2767 section 6 which LWG looked at in Varna."
[2025-09-05; LWG telecon]
This will be resolved by P3567.
Proposed resolution:
This wording is relative to N4964.
Modify 23.6.8.7 [flat.map.modifiers] as indicated:
template<container-compatible-range<value_type> R> void insert_range(R&& rg);-12- Effects: Adds elements to
cas if by:for (value_typeconst auto&e : rg) { c.keys.insert(c.keys.end(), std::move(e.first)); c.values.insert(c.values.end(), std::move(e.second)); }[…]
iota_view::iterator::iterator_concept should be improvedSection: 25.6.4.3 [range.iota.iterator] Status: New Submitter: Hewill Kang Opened: 2023-10-27 Last modified: 2023-11-07
Priority: 3
View other active issues in [range.iota.iterator].
View all other issues in [range.iota.iterator].
View all issues with New status.
Discussion:
When W models incrementable, the iota_view iterator will be a forward_iterator,
which accidentally makes iota_view<istream_iterator<int>> satisfies forward_range,
which is obviously incorrect.
W, it seems more appropriate and accurate to define
the iterator_concept through the iterator concept in this case.
[2023-11-07; Reflector poll]
Set priority to 3 after reflector poll.
"This would change the iterator concept for types which model advanceable but do not model random_access_iterator. A type with +=
and -= and operator* will currently get random_access_iterator_tag
but this proposed resolution would change it to bidirectional_iterator_tag."
"If we want to handle this case (and not leave it as IFNDR - the type
satisfies-but-not-models incrementable) then we need to account
for it everywhere we are currently using incrementable
(and possibly equality_comparable?), and not just in the concept."
Proposed resolution:
This wording is relative to N4964.
Modify 25.6.4.3 [range.iota.iterator] as indicated:
[Drafting note: When
Wonly modelsinput_or_output_iterator, it implies that its pre- and post-increment are not equality-preserving, soiterator_conceptshould beinput_iterator_tag.]
-1-
iterator::iterator_conceptis defined as follows:
(?.?) — If
Wmodelsinput_or_output_iterator, then
(?.?) — if
Wmodelsrandom_access_iterator, theniterator_conceptisrandom_access_iterator_tag;(?.?) — otherwise, if
Wmodelsbidirectional_iterator, theniterator_conceptisbidirectional_iterator_tag;(?.?) — otherwise, if
Wmodelsforward_iterator, theniterator_conceptisforward_iterator_tag;(?.?) — otherwise,
iterator_conceptisinput_iterator_tag.(1.1) — Otherwise, i
IfWmodelsadvanceable, theniterator_conceptisrandom_access_iterator_tag.(1.2) — Otherwise, if
Wmodelsdecrementable, theniterator_conceptisbidirectional_iterator_tag.(1.3) — Otherwise, if
Wmodelsincrementable, theniterator_conceptisforward_iterator_tag.(1.4) — Otherwise,
iterator_conceptisinput_iterator_tag.
Section: 32.5.4 [atomics.order] Status: SG1 Submitter: jim x Opened: 2023-10-30 Last modified: 2024-05-19
Priority: 3
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with SG1 status.
Discussion:
32.5.4 [atomics.order] p1 says:
(1.2) —
memory_order::release,memory_order::acq_rel, andmemory_order::seq_cst: a store operation performs a release operation on the affected memory location.(1.3) —
memory_order::consume: a load operation performs a consume operation on the affected memory location. […](1.4) —
memory_order::acquire,memory_order::acq_rel, andmemory_order::seq_cst: a load operation performs an acquire operation on the affected memory location.
What do the store and load operations intend to mean in this context? If there is no extra specification,
it is easy to consider them as the operations performed by the non-static member functions "store" and "load"
defined in the atomic class (template).
An atomic operation A that performs a release operation on an atomic object M synchronizes with an atomic operation B that performs an acquire operation on M and takes its value from any side effect in the release sequence headed by A.
According to the above interpretation, A is an operation performed by the non-static member function
store, however, I think the following example can establish the synchronization relationship
std::atomic<int> x{0};
Thread 1:
int expected = 0;
x.compare_exchange_strong(expected, 1, memory_order::release, memory_order::relaxed); // #1
Thread 2:
int expected = 1;
while(x.compare_exchange_strong(expected, 2, memory_order::acquire, memory_order::relaxed)){} // #2
Assuming the RMW operations are successful in the two threads, I think #1 intends to perform a
release operation while #2 performs an acquire operation, and hence they can establish the
synchronization relationship, however, they both are RMW operations.
[2024-03-11; Reflector poll]
Set priority to 3 after reflector poll in November 2023. Ask SG1 to look.
Jonathan:
"Interpreting this to only mean the store and load
member functions wouldn't even be self-consistent. Could be clearer though,
6.10.2 [intro.multithread] talks about reads and writes (and RMW ops)
and only uses "store" and "load" informally. Maybe just add something saying
"reads are also called loads and writes are also called stores".
[2024-05-15; jim x comments and expands the discussion]
This is an addition to this issue, consider this example:
std::atomic<int> x{0};
Thread 1:
int expected = 0;
x.compare_exchange_strong(expected, 1, memory_order::acq_rel, memory_order::relaxed); // #1
Thread 2:
int expected = 1;
while(x. compare_exchange_strong(expected, 2, memory_order::acq_rel, memory_order::relaxed)){} // #2
memory_order::acq_rel performs a release operation when the operation is a store and is
an acquire operation when the operation is a load. It is unclear what operations #1 and
#2 are considered when they succeed, as pointed out in the original issue, we still don't
specify whether RMW is classified as load or store operation or can be both.
success order affects the RMW when it is viewed as a load or store
operation.
Proposed resolution:
Section: 3.48 [defns.required.behavior], 16.3.2.4 [structure.specifications], 16.4.5.8 [res.on.functions] Status: New Submitter: Eric Niebler Opened: 2023-11-03 Last modified: 2024-06-24
Priority: 2
View all issues with New status.
Discussion:
The library's definition of the term "required behavior" (3.48 [defns.required.behavior]) makes it specific to certain replaceable functions such as the replaceable global allocation functions. Notably, it is not one of the elements that are allowed to appear in the descriptions of general function semantics. That is, it isn't in the list in 16.3.2.4 [structure.specifications] p3.
However, the specification of the random number generator library uses "Required behavior" as such a descriptive element of its functions' semantics (e.g., 29.5.6 [rand.predef]). I think that's a fine use of "Required behavior", so I would like the term to be more generally applicable to the behavior of any stdlib function that may be customized by users. This is relevant forstd::execution, where algorithms are
customizable but the customizations are required to behave a certain way.
Daniel:
Bullet (2.4) of 16.4.5.8 [res.on.functions] also contradicts to the more narrow definition of
3.48 [defns.required.behavior] by suddenly extending it to "destructor operations".
[2024-03-11; Reflector poll]
Set priority to 2 after reflector poll.
[2024-06-24; The approval of P2810R4 means that "required behavior" is now being incorrectly used in the working paper.]
Proposed resolution:
volatile-qualified perfect forwarding call wrapperSection: 22.10.4 [func.require] Status: New Submitter: Jiang An Opened: 2023-11-05 Last modified: 2024-03-11
Priority: 3
View other active issues in [func.require].
View all other issues in [func.require].
View all issues with New status.
Discussion:
22.10.4 [func.require]/4 says:
[…] This forwarding step delivers a state entity of type
Tas cvT&when the call is performed on an lvalue of the call wrapper type and as cvT&&otherwise, where cv represents the cv-qualifiers of the call wrapper and where cv shall be neithervolatilenorconst volatile.
The "shall" seemingly indicates a requirement for user codes that calling a volatile perfect forwarding call wrapper is undefined behavior. Such requirement seems to be combined with requirements for implementations in an unusual way.
Moreover, it is unclear whether UB is intended here. Perhaps we only want calling such avolatile
wrapper to be conditionally-supported. E.g. when the operator() happens to be implementable as a
static member function, calling a volatile wrapper can be naturally supported and have expected
behavior.
[2024-03-11; Reflector poll]
Set priority to 3 after reflector poll.
LWG 2487(i) doesn't seem to intend to forbid volatile
support as a conforming extension.
P1065R2 made it ill-formed for std::bind but didn't
make the same change to the definition of perfect forwarding call wrapper.
So given an as_volatile function that works like std::as_const,
as_volatile(std::bind(&T::fn))(t) is ill-formed but
as_volatile(std::mem_fn(&T::fn))(t) is undefined.
Supporting it as a conforming extension is fine, but making it UB is not.
Maybe "conditionally supported" instead?
Proposed resolution:
This wording is relative to N4964.
Modify 22.10.4 [func.require] as indicated:
4 A perfect forwarding call wrapper is an argument forwarding call wrapper that forwards its state entities to the underlying call expression. This forwarding step delivers a state entity of type
Tas cvT&when the call is performed on an lvalue of the call wrapper type and as cvT&&otherwise, where cv represents the cv-qualifiers of the call wrapperand where cv shall be neither, except that if cv is eithervolatilenorconst volatilevolatileorconst volatile, it is unspecified whether the call is well-formed.
ranges::to may cause infinite recursion if range_value_t<C>
is a non-move-constructible rangeSection: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: S. B. Tam Opened: 2023-11-08 Last modified: 2024-03-11
Priority: 3
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with New status.
Discussion:
25.5.7.2 [range.utility.conv.to]/2 says:
(2.1) — If
Cdoes not satisfyinput_rangeorconvertible_to<range_reference_t<R>, range_value_t<C>>istrue:
[…]
(2.2) — Otherwise, if
input_range<range_reference_t<R>>istrue:to<C>(r | views::transform([](auto&& elem) { return to<range_value_t<C>>(std::forward<decltype(elem)>(elem)); }), std::forward<Args>(args)...);[…]
That is, if range_reference_t<R> is not convertible to range_value_t<C>, and
range_reference_t<R> is an input range, views::transform is applied to convert the
inner range to range_value_t<C> (through to<range_value_t<C>>), and then
the transformed range is converted to C (through to<C>).
#include <ranges>
struct ImmovableRange {
ImmovableRange(int*, int*);
ImmovableRange(ImmovableRange&&) = delete;
int* begin();
int* end();
};
struct C {
ImmovableRange* begin();
ImmovableRange* end();
};
using R = int[1][2];
void test() {
(void)std::ranges::to<C>(R{});
}
Here:
convertible_to<range_reference_t<R>, range_value_t<C>> is false.
range_reference_t<R> satisfies input_range.
range_reference_t<R> can be converted to range_value_t<C> through
to<range_value_t<C>>. (If it couldn't, an error would be produced immediately.)
So to<C> is called recursively, constructing C with the transformed range (whose
range_reference_t<R> is the same as range_value_t<C>). For the construction
from the transformed range:
range_reference_t<R> and range_value_t<C> are both ImmovableRange.
convertible_to<range_reference_t<R>, range_value_t<C>> (i.e.
convertible_to<ImmovableRange, ImmovableRange>) is false.
range_reference_t<R> (i.e. ImmovableRange) satisfies input_range.
range_reference_t<R> can be converted to range_value_t<C> through
to<range_value_t<C>>.
So to<C> is called recursively again, transforming the range for the second time. This time,
the transformation does not change any of the above four facts. As a result, to<C> is called
yet again, leading to an infinite recursion.
to<C> recursively when range_reference_t<R>
is the same as range_value_t<C>.
[2024-03-11; Reflector poll]
Set priority to 3 after reflector poll.
"Do we want same_as or !different-from?"
Proposed resolution:
This wording is relative to N4964.
Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);-1- Mandates:
Cis a cv-unqualified class type.-2- Returns: An object of type
Cconstructed from the elements ofrin the following manner:
(2.1) — If
Cdoes not satisfyinput_rangeorconvertible_to<range_reference_t<R>, range_value_t<C>>istrue:
[…]
(2.2) — Otherwise, if
same_as<range_reference_t<R>, range_value_t<C>>isfalseandinput_range<range_reference_t<R>>istrue:to<C>(r | views::transform([](auto&& elem) { return to<range_value_t<C>>(std::forward<decltype(elem)>(elem)); }), std::forward<Args>(args)...);(2.3) — Otherwise, the program is ill-formed.
subrange::advance should be improvedSection: 25.5.4.3 [range.subrange.access] Status: New Submitter: Hewill Kang Opened: 2023-11-09 Last modified: 2024-03-11
Priority: 3
View all other issues in [range.subrange.access].
View all issues with New status.
Discussion:
subrange::advance always increments begin_ via ranges::advance(begin_,
n, end_), which has 𝒪(n) complexity for non-common random-access ranges,
which can be improved to 𝒪(1) with the help of the size_ member (if provided).
[2024-03-11; Reflector poll]
Set priority to 3 after reflector poll.
Jonathan: The "Effects: Equivalent to" wording strongly suggests doing exactly
what it suggests there, and the difference would be observable in the number
of times the iterator is compared to the sentinel. I'm not sure if we care
about that, or if an implementation would be free to make this change as QoI.
Regarding the proposed resolution, we know the type of n
so we don't need to use decltype(n) in the cast.
Proposed resolution:
This wording is relative to N4964.
Modify 25.5.4.3 [range.subrange.access] as indicated:
constexpr subrange& advance(iter_difference_t<I> n);
-9- Effects: Equivalent to:
if constexpr (bidirectional_iterator<I>) { if (n < 0) { ranges::advance(begin_, n); if constexpr (StoreSize) size_ += to-unsigned-like(-n); return *this; } }auto d = n - ranges::advance(begin_, n, end_);if constexpr (StoreSize) { n = std::min(n, static_cast<decltype(n)>(size_)); ranges::advance(begin_, n); size_ -= to-unsigned-like(nd); } else { ranges::advance(begin_, n, end_); } return *this;
const overloads of std::optional monadic operationsSection: 22.5.3.8 [optional.monadic] Status: Open Submitter: Jonathan Wakely Opened: 2023-11-24 Last modified: 2024-10-02
Priority: 1
View all issues with Open status.
Discussion:
The resolution of LWG 3973(i) (adopted in Kona) changed all
occurrences of value() to *val.
The intention was not to change the meaning, just avoid the non-freestanding
value() function, and avoid ADL that would be caused by using
**this.
However, in the const overloads such as
and_then(F&&) const the type of value()
was const T&, but the type of *val is always
T&. This implies that the const overloads invoke the callable
with a non-const argument, which is incorrect (and would be undefined
behaviour for a const std::optional<T>).
On the LWG reflector it was suggested that we should rewrite the specification
of std::optional to stop using an exposition-only data member
of type T*. No such member ever exists in real implemetations,
so it is misleading and leads to specification bugs of this sort.
Change the class definition in 22.5.3.1 [optional.optional.general]
to use a union, and update every use of val accordingly
throughout 22.5.3 [optional.optional].
For consistency with 22.8.6.1 [expected.object.general] we might
also want to introduce a bool has_val member and refer to
that in the specification.
private:T *val; // exposition onlybool has_val; // exposition only union { T val; // exposition only }; };
For example, in 22.5.3.9 [optional.mod]:
-1- Effects: If
*thiscontains a value, callsvalto destroy the contained value and sets->.T::~T()has_valtofalse; otherwise no effect.
[2023-11-26; Daniel provides wording]
The proposed wording is considerably influenced by that of the specification of expected, but
attempts to reduce the amount of changes to not perfectly mimic it. Although "the contained value" is
a magic word of power it seemed feasible and simpler to use the new exposition-only member val
directly in some (but not all) places, usually involved with initializations.
has_val to true/false"
where either the Effects wording says "otherwise no effect" or in other cases if the postconditions
did not already say that indirectly. I also added extra mentioning of has_val changes in tables
where different cells had very different effects on that member (unless these cells specify postconditions),
to prevent misunderstanding.
[2024-03-11; Reflector poll]
Set priority to 1 after reflector poll in November 2023. Six votes for 'Tentatively Ready' but enough uncertainty to deserve discussion at a meeting.
Previous resolution [SUPERSEDED]:
This wording is relative to N4964 after application of the wording of LWG 3973(i).
Modify 22.5.3.1 [optional.optional.general], class template
optionalsynopsis, as indicated:namespace std { template<class T> class optional { public: using value_type = T; […] private: bool has_val; // exposition only union { T val*val; // exposition only }; }; […] }Modify 22.5.3.1 [optional.optional.general] as indicated:
-2- Member
has_valindicates whether anoptional<T>object contains a valueWhen an.optional<T>object contains a value, membervalpoints to the contained valueModify 22.5.3.2 [optional.ctor] as indicated:
[Drafting note: Normatively, this subclause doesn't require any changes, but I'm suggesting to replace phrases of the form "[…]initializes the contained value with"] by "[…]initializes
valwith" as we do in 22.8.6.2 [expected.object.cons]. I intentionally did not add extra "and setshas_valtotrue/false" since those effects are already guaranteed by the postconditions]constexpr optional(const optional& rhs);-4- Effects: If
-5- Postconditions:rhscontains a value, direct-non-list-initializesvalthe contained valuewith.*rhs.valrhs.has_value() == this->has_value(). […]constexpr optional(optional&& rhs) noexcept(see below);-8- Constraints: […]
-9- Effects: Ifrhscontains a value, direct-non-list-initializesvalthe contained valuewithstd::move(.*rhs.val)rhs.has_value()is unchanged. -10- Postconditions:rhs.has_value() == this->has_value(). […]template<class... Args> constexpr explicit optional(in_place_t, Args&&... args);-13- Constraints: […]
-14- Effects: Direct-non-list-initializesvalthe contained valuewithstd::forward<Args>(args).... -15- Postconditions:*thiscontains a value. […]template<class U, class... Args> constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);-18- Constraints: […]
-19- Effects: Direct-non-list-initializesvalthe contained valuewithil, std::forward<Args>(args).... -20- Postconditions:*thiscontains a value. […]template<class U = T> constexpr explicit(see below) optional(U&& v);-23- Constraints: […]
-24- Effects: Direct-non-list-initializesvalthe contained valuewithstd::forward<U>(v). -25- Postconditions:*thiscontains a value. […]template<class U> constexpr explicit(see below) optional(const optional<U>& rhs);-28- Constraints: […]
-29- Effects: Ifrhscontains a value, direct-non-list-initializesvalthe contained valuewith. -30- Postconditions:*rhs.valrhs.has_value() == this->has_value(). […]template<class U> constexpr explicit(see below) optional(optional<U>&& rhs);-33- Constraints: […]
-34- Effects: Ifrhscontains a value, direct-non-list-initializesvalthe contained valuewithstd::move(.*rhs.val)rhs.has_value()is unchanged. -35- Postconditions:rhs.has_value() == this->has_value(). […]Modify 22.5.3.3 [optional.dtor] as indicated:
constexpr ~optional();-1- Effects: If
is_trivially_destructible_v<T> != trueand*thiscontains a value, calls.val->val.T::~T()Modify 22.5.3.4 [optional.assign] as indicated:
constexpr optional<T>& operator=(nullopt_t) noexcept;-1- Effects: If
-2- Postconditions:*thiscontains a value, callsto destroy the contained value and setsval->val.T::~T()has_valtofalse; otherwise no effect.*thisdoes not contain a value.constexpr optional<T>& operator=(const optional& rhs);-4- Effects: See Table 58.
Table 58 — optional::operator=(const optional&)effects [tab:optional.assign.copy]*thiscontains a value*thisdoes not contain a valuerhscontains a valueassigns to*rhs.valvalthe contained valuedirect-non-list-initializes valthe contained valuewith*rhs.val
and setshas_valtotruerhsdoes not contain a valuedestroys the contained value by calling val->val.T::~T()
and setshas_valtofalseno effect -5- Postconditions:
[…]rhs.has_value() == this->has_value().constexpr optional<T>& operator=(optional&& rhs) noexcept(see below);-8- Constraints: […]
-9- Effects: See Table 59. The result of the expressionrhs.has_value()remains unchanged. -10- Postconditions:rhs.has_value() == this->has_value(). -11- Returns:*this.
Table 59 — optional::operator=(optional&&)effects [tab:optional.assign.move]*thiscontains a value*thisdoes not contain a valuerhscontains a valueassigns std::move(to*rhs.val)valthe contained valuedirect-non-list-initializes valthe contained valuewithstd::move(and sets*rhs.val)has_valtotruerhsdoes not contain a valuedestroys the contained value by calling
and setsval->val.T::~T()has_valtofalseno effect -12- Remarks: […]
-13- If any exception is thrown, the result of the expressionthis->has_value()remains unchanged. If an exception is thrown during the call toT's move constructor, the state ofis determined by the exception safety guarantee of*rhs.valvalT's move constructor. If an exception is thrown during the call toT's move assignment, the state ofand*valvalis determined by the exception safety guarantee of*rhs.valvalT's move assignment.template<class U = T> constexpr optional<T>& operator=(U&& v);-14- Constraints: […]
-15- Effects: If*thiscontains a value, assignsstd::forward<U>(v)tovalthe contained value; otherwise direct-non-list-initializesvalthe contained valuewithstd::forward<U>(v). -16- Postconditions:*thiscontains a value. -17- Returns:*this. -18- Remarks: If any exception is thrown, the result of the expressionthis->has_value()remains unchanged. If an exception is thrown during the call toT's constructor, the state ofvis determined by the exception safety guarantee ofT's constructor. If an exception is thrown during the call toT's assignment, the state ofvaland*valvis determined by the exception safety guarantee ofT's assignment.template<class U> constexpr optional<T>& operator=(const optional<U>& rhs);-19- Constraints: […]
-20- Effects: See Table 60.
Table 60 — optional::operator=(const optional<U>&)effects [tab:optional.assign.copy.templ]*thiscontains a value*thisdoes not contain a valuerhscontains a valueassigns to*rhs.valvalthe contained valuedirect-non-list-initializes valthe contained valuewithand sets*rhs.valhas_valtotruerhsdoes not contain a valuedestroys the contained value by calling
and setsval->val.T::~T()has_valtofalseno effect -21- Postconditions:
-22- Returns:rhs.has_value() == this->has_value().*this. -23- If any exception is thrown, the result of the expressionthis->has_value()remains unchanged. If an exception is thrown during the call toT's constructor, the state ofis determined by the exception safety guarantee of*rhs.valvalT's constructor. If an exception is thrown during the call toT's assignment, the state ofvaland*valis determined by the exception safety guarantee of*rhs.valvalT's assignment.template<class U> constexpr optional<T>& operator=(optional<U>&& rhs);-24- Constraints: […]
-25- Effects: See Table 61. The result of the expressionrhs.has_value()remains unchanged.
Table 61 — optional::operator=(optional<U>&&)effects [tab:optional.assign.move.templ]*thiscontains a value*thisdoes not contain a valuerhscontains a valueassigns std::move(to*rhs.val)valthe contained valuedirect-non-list-initializes valthe contained valuewith
std::move(and sets*rhs.val)has_valtotruerhsdoes not contain a valuedestroys the contained value by calling
and setsval->val.T::~T()has_valtofalseno effect -26- Postconditions:
-27- Returns:rhs.has_value() == this->has_value().*this. -28- If any exception is thrown, the result of the expressionthis->has_value()remains unchanged. If an exception is thrown during the call toT's constructor, the state ofis determined by the exception safety guarantee of*rhs.valvalT's constructor. If an exception is thrown during the call toT's assignment, the state ofvaland*valis determined by the exception safety guarantee of*rhs.valvalT's assignment.template<class... Args> constexpr T& emplace(Args&&... args);-29- Mandates: […]
-30- Effects: Calls*this = nullopt. Then direct-non-list-initializesvalthe contained valuewithstd::forward<Args>(args).... -31- Postconditions:*thiscontains a value. -32- Returns:valA reference to the new contained value. […] -34- Remarks: If an exception is thrown during the call toT's constructor,*thisdoes not contain a value, and the previousval(if any) has been destroyed.*valtemplate<class U, class... Args> constexpr T& emplace(initializer_list<U> il, Args&&... args);-35- Constraints: […]
-36- Effects: Calls*this = nullopt. Then direct-non-list-initializesvalthe contained valuewithil, std::forward<Args>(args).... -37- Postconditions:*thiscontains a value. -38- Returns:valA reference to the new contained value. […] -40- Remarks: If an exception is thrown during the call toT's constructor,*thisdoes not contain a value, and the previousval(if any) has been destroyed.*valModify 22.5.3.5 [optional.swap] as indicated:
constexpr void swap(optional& rhs) noexcept(see below);-1- Mandates: […]
-2- Preconditions: […] -3- Effects: See Table 62.
Table 62 — optional::swap(optional&)effects [tab:optional.swap]*thiscontains a value*thisdoes not contain a valuerhscontains a valuecalls swap(val*(*this),*rhs.val)direct-non-list-initializes valthe contained value of*this
withstd::move(, followed by*rhs.val)rhs.val.;val->T::~T()
postcondition is that*thiscontains a value andrhsdoes
not contain a valuerhsdoes not contain a valuedirect-non-list-initializes the contained value ofrhs.val
withstd::move(val, followed by*(*this))val.;val->T::~T()
postcondition is that*thisdoes not contain a value andrhs
contains a valueno effect -4- Throws: […]
-5- Remarks: […] -6- If any exception is thrown, the results of the expressionsthis->has_value()andrhs.has_value()remain unchanged. If an exception is thrown during the call to functionswap, the state ofvaland*valis determined by the exception safety guarantee of*rhs.valvalswapfor lvalues ofT. If an exception is thrown during the call toT's move constructor, the state ofvaland*valis determined by the exception safety guarantee of*rhs.valvalT's move constructor.Modify 22.5.3.7 [optional.observe] as indicated:
constexpr const T* operator->() const noexcept; constexpr T* operator->() noexcept;-1- Preconditions:
-2- Returns:*thiscontains a value.addressof(val). -3- […]valconstexpr const T& operator*() const & noexcept; constexpr T& operator*() & noexcept;-4- Preconditions:
-5- Returns:*thiscontains a value.val. -6- […]*valconstexpr T&& operator*() && noexcept; constexpr const T&& operator*() const && noexcept;-7- Preconditions:
-8- Effects: Equivalent to:*thiscontains a value.return std::move(val*val);constexpr explicit operator bool() const noexcept;
-9- Returns:trueif and only if*thiscontains a value.-10- Remarks: This function is a constexpr function.constexpr bool has_value() const noexcept;-11- Returns:
-12- Remarks: These functions arehas_val.trueif and only if*thiscontains a valueThis function is aconstexpr functions.constexpr const T& value() const &; constexpr T& value() &;-13- Effects: Equivalent to:
return has_value() ? val*val: throw bad_optional_access();constexpr T&& value() &&; constexpr const T&& value() const &&;-14- Effects: Equivalent to:
return has_value() ? std::move(val*val) : throw bad_optional_access();template<class U> constexpr T value_or(U&& v) const &;-15- Mandates: […]
-16- Effects: Equivalent to:return has_value() ? val**this: static_cast<T>(std::forward<U>(v));template<class U> constexpr T value_or(U&& v) &&;-17- Mandates: […]
-18- Effects: Equivalent to:return has_value() ? std::move(val**this) : static_cast<T>(std::forward<U>(v));Modify 22.5.3.8 [optional.monadic] as indicated:
template<class F> constexpr auto and_then(F&& f) &; template<class F> constexpr auto and_then(F&& f) const &;-1- Let
-2- Mandates: […] -3- Effects: Equivalent to:Ubeinvoke_result_t<F, decltype((val).*val)>if (*this) { return invoke(std::forward<F>(f), val*val); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto and_then(F&& f) &&; template<class F> constexpr auto and_then(F&& f) const &&;-4- Let
-5- Mandates: […] -6- Effects: Equivalent to:Ubeinvoke_result_t<F, decltype(std::move(val.*val))>if (*this) { return invoke(std::forward<F>(f), std::move(val*val)); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto transform(F&& f) &; template<class F> constexpr auto transform(F&& f) const &;-7- Let
-8- Mandates:Uberemove_cv_t<invoke_result_t<F, decltype((val).*val)>>Uis a non-array object type other thanin_place_tornullopt_t. The declarationU u(invoke(std::forward<F>(f), val*val));is well-formed for some invented variable
[…] -9- Returns: Ifu.*thiscontains a value, anoptional<U>object whose contained value is direct-non-list-initialized withinvoke(std::forward<F>(f), val; otherwise,*val)optional<U>().template<class F> constexpr auto transform(F&& f) &&; template<class F> constexpr auto transform(F&& f) const &&;-10- Let
-11- Mandates:Uberemove_cv_t<invoke_result_t<F, decltype(std::move(val.*val))>>Uis a non-array object type other thanin_place_tornullopt_t. The declarationU u(invoke(std::forward<F>(f), std::move(val*val)));is well-formed for some invented variable
[…] -12- Returns: Ifu.*thiscontains a value, anoptional<U>object whose contained value is direct-non-list-initialized withinvoke(std::forward<F>(f), std::move(val; otherwise,*val))optional<U>().Modify 22.5.3.9 [optional.mod] as indicated:
constexpr void reset() noexcept;-1- Effects: If
-2- Postconditions:*thiscontains a value, callsto destroy the contained value and setsval->val.T::~T()has_valtofalse; otherwise no effect.*thisdoes not contain a value.
[St. Louis 2024-06-24; Jonathan provides improved wording]
[2024-08-21; LWG telecon]
During telecon review it was suggested to replace 22.5.3.1 [optional.optional.general] p1 and p2. On the reflector Daniel requested to keep the "additional storage" prohibition, so that will be addressed by issue 4141(i) instead.
[2024-10-02; Jonathan tweaks proposed resolution]
On the reflector we decided that the union member should use remove_cv_t,
as proposed for expected by issue 3891(i).
The rest of the proposed resolution is unchanged, so that edit was made
in-place below, instead of as a new resolution that supersedes the old one.
Proposed resolution:
This wording is relative to N4988.
Modify 22.5.3.1 [optional.optional.general], class template optional synopsis, as indicated:
namespace std {
template<class T>
class optional {
public:
using value_type = T;
[…]
private:
*val // exposition only;
union {
remove_cv_t<T> val; // exposition only
};
};
[…]
}
Modify 22.5.3.1 [optional.optional.general] as indicated:
-1- When its member
valis active (11.5.1 [class.union.general]), an instance ofoptional<T>is said to contain a value, andvalis referred to as its contained value.Any instance ofAn optional object's contained valueoptional<T>at any given time either contains a value or does not contain a value. When an instance ofoptional<T>contains a value, it means that an object of typeT, referred to as thecontained value,is allocated within the storage of the optional object. Implementations are not permitted to use additional storage, such as dynamic memory, to allocate its contained value.When an object of typeoptional<T>is contextually converted tobool, the conversion returnstrueif the object contains a value; otherwise the conversion returnsfalse.
-2- When anoptional<T>object contains a value, membervalpoints to the contained value.
Modify 22.5.3.2 [optional.ctor] as indicated:
constexpr optional(const optional& rhs);-4- Effects: If
-5- Postconditions:rhscontains a value, direct-non-list-initializesvalthe contained valuewith.*rhs.valrhs.has_value() == this->has_value(). […]constexpr optional(optional&& rhs) noexcept(see below);-8- Constraints: […]
-9- Effects: Ifrhscontains a value, direct-non-list-initializesvalthe contained valuewithstd::move(.*rhs.val)rhs.has_value()is unchanged. -10- Postconditions:rhs.has_value() == this->has_value(). […]template<class... Args> constexpr explicit optional(in_place_t, Args&&... args);-13- Constraints: […]
-14- Effects: Direct-non-list-initializesvalthe contained valuewithstd::forward<Args>(args).... -15- Postconditions:*thiscontains a value. […]template<class U, class... Args> constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);-18- Constraints: […]
-19- Effects: Direct-non-list-initializesvalthe contained valuewithil, std::forward<Args>(args).... -20- Postconditions:*thiscontains a value. […]template<class U = T> constexpr explicit(see below) optional(U&& v);-23- Constraints: […]
-24- Effects: Direct-non-list-initializesvalthe contained valuewithstd::forward<U>(v). -25- Postconditions:*thiscontains a value. […]template<class U> constexpr explicit(see below) optional(const optional<U>& rhs);-28- Constraints: […]
-29- Effects: Ifrhscontains a value, direct-non-list-initializesvalthe contained valuewith. -30- Postconditions:*rhs.valrhs.has_value() == this->has_value(). […]template<class U> constexpr explicit(see below) optional(optional<U>&& rhs);-33- Constraints: […]
-34- Effects: Ifrhscontains a value, direct-non-list-initializesvalthe contained valuewithstd::move(.*rhs.val)rhs.has_value()is unchanged. -35- Postconditions:rhs.has_value() == this->has_value(). […]
Modify 22.5.3.3 [optional.dtor] as indicated:
constexpr ~optional();-1- Effects: If
is_trivially_destructible_v<T> != trueand*thiscontains a value, calls.val->val.T::~T()
Modify 22.5.3.4 [optional.assign] as indicated:
constexpr optional<T>& operator=(nullopt_t) noexcept;-1- Effects: If
-2- Postconditions:*thiscontains a value, callsto destroy the contained value; otherwise no effect.val->val.T::~T()*thisdoes not contain a value.constexpr optional<T>& operator=(const optional& rhs);-4- Effects: See Table 58.
Table 58 — optional::operator=(const optional&)effects [tab:optional.assign.copy]*thiscontains a value*thisdoes not contain a valuerhscontains a valueassigns to*rhs.valvalthe contained valuedirect-non-list-initializes valthe contained valuewith*rhs.val
rhsdoes not contain a valuedestroys the contained value by calling val->val.T::~T()
no effect -5- Postconditions:
[…]rhs.has_value() == this->has_value().constexpr optional<T>& operator=(optional&& rhs) noexcept(see below);-8- Constraints: […]
-9- Effects: See Table 59. The result of the expressionrhs.has_value()remains unchanged. -10- Postconditions:rhs.has_value() == this->has_value(). -11- Returns:*this.
Table 59 — optional::operator=(optional&&)effects [tab:optional.assign.move]*thiscontains a value*thisdoes not contain a valuerhscontains a valueassigns std::move(to*rhs.val)valthe contained valuedirect-non-list-initializes valthe contained valuewithstd::move(*rhs.val)rhsdoes not contain a valuedestroys the contained value by calling
val->val.T::~T()no effect -12- Remarks: […]
-13- If any exception is thrown, the result of the expressionthis->has_value()remains unchanged. If an exception is thrown during the call toT's move constructor, the state ofis determined by the exception safety guarantee of*rhs.valvalT's move constructor. If an exception is thrown during the call toT's move assignment, the statesstateofand*valvalare*rhs.valvalisdetermined by the exception safety guarantee ofT's move assignment.template<class U = T> constexpr optional<T>& operator=(U&& v);-14- Constraints: […]
-15- Effects: If*thiscontains a value, assignsstd::forward<U>(v)tovalthe contained value; otherwise direct-non-list-initializesvalthe contained valuewithstd::forward<U>(v). -16- Postconditions:*thiscontains a value. -17- Returns:*this. -18- Remarks: If any exception is thrown, the result of the expressionthis->has_value()remains unchanged. If an exception is thrown during the call toT's constructor, the state ofvis determined by the exception safety guarantee ofT's constructor. If an exception is thrown during the call toT's assignment, the statesstateofvaland*valvareisdetermined by the exception safety guarantee ofT's assignment.template<class U> constexpr optional<T>& operator=(const optional<U>& rhs);-19- Constraints: […]
-20- Effects: See Table 60.
Table 60 — optional::operator=(const optional<U>&)effects [tab:optional.assign.copy.templ]*thiscontains a value*thisdoes not contain a valuerhscontains a valueassigns to*rhs.valvalthe contained valuedirect-non-list-initializes valthe contained valuewith*rhs.valrhsdoes not contain a valuedestroys the contained value by calling
val->val.T::~T()no effect -21- Postconditions:
-22- Returns:rhs.has_value() == this->has_value().*this. -23- If any exception is thrown, the result of the expressionthis->has_value()remains unchanged. If an exception is thrown during the call toT's constructor, the state ofis determined by the exception safety guarantee of*rhs.valvalT's constructor. If an exception is thrown during the call toT's assignment, the statesstateofvaland*valare*rhs.valvalisdetermined by the exception safety guarantee ofT's assignment.template<class U> constexpr optional<T>& operator=(optional<U>&& rhs);-24- Constraints: […]
-25- Effects: See Table 61. The result of the expressionrhs.has_value()remains unchanged.
Table 61 — optional::operator=(optional<U>&&)effects [tab:optional.assign.move.templ]*thiscontains a value*thisdoes not contain a valuerhscontains a valueassigns std::move(to*rhs.val)valthe contained valuedirect-non-list-initializes valthe contained valuewith
std::move(*rhs.val)rhsdoes not contain a valuedestroys the contained value by calling
val->val.T::~T()no effect -26- Postconditions:
-27- Returns:rhs.has_value() == this->has_value().*this. -28- If any exception is thrown, the result of the expressionthis->has_value()remains unchanged. If an exception is thrown during the call toT's constructor, the state ofis determined by the exception safety guarantee of*rhs.valvalT's constructor. If an exception is thrown during the call toT's assignment, the statesstateofvaland*valare*rhs.valvalisdetermined by the exception safety guarantee ofT's assignment.template<class... Args> constexpr T& emplace(Args&&... args);-29- Mandates: […]
-30- Effects: Calls*this = nullopt. Then direct-non-list-initializesvalthe contained valuewithstd::forward<Args>(args).... -31- Postconditions:*thiscontains a value. -32- Returns:valA reference to the new contained value. […] -34- Remarks: If an exception is thrown during the call toT's constructor,*thisdoes not contain a value, and the previousval(if any) has been destroyed.*valtemplate<class U, class... Args> constexpr T& emplace(initializer_list<U> il, Args&&... args);-35- Constraints: […]
-36- Effects: Calls*this = nullopt. Then direct-non-list-initializesvalthe contained valuewithil, std::forward<Args>(args).... -37- Postconditions:*thiscontains a value. -38- Returns:valA reference to the new contained value. […] -40- Remarks: If an exception is thrown during the call toT's constructor,*thisdoes not contain a value, and the previousval(if any) has been destroyed.*val
Modify 22.5.3.5 [optional.swap] as indicated:
constexpr void swap(optional& rhs) noexcept(see below);-1- Mandates: […]
-2- Preconditions: […] -3- Effects: See Table 62.
Table 62 — optional::swap(optional&)effects [tab:optional.swap]*thiscontains a value*thisdoes not contain a valuerhscontains a valuecalls swap(val*(*this),*rhs.val)direct-non-list-initializes valthe contained value of*this
withstd::move(, followed by*rhs.val)rhs.val.;val->T::~T()
postcondition is that*thiscontains a value andrhsdoes
not contain a valuerhsdoes not contain a valuedirect-non-list-initializes the contained value ofrhs.val
withstd::move(val, followed by*(*this))val.;val->T::~T()
postcondition is that*thisdoes not contain a value andrhs
contains a valueno effect -4- Throws: […]
-5- Remarks: […] -6- If any exception is thrown, the results of the expressionsthis->has_value()andrhs.has_value()remain unchanged. If an exception is thrown during the call to functionswap, the state ofvaland*valis determined by the exception safety guarantee of*rhs.valvalswapfor lvalues ofT. If an exception is thrown during the call toT's move constructor, the statesstateofvaland*valare*rhs.valvalisdetermined by the exception safety guarantee ofT's move constructor.
Modify 22.5.3.7 [optional.observe] as indicated:
constexpr const T* operator->() const noexcept; constexpr T* operator->() noexcept;-1- Preconditions:
-2- Returns:*thiscontains a value.addressof(val). -3- […]valconstexpr const T& operator*() const & noexcept; constexpr T& operator*() & noexcept;-4- Preconditions:
-5- Returns:*thiscontains a value.val. -6- […]*valconstexpr T&& operator*() && noexcept; constexpr const T&& operator*() const && noexcept;-7- Preconditions:
-8- Effects: Equivalent to:*thiscontains a value.return std::move(val*val);constexpr explicit operator bool() const noexcept;-9- Returns:
-10- Remarks: This function is a constexpr function.trueif and only if*thiscontains a value.constexpr bool has_value() const noexcept;-11- Returns:
-12- Remarks: This function is a constexpr function.trueif and only if*thiscontains a value.constexpr const T& value() const &; constexpr T& value() &;-13- Effects: Equivalent to:
return has_value() ? val*val: throw bad_optional_access();constexpr T&& value() &&; constexpr const T&& value() const &&;-14- Effects: Equivalent to:
return has_value() ? std::move(val*val) : throw bad_optional_access();template<class U> constexpr T value_or(U&& v) const &;-15- Mandates: […]
-16- Effects: Equivalent to:return has_value() ? val**this: static_cast<T>(std::forward<U>(v));template<class U> constexpr T value_or(U&& v) &&;-17- Mandates: […]
-18- Effects: Equivalent to:return has_value() ? std::move(val**this) : static_cast<T>(std::forward<U>(v));
Modify 22.5.3.8 [optional.monadic] as indicated:
template<class F> constexpr auto and_then(F&& f) &; template<class F> constexpr auto and_then(F&& f) const &;-1- Let
-2- Mandates: […] -3- Effects: Equivalent to:Ubeinvoke_result_t<F, decltype((val).*val)>if (*this) { return invoke(std::forward<F>(f), val*val); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto and_then(F&& f) &&; template<class F> constexpr auto and_then(F&& f) const &&;-4- Let
-5- Mandates: […] -6- Effects: Equivalent to:Ubeinvoke_result_t<F, decltype(std::move(val.*val))>if (*this) { return invoke(std::forward<F>(f), std::move(val*val)); } else { return remove_cvref_t<U>(); }template<class F> constexpr auto transform(F&& f) &; template<class F> constexpr auto transform(F&& f) const &;-7- Let
-8- Mandates:Uberemove_cv_t<invoke_result_t<F, decltype((val).*val)>>Uis a non-array object type other thanin_place_tornullopt_t. The declarationU u(invoke(std::forward<F>(f), val*val));is well-formed for some invented variable
[…] -9- Returns: Ifu.*thiscontains a value, anoptional<U>object whose contained value is direct-non-list-initialized withinvoke(std::forward<F>(f), val; otherwise,*val)optional<U>().template<class F> constexpr auto transform(F&& f) &&; template<class F> constexpr auto transform(F&& f) const &&;-10- Let
-11- Mandates:Uberemove_cv_t<invoke_result_t<F, decltype(std::move(val.*val))>>Uis a non-array object type other thanin_place_tornullopt_t. The declarationU u(invoke(std::forward<F>(f), std::move(val*val)));is well-formed for some invented variable
[…] -12- Returns: Ifu.*thiscontains a value, anoptional<U>object whose contained value is direct-non-list-initialized withinvoke(std::forward<F>(f), std::move(val; otherwise,*val))optional<U>().
Modify 22.5.3.9 [optional.mod] as indicated:
constexpr void reset() noexcept;-1- Effects: If
-2- Postconditions:*thiscontains a value, callsto destroy the contained value; otherwise no effect.val->val.T::~T()*thisdoes not contain a value.
std::views::split on an empty rangeSection: 25.7.17.3 [range.split.iterator], 25.7.16.3 [range.lazy.split.outer] Status: New Submitter: David Stone Opened: 2023-11-19 Last modified: 2024-06-24
Priority: 3
View all issues with New status.
Discussion:
Consider the following example (which uses fmt::println instead of std::println,
but they do the same thing in C++23):
#include <iostream>
#include <string>
#include <ranges>
#include <fmt/ranges.h>
int main()
{
fmt::println("{}", std::views::split(std::string(" x "), ' '));
fmt::println("{}", std::views::split(std::string(" "), ' '));
fmt::println("{}", std::views::split(std::string("x"), ' '));
fmt::println("{}", std::views::split(std::string(""), ' '));
}
The output of this program (as specified today) is
[[], ['x'], []] [[], []] [['x']] []
The principle set out in LWG 3478(i) is that splitting a sequence containing N
delimiters should lead to N+1 subranges. That principle was broken if the N-th
delimiter was at the end of the sequence, which was fixed by P2210.
split_view::iterator's constructor unconditionally initializes
trailing_empty_ to false. Instead, change 25.7.17.3 [range.split.iterator]/1
to initialize trailing_empty_ to cur_ == next_.begin() (i.e.
trailing_empty_ is typically false, but if we're empty on initialization then we
have to have a trailing empty range).
The following demo shows Barry Revzin's implementation from P2210, adjusted to fix this:
godbolt.org/z/axWb64j9f
[2024-03-11; Reflector poll]
Set priority to 3 after reflector poll.
[2024-03; Reflector comments]
"For split, we need to adjust the definition of end() for the
common_range case
(which may require introducing a new constructor to the iterator);
right now it would compare ranges::end(base_) against a value-initialized
iterator, which is not in the domain of ==.
For lazy_split, we need to also change the non-forward overload."
"What should splitting an empty range on an empty pattern produce? Right now the behavior is that splitting a range of N > 0 elements with an empty pattern produces a range of N single-element ranges. I suppose you can argue that an empty pattern matches between adjacent elements but not at the start or end, so that an empty range, like a single-element range, contains 0 delimiters so should produce a range of one empty range. But it's also at least arguable that this should produce an empty range instead, so that we maintain the N element <-> N subrange and 1 element per subrange invariant.
Proposed resolution:
This wording is relative to N4964.
Modify 25.7.17.3 [range.split.iterator] as indicated:
constexpr iterator(split_view& parent, iterator_t<V> current, subrange<iterator_t<V>> next);-1- Effects: Initializes
parent_withaddressof(parent),cur_withstd::move(current),andnext_withstd::move(next), andtrailing_empty_withcur_ == next_.begin().
Modify 25.7.16.3 [range.lazy.split.outer] as indicated:
constexpr outer-iterator(Parent& parent, iterator_t<Base> current) requires forward_range<Base>;-3- Effects: Initializes
parent_withaddressof(parent),andcurrent_withstd::move(current), andtrailing_empty_withcurrent_ == ranges::end(parent.base_).
ranges::to's copy branch is underconstrainedSection: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: Hewill Kang Opened: 2023-11-25 Last modified: 2024-03-11
Priority: 3
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with New status.
Discussion:
Unlike other branches that return a prvalue C, this branch's C is returned by elidable move,
indicating that C needs to be move constructible (demo):
#include <ranges>
#include <vector>
struct nonmovable {
nonmovable() = default;
nonmovable(const nonmovable&) = delete;
nonmovable& operator=(const nonmovable&) = delete;
};
template<class T>
struct nonmovable_vector : std::vector<T>, nonmovable { };
int main() {
int arr[] = {42};
auto v = std::ranges::to<nonmovable_vector<int>>(arr); // hard error
}
[2024-03-11; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4964.
Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);-1- Mandates:
Cis a cv-unqualified class type.-2- Returns: An object of type
Cconstructed from the elements ofrin the following manner:
(2.1) — If
Cdoes not satisfyinput_rangeorconvertible_to<range_reference_t<R>, range_value_t<C>>istrue:
(2.1.1) — […]
(2.1.2) — […]
(2.1.3) — […]
(2.1.4) — Otherwise, if
(2.1.4.?) —
move_constructible<C>istrue,(2.1.4.1) —
constructible_from<C, Args...>istrue, and(2.1.4.2) —
container-insertable<C, range_reference_t<R>>istrue:C c(std::forward<Args>(args)...); if constexpr (sized_range<R> && reservable-container<C>) c.reserve(static_cast<range_size_t<C>>(ranges::size(r))); ranges::copy(r, container-inserter<range_reference_t<R>>(c));(2.1.5) — Otherwise, the program is ill-formed.
(2.2) — […]
(2.3) — Otherwise, the program is ill-formed.
Section: 25.7.21 [range.reverse] Status: SG9 Submitter: Barry Revzin Opened: 2023-11-25 Last modified: 2024-11-14
Priority: 3
View all other issues in [range.reverse].
View all issues with SG9 status.
Discussion:
Consider the following:
auto a = views::iota(0) | views::reverse; auto b = views::repeat(42) | views::reverse;
Here, views::iota(0) and views::repeat(42) are both non-common bidirectional (even random-access) ranges.
They are also infinite ranges, even if the standard doesn't really recognize that.
views::reverse on a non-common range will actually compute the end iterator for you. So while both declarations
of a and b above compile, attempting to use either in any way will lead to an infinite loop when you
try a.begin() or b.begin().
A reddit post suggested disallowing reversing a non-common range but that likely breaks reasonable use-cases. We could at
the very least recognize ranges whose sentinel is unreachable_t and reject those from consideration. For instance,
we could change 24.4.4.4 [range.iter.op.next]/3 to Mandate that S is not unreachable_t.
[2024-03-11; Reflector poll]
Set priority to 3 after reflector poll. Ask SG9 to look. Probably needs a paper.
Infinite ranges are invalid and giving an invalid range to the library is undefined. But this is not a particularly satisfactory answer given that we provide such ranges ourselves...
[St. Louis 2024-06-28; LWG and SG9 joint session]
Poll: SG9 and LWG believe this is not a defect?
|SF| F| N| A|SA| | 3| 3| 0| 2| 0|Weak consensus => needs to go to LEWG
Poll: SG9 and LWG agree that standard could do something to create fewer sharp edges here, and we encourage a paper exploring options.
|SF| F| N| A|SA| | 4| 2| 2| 0| 0|
[2024-11-14; Related to LWG 4097(i).]
Proposed resolution:
mdspan::is_always_meow() should be noexceptSection: 23.7.3.6.1 [mdspan.mdspan.overview] Status: New Submitter: Stephan T. Lavavej Opened: 2023-12-07 Last modified: 2025-10-21
Priority: 3
View all issues with New status.
Discussion:
It can easily be proven that mdspan::is_always_meow() is preconditionless and can never throw exceptions.
microsoft/STL has already shipped a noexcept-strengthened implementation, libc++ would like to (see
llvm/llvm-project#74254), and together we believe that the
Standard should simply mandate noexcept so users can rely on this. The proof is:
N4964 23.7.3.6.1 [mdspan.mdspan.overview]/3:
LayoutPolicyshall meet the layout mapping policy requirements (23.7.3.4.3 [mdspan.layout.policy.reqmts]) […]
23.7.3.4.3 [mdspan.layout.policy.reqmts]/1:
A type
MPmeets the layout mapping policy requirements if for a typeEthat is a specialization ofextents,MP::mapping<E>is valid and denotes a typeXthat meets the layout mapping requirements (23.7.3.4.2 [mdspan.layout.reqmts]) […]
23.7.3.4.2 [mdspan.layout.reqmts]/1, /1.5, /22, /24, /26:
A type
[…]Mmeets the layout mapping requirements if
— the following types and expressions are well-formed and have the specified semantics.
[…]M::is_always_unique()Result: A constant expression (7.7 [expr.const]) of typebool. […]M::is_always_exhaustive()Result: A constant expression (7.7 [expr.const]) of typebool. […]M::is_always_strided()Result: A constant expression (7.7 [expr.const]) of typebool.
Constant expressions can surely be obtained without throwing exceptions.
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
Use if consteval may lead to exception thrown from invocation
of is_always_meow, when evaluated at runtime.
Design intent was for is_always_meow functions to always
produce value at compile time. Layout requirements may need to be updated.
Separately, 16.3.2.4 [structure.specifications] says that Result: is just the type and value category; not "constant expression" - this matters because we need to require a prvalue constant expression here.
Proposed resolution:
This wording is relative to N4964.
Modify 23.7.3.6.1 [mdspan.mdspan.overview], class template mdspan synopsis, as indicated:
[…]
static constexpr bool is_always_unique() noexcept
{ return mapping_type::is_always_unique(); }
static constexpr bool is_always_exhaustive() noexcept
{ return mapping_type::is_always_exhaustive(); }
static constexpr bool is_always_strided() noexcept
{ return mapping_type::is_always_strided(); }
[…]
%CSection: 30.12 [time.format] Status: New Submitter: Jiang An Opened: 2023-12-08 Last modified: 2025-10-17
Priority: 3
View other active issues in [time.format].
View all other issues in [time.format].
View all issues with New status.
Discussion:
Table 101, [tab:time.format.spec] reads:
| Specifier | Replacement |
|---|---|
[…]
|
|
%C |
The year divided by 100 using floored division. If the result is a single decimal digit, itis prefixed with 0. The modified command %EC produces the locale's alternativerepresentation of the century. |
[…]
|
|
When the year is in range [-99, -10], it is unclear whether the result (in range [-9, -1])
is considered as a single decimal digit, and there is implementation divergence
(llvm/llvm-project#74727).
[2025-10-17; Reflector poll.]
Set priority to 3 after reflector poll.
Howard intended Option B, i.e. "-09", but Option B could possibly be interpreted to mean "0-9".
"Maybe something like the 0 is inserted before the sole digit, or after the sign, if any."
Previous resolution [SUPERSEDED]:
This wording is relative to N4964.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A:
Modify 30.12 [time.format], Table 101 [tab:time.format.spec], as indicated:
Table 101 — Meaning of conversion specifiers [tab:time.format.spec] Specifier Replacement […]%CThe year divided by 100using floored division. If the result isa single decimal digitin range[0, 9], it
is prefixed with0. The modified command%ECproduces the locale's alternative
representation of the century.[…]Option B:
Modify 30.12 [time.format], Table 101 [tab:time.format.spec], as indicated:
Table 101 — Meaning of conversion specifiers [tab:time.format.spec] Specifier Replacement […]%CThe year divided by 100using floored division. If the result isa single decimal digitin range[-9, 9], it
is prefixed with0. The modified command%ECproduces the locale's alternative
representation of the century.[…]
[2025-10-17; Jonathan provides new wording based on Option B]
Proposed resolution:
This wording is relative to N5014.
Modify 30.12 [time.format], Table 133 [tab:time.format.spec], as indicated:
Table 133 — Meaning of conversion specifiers [tab:time.format.spec] Specifier Replacement […]%CThe year divided by 100using floored division. If the result isa single decimal digit, it
is prefixed with0
in the range[-9, 9]then0is inserted before the single digit (after the sign, if present).
The modified command%ECproduces the locale's alternative
representation of the century.[…]
std::expected should propagate trivialitySection: 22.8.6.4 [expected.object.assign], 22.8.7.4 [expected.void.assign] Status: New Submitter: Jiang An Opened: 2023-12-16 Last modified: 2024-03-11
Priority: 2
View other active issues in [expected.object.assign].
View all other issues in [expected.object.assign].
View all issues with New status.
Discussion:
Currently, only copy and move constructors of std::expected are required to propagate triviality,
while copy and move assignment operators are not. Given that the assignment operators of std::optional
and std::variant are already required to propagate triviality, it seems to me that we should also
apply such requirements for std::expected.
[2024-03-11; Reflector poll]
Set priority to 2 after reflector poll in January 2024. A few votes for Tentatively Ready, others thought it needed more consideration.
Proposed resolution:
This wording is relative to N4971.
Modify 22.8.6.4 [expected.object.assign] as indicated:
constexpr expected& operator=(const expected& rhs);-2- Effects: […]
[…] -4- Remarks: This operator is defined as deleted unless:
[…]
-?- This operator is trivial if:
(?.1) —
is_trivially_copy_constructible_v<T>istrue, and(?.2) —
is_trivially_copy_assignable_v<T>istrue, and(?.3) —
is_trivially_destructible_v<T>istrue, and(?.4) —
is_trivially_copy_constructible_v<E>istrue, and(?.5) —
is_trivially_copy_assignable_v<E>istrue, and(?.6) —
is_trivially_destructible_v<E>istrue.constexpr expected& operator=(expected&& rhs) noexcept(see below);-5- Constraints: […]
[…] -8- Remarks: The exception specification is equivalent to: […]-?- This operator is trivial if:
(?.1) —
is_trivially_move_constructible_v<T>istrue, and(?.2) —
is_trivially_move_assignable_v<T>istrue, and(?.3) —
is_trivially_destructible_v<T>istrue, and(?.4) —
is_trivially_move_constructible_v<E>istrue, and(?.5) —
is_trivially_move_assignable_v<E>istrue, and(?.6) —
is_trivially_destructible_v<E>istrue.
Modify 22.8.7.4 [expected.void.assign] as indicated:
constexpr expected& operator=(const expected& rhs);-1- Effects: […]
[…] -3- Remarks: This operator is defined as deleted unlessis_copy_assignable_v<E>istrueandis_copy_constructible_v<E>istrue. -?- This operator is trivial ifis_trivially_copy_constructible_v<E>,is_trivially_copy_assignable_v<E>, andis_trivially_destructible_v<E>are alltrue.constexpr expected& operator=(expected&& rhs) noexcept(see below);-4- Effects: […]
[…] -6- Remarks: The exception specification is equivalent tois_nothrow_move_constructible_v<E> && is_nothrow_move_assignable_v<E>. -7- This operator is defined as deleted unlessis_move_constructible_v<E>istrueandis_move_assignable_v<E>istrue. -?- This operator is trivial ifis_trivially_move_constructible_v<E>,is_trivially_move_assignable_v<E>, andis_trivially_destructible_v<E>are alltrue.
std::is_(nothrow_)convertible should be reworded to avoid dependence on the return statementSection: 21.3.8 [meta.rel] Status: New Submitter: Jiang An Opened: 2023-12-18 Last modified: 2025-10-17
Priority: 2
View other active issues in [meta.rel].
View all other issues in [meta.rel].
View all issues with New status.
Discussion:
The current specification for std::is_convertible is sensitive to the requirements for the return
statements. As a result, the requirements were accidentally changed by P0135R1 and then changed back by
CWG issue 2426. The current revision of P2748 also plans to
change the wording for std::is_convertible to avoid actual behavioral changing.
std::is_convertible in a such way that is independent to return statements.
The proposed resolution matches what mainstream implementations do, and should resolve LWG 3400(i) together.
[2025-10-17; Reflector poll.]
Set priority to 2 after reflector poll.
"I think the P/R is either insufficient or redundant: either remove the words "To is neither array nor function type and" (because they are unnecessary assuming the P/R's definition of conv-dest must be well-formed) or else add "To is neither cv void nor array nor function type" (because the P/R's definition of conv-dest remains ill-formed in cases like is_convertible_v<int, void>."
"Might need to revisit the hack added to 8.8.4 [stmt.return] by P2748R5."
Proposed resolution:
This wording is relative to N4971.
Modify 21.3.8 [meta.rel] as indicated:
Table 49 — Type relationship predicates [tab:meta.rel] Template Condition Comments […] […] […] template<class From, class To> struct is_convertible;see below FromandToshall be
complete types,cv void, or arrays of
unknown bound. template<class From, class To> struct is_nothrow_convertible;is_convertible_v<From,is
To>trueandthe
conversion, as defined by
is_convertible, is known
not to throw any exceptions
either bothFromandTo
arecv void, or the function call
expression used for specifyingis_convertible
is non-throwing (7.6.2.7 [expr.unary.noexcept])FromandToshall be
complete types,cv void, or arrays of
unknown bound.[…] […] […] -5- The predicate condition for a template specialization
is_convertible<From, To>shall be satisfied if and only ifthe return expression in the following code would be well-formed, including any implicit conversions to the return type of the function:To test() { return declval<From>(); }
[Note 2: This requirement gives well-defined results for reference types, array types, function types, and cv
(?.1) — either both
FromandToarecv void, or(?.2) —
Tois neither array nor function type and the function call expressionconv-dest(declval<From>())would be well-formed when treated as an unevaluated operand, whereconv-destis a hypothetical function declared asvoid conv-dest(To) noexcept;.
void. — end note] Access checking is performed in a context unrelated toToandFrom. Only the validity of the immediate context of the expression of thereturn statement (8.8.4 [stmt.return])function call expression (7.6.1.3 [expr.call]) (including initialization of thereturnedparameter object or reference) is considered.
basic_string accidentally fails to meet the reversible container requirementsSection: 27.4.3.1 [basic.string.general] Status: New Submitter: Jan Schultke Opened: 2023-12-18 Last modified: 2024-03-15
Priority: 3
View all other issues in [basic.string.general].
View all issues with New status.
Discussion:
The complexity requirements for a reversible container (23.2.2.3 [container.rev.reqmts]) are that each
function must have constant complexity. The corresponding member functions in 27.4.3.4 [string.iterators]
have no complexity requirements, and basic_string unintentionally is not a reversible container
(unless the implementation coincidentally provides constant complexity member functions).
[2024-03-15; Reflector poll]
Set priority to 3 after reflector poll.
Could harmonize with vector by saying:
"A basic_string meets all of the requirements of a contiguous container
(23.2.2.2 [container.reqmts]) and of a reversible container
(23.2.2.3 [container.rev.reqmts])."
It's not a sequence container (see LWG 718(i))
but as per 23.2.2.5 [container.alloc.reqmts] p1 it is allocator-aware
(although it doesn't use construct and destroy).
Not comfortable removing the detailed descriptions for those members. We don't have the "Descriptions are only provided here for operations on [...] that are not described in one of these tables" wording that the containers have.
Proposed resolution:
This wording is relative to N4971.
[Drafting Note: The proposed wording is similar to the specification in 23.3.13.1 [vector.overview] p2 and suggests to simply strike 27.4.3.4 [string.iterators] because it doesn't say anything new compared to 23.2.2.2 [container.reqmts] and 23.2.2.3 [container.rev.reqmts].
Alternatively, one could add a Complexity: Constant. paragraph to each function in 27.4.3.4 [string.iterators], but that would be less clearer and would not explicitly saybasic_stringmeets the reversible container requirements. ]
Modify 27.4.3.1 [basic.string.general] as indicated:
-2- A specialization of
-3- In all cases,basic_stringis a contiguous container (23.2.2.2 [container.reqmts]) and a reversible container (23.2.2.3 [container.rev.reqmts]).[data(), data() + size()]is a valid range,data() + size()points at an object with valuecharT()(a "null terminator"), andsize() <= capacity()istrue.[…]namespace std { template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>> class basic_string { […] //27.4.3.4 [string.iterators],iterators constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // 27.4.3.5 [string.capacity], capacity […] }; […] }
Remove subclause 27.4.3.4 [string.iterators] in its entirety:
23.4.3.4 Iterator support [string.iterators]constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr const_iterator cbegin() const noexcept;[…]
-1- Returns: An iterator referring to the first character in the string.constexpr reverse_iterator rend() noexcept; constexpr const_reverse_iterator rend() const noexcept; constexpr const_reverse_iterator crend() const noexcept;
-4- Returns: An iterator which is semantically equivalent toreverse_iterator(begin()).
std::shared_ptrSection: 20.3.2.2.2 [util.smartptr.shared.const] Status: New Submitter: Jiang An Opened: 2023-12-25 Last modified: 2024-03-15
Priority: 4
View other active issues in [util.smartptr.shared.const].
View all other issues in [util.smartptr.shared.const].
View all issues with New status.
Discussion:
Currently, 20.3.2.2.2 [util.smartptr.shared.const]/3 and /9.1 says Y(*)[N] and Y(*)[],
however, they may be invalid types when Y is an array type of unknown bound or a function type.
Presumably, the constraints should be satisfied only when the mentioned Y(*)[N] or Y(*)[] is valid.
[2024-03-15; Reflector poll]
Set priority to 4 after reflector poll.
Jens pointed out that "convertible", as a core language concept, goes from "expression" to "type", not from "type" to "type".
Previous resolution [SUPERSEDED]:
This wording is relative to N4971.
Modify 20.3.2.2.2 [util.smartptr.shared.const] as indicated:
template<class Y> explicit shared_ptr(Y* p);-3- Constraints: When
[…]Tis an array type, the expressiondelete[] pis well-formed and eitherTisU[N]andY(*)[N]is a valid type and convertible toT*, orTisU[]andY(*)[]is a valid type and convertible toT*. WhenTis not an array type, the expressiondelete pis well-formed andY*is convertible toT*.template<class Y, class D> shared_ptr(Y* p, D d); template<class Y, class D, class A> shared_ptr(Y* p, D d, A a); template<class D> shared_ptr(nullptr_t p, D d); template<class D, class A> shared_ptr(nullptr_t p, D d, A a);-9- Constraints:
is_move_constructible_v<D>istrue, andd(p)is a well-formed expression. For the first two overloads:
(9.1) — If
Tis an array type, then eitherTisU[N]andY(*)[N]is a valid type and convertible toT*, orTisU[]andY(*)[]is a valid type and convertible toT*.(9.2) — If
Tis not an array type, thenY*is convertible toT*.[…]
[2024-03-15; Jonathan provides alternative wording]
Can we just use is_convertible_v<Y(*)[N], T*>?
With enable_if-style SFINAE an invalid type will cause substitution failure
and with a requires-clause the constraints won't be satisfied.
Either way we get the desired outcome.
Also, the delete expression is already required to be well-formed, which
rules out function types, so that part of the issue is NAD.
Proposed resolution:
This wording is relative to N4971.
Modify 20.3.2.2.2 [util.smartptr.shared.const] as indicated:
template<class Y> explicit shared_ptr(Y* p);-3- Constraints: When
Tis an array type, the expressiondelete[] pis well-formed and either:TisU[N]andY(*)[N]is convertible toT*, orTisU[]andY(*)[]is convertible toT*.
- —
is_bounded_array_v<T> && is_convertible_v<Y(*)[rank_v<T>], T*>istrue, or- —
is_unbounded_array_v<T> && is_convertible_v<Y(*)[], T*>istrue.When
Tis not an array type, the expressiondelete pis well-formed andY*is convertible toT*is_convertible_v<Y*, T*>istrue.[…]
template<class Y, class D> shared_ptr(Y* p, D d); template<class Y, class D, class A> shared_ptr(Y* p, D d, A a); template<class D> shared_ptr(nullptr_t p, D d); template<class D, class A> shared_ptr(nullptr_t p, D d, A a);-9- Constraints:
is_move_constructible_v<D>istrue, andd(p)is a well-formed expression. For the first two overloads:
(9.1) — If
Tis an array type, then either:TisU[N]andY(*)[N]is convertible toT*, orTisU[]andY(*)[]is convertible toT*.
- —
is_bounded_array_v<T> && is_convertible_v<Y(*)[rank_v<T>], T*>istrue, or- —
is_unbounded_array_v<T> && is_convertible_v<Y(*)[], T*>istrue.(9.2) — If
Tis not an array type, thenY*is convertible toT*is_convertible_v<Y*, T*>istrue.[…]
Section: 16.4.5.3.3 [macro.names] Status: New Submitter: Alisdair Meredith Opened: 2023-12-27 Last modified: 2024-08-02
Priority: 3
View all other issues in [macro.names].
View all issues with New status.
Discussion:
There are several places where the standard talks about what happens when including a header, and only sometimes do we address the notion of importing a header unit, or importing a standard library module. One global concern is that 16.4.5.3.3 [macro.names] prohibits users defining macros with a name that matches a name from any standard library header, but only if a standard library header is included. We should maintain that same prohibition on importing a standard header unit, and importing a standard library module. In general, we probably want some front matter to address the include vs. import wording issue in one place, rather than trying to update every usage — if it is already there I could not find it. It is also likely that some uses will want to say specific things about import vs. include and those cases will be handed to audit for if we have blanket front-matter wording. I also question whether "shall not" is the right way to prohibit users defining macros in their own code. Must a "shall not" restriction be diagnosed? I think the "right" answer is to make the program ill-formed, no diagnostic required. That is a foul answer, but UB does not really fit as there is no actual behavior to be undefined, unless we mean the process of translating the program is undefined, in which case IFNDR is the better way to say that. If we expect implementations to diagnose users defining macros that match library names we should say so more clearly, but I am not aware of any current tool-chain that does so.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll.
"Why should import std care if I #define vector potato?"
Proposed resolution:
This wording is relative to N4971.
Modify 16.4.5.3.3 [macro.names] as indicated:
-1- If a translation unit includes a standard header, imports a standard header unit, or imports a standard library module, and provides a name specified in the standard library as an argument to
-2- A translation unit shall not#defineor#undef, the program is ill-formed, no diagnostic requiredA translation unit that includes a standard library header shall not.#defineor#undefnames declared in any standard library header#defineor#undefnames lexically identical to keywords, to the identifiers listed in Table [tab:lex.name.special], or to the attribute-tokens described in 9.13 [dcl.attr], except that the nameslikelyandunlikelymay be defined as function-like macros (15.7 [cpp.replace]).
std::min and std::maxSection: 26.8.9 [alg.min.max] Status: New Submitter: Jan Schultke Opened: 2023-12-29 Last modified: 2024-08-02
Priority: 4
View other active issues in [alg.min.max].
View all other issues in [alg.min.max].
View all issues with New status.
Discussion:
All standard libraries effectively use the same implementation for std::min(a, b),
namely something along the lines of
return __b < __a ? __b : __a;
However, the wording in 26.8.9 [alg.min.max] is not so clear:
Returns: The smaller value. Returns the first argument when the arguments are equivalent.
This leaves a few questions open:
How is the smaller value determined? Must it be through the less-than operator?
For std::max, can the "larger" value be determined through the greater-than operator or must it be through less-than?
[2024-08-02; Reflector poll]
Set priority to 4 after reflector poll. Suggestions for NAD. "The result can be determined in any way that is allowed by the constaints the function imposes on its arguments. Since the arguments must be Cpp17LessThanComparable, obviously it has to use a less-than operation, because it can't assume any other operation is possible." Some of the submitter's questions could be addressed with an editorial change.
Proposed resolution:
This wording is relative to N4971.
Modify 26.8.9 [alg.min.max] as indicated:
template<class T> constexpr const T& min(const T& a, const T& b);-?- Preconditions:
-?- Returns:Tmeets the Cpp17LessThanComparable requirements (Table 29).b < a ? b : a. -?- Remarks: An invocation may explicitly specify an argument for the template parameterT.template<class T, class Compare> constexpr const T& min(const T& a, const T& b, Compare comp);-?- Returns:
-?- Remarks: An invocation may explicitly specify an argument for the template parametercomp(b, a) ? b : a.T.template<class T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr const T& ranges::min(const T& a, const T& b, Comp comp = {}, Proj proj = {});-2- Returns:
-1- Preconditions: For the first form,Tmeets the Cpp17LessThanComparable requirements (Table 29).comp(proj(b), proj(a)) ? b : aThe smaller value. Returns the first argument when the arguments are equivalent.-3- Complexity: Exactly one comparison and two applications of the projection, if any.-4- Remarks: An invocation may explicitly specify an argument for the template parameterTof the overloads in namespacestd.template<class T> constexpr T min(initializer_list<T> r);-?- Preconditions:
-?- Returns: The leftmost elementranges::distance(r) > 0.Tmeets the Cpp17CopyConstructible (Table 32) and Cpp17LessThanComparable (Table 29) requirements.xinrwherey < xisfalsefor all subsequent elementsy. -?- Complexity: Exactlyranges::distance(r) - 1comparisons. -?- Remarks: An invocation may explicitly specify an argument for the template parameterT.template<class T, class Compare> constexpr T min(initializer_list<T> r, Compare comp);-?- Preconditions:
-?- Returns: The leftmost elementranges::distance(r) > 0.Tmeets the Cpp17CopyConstructible requirements (Table 32).xinrwherecomp(y, x)isfalsefor all subsequent elementsy. -?- Complexity: Exactlyranges::distance(r) - 1comparisons. -?- Remarks: An invocation may explicitly specify an argument for the template parameterT.template<copyable T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr T ranges::min(initializer_list<T> r, Comp comp = {}, Proj proj = {}); template<input_range R, class Proj = identity, indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less> requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*> constexpr range_value_t<R> ranges::min(R&& r, Comp comp = {}, Proj proj = {});-5- Preconditions:
-6- Returns: The leftmost elementranges::distance(r) > 0.For the overloads in namespacestd,Tmeets the Cpp17CopyConstructible requirements. For the first form,Tmeets the Cpp17LessThanComparable requirements (Table 29).xinrwherecomp(proj(y), proj(x))isfalsefor all subsequent elementsyThe smallest value in the input range. Returns a copy of the leftmost element when several elements are equivalent to the smallest. -7- Complexity: Exactlyranges::distance(r) - 1comparisons and twice as many applications of the projection, if any.-8- Remarks: An invocation may explicitly specify an argument for the template parameterTof the overloads in namespacestd.template<class T> constexpr const T& max(const T& a, const T& b);-?- Preconditions:
-?- Returns:Tmeets the Cpp17LessThanComparable requirements (Table 29).a < b ? b : a. -?- Remarks: An invocation may explicitly specify an argument for the template parameterT.template<class T, class Compare> constexpr const T& max(const T& a, const T& b, Compare comp);-?- Returns:
-?- Remarks: An invocation may explicitly specify an argument for the template parametercomp(a, b) ? b : a.T.template<class T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr const T& ranges::max(const T& a, const T& b, Comp comp = {}, Proj proj = {});-10- Returns:
-9- Preconditions: For the first form,Tmeets the Cpp17LessThanComparable requirements (Table 29).comp(proj(a), proj(b)) ? b : aThe larger value. Returns the first argument when the arguments are equivalent.-11- Complexity: Exactly one comparison and two applications of the projection, if any.-12- Remarks: An invocation may explicitly specify an argument for the template parameterTof the overloads in namespacestd.template<class T> constexpr T max(initializer_list<T> r);-?- Preconditions:
-?- Returns: The leftmost elementranges::distance(r) > 0.Tmeets the Cpp17CopyConstructible (Table 32) and Cpp17LessThanComparable (Table 29) requirements.xinrwherex < yisfalsefor all subsequent elementsy. -?- Complexity: Exactlyranges::distance(r) - 1comparisons. -?- Remarks: An invocation may explicitly specify an argument for the template parameterT.template<class T, class Compare> constexpr T max(initializer_list<T> r, Compare comp);-?- Preconditions:
-?- Returns: The leftmost elementranges::distance(r) > 0.Tmeets the Cpp17CopyConstructible requirements (Table 32).xinrwherecomp(x, y)isfalsefor all subsequent elementsy. -?- Complexity: Exactlyranges::distance(r) - 1comparisons. -?- Remarks: An invocation may explicitly specify an argument for the template parameterT.template<copyable T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr T ranges::max(initializer_list<T> r, Comp comp = {}, Proj proj = {}); template<input_range R, class Proj = identity, indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less> requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*> constexpr range_value_t<R> ranges::max(R&& r, Comp comp = {}, Proj proj = {});-13- Preconditions:
-14- Returns: The leftmost elementranges::distance(r) > 0.For the overloads in namespacestd,Tmeets the Cpp17CopyConstructible requirements. For the first form,Tmeets the Cpp17LessThanComparable requirements (Table 29).xinrwherecomp(proj(x), proj(y)isfalsefor all subsequent elementsyThe largest value in the input range. Returns a copy of the leftmost element when several elements are equivalent to the largest. -15- Complexity: Exactlyranges::distance(r) - 1comparisons and twice as many applications of the projection, if any.-16- Remarks: An invocation may explicitly specify an argument for the template parameterTof the overloads in namespacestd.
badbit in definition of
vprint_unicode/vprint_nonunicodeSection: 31.7.6.3.5 [ostream.formatted.print] Status: New Submitter: Jan Kokemüller Opened: 2024-01-13 Last modified: 2025-10-16
Priority: 4
View all other issues in [ostream.formatted.print].
View all issues with New status.
Discussion:
In section 31.7.6.3.5 [ostream.formatted.print], bullet 3.2 there is a mention of badbit:
any exception thrown by the call to
vformatis propagated without regard to the value ofos.exceptions()and without turning onios_base::badbitin the error state ofos.
Now the affected functions vprint_unicode and vprint_nonunicode are specified to behave as
formatted output functions (31.7.6.3.1 [ostream.formatted.reqmts]), which distinguishes two phases:
Output generation, which would call setstate(ios_base::failbit) in case of a failure
(which may throw an exception)
The actual output itself, which would call setstate(ios_base::badbit) in case of an exception
The vformat call is obviously part of the first phase (since it generates the output sequence and not
yet the actual output), and any failure here (such as an exception) would under regular formatted output function
rules set ios_base::failbit and not ios_base::badbit (the latter would indicate a loss
of the actual output sequence integrity and is therefore set by the second phase in case of an exception).
ios_base::badbit in (3.2) doesn't really make sense, it could only meaningfully
refer to ios_base::failbit instead.
[2025-10-16; Reflector poll]
Set priority to 4 after reflector poll.
"NAD. The reason it talks about badbit is that exceptions set badbit in IOStreams but not failbit. If we don’t want to hook into the stream’s error handling mentioning badbit is correct."
Previous resolution [SUPERSEDED]:
This wording is relative to N4971.
Modify 31.7.6.3.5 [ostream.formatted.print] as indicated:
void vprint_unicode(ostream& os, string_view fmt, format_args args); void vprint_nonunicode(ostream& os, string_view fmt, format_args args);-3- Effects: Behaves as a formatted output function (31.7.6.3.1 [ostream.formatted.reqmts]) of
os, except that:
(3.1) — failure to generate output is reported as specified below, and
(3.2) — any exception thrown by the call to
vformatis propagated without regard to the value ofos.exceptions()and without turning onios_base::failbitin the error state ofbadbitos.[…]
[2025-10-16; Daniel provides new wording]
Proposed resolution:
This wording is relative to N5014.
Modify 31.7.6.3.5 [ostream.formatted.print] as indicated:
void vprint_unicode(ostream& os, string_view fmt, format_args args); void vprint_nonunicode(ostream& os, string_view fmt, format_args args);-3- Effects: Behaves as a formatted output function (31.7.6.3.1 [ostream.formatted.reqmts]) of
os, except that:
(3.1) — failure to generate output is reported as specified below, and
(3.2) — any exception thrown by the call to
vformatis propagated without regard to the value ofos.exceptions()and without turning onios_base::failbitorios_base::badbitin the error state ofos.[…]
std::tuple_sizeSection: 22.4.7 [tuple.helper] Status: New Submitter: Jiang An Opened: 2024-01-20 Last modified: 2024-03-11
Priority: 3
View all other issues in [tuple.helper].
View all issues with New status.
Discussion:
22.4.7 [tuple.helper]/1 currently says:
All specializations of
tuple_sizemeet the Cpp17UnaryTypeTrait requirements (21.3.2 [meta.rqmts]) with a base characteristic ofintegral_constant<size_t, N>for someN.
which is probably wrong for std::tuple_size<int> and
std::tuple_size<std::vector<int>> whose definitions are not provided, and
contradicts with 22.4.7 [tuple.helper]/4
[…] Otherwise, it has no member
value.
Also, there's currently implementation divergence on whether std::tuple_size<cv T>
is a complete type when std::tuple_size<T>::value is absent (related to LWG 2770(i)).
Some implementations (e.g.
libc++
and libstdc++)
treat 22.4.7 [tuple.helper]/4 and D.15 [depr.tuple]/2 as "std::tuple_size<cv T>
is generated from partial specializations only if std::tuple_size<T> meets the aforementioned conditions",
while others (e.g. MSVC STL)
always consider std::tuple_size<cv T> needs to be defined. The latter
reading seems to be justified by 16.4.2.2 [contents]/1 and 22.4.2 [tuple.syn].
[2024-03-11; Reflector poll]
Set priority to 3 after reflector poll. The first part of the issue was fixed editorially.
Proposed resolution:
Section: 18.4.9 [concept.swappable] Status: New Submitter: Jiang An Opened: 2024-01-20 Last modified: 2024-03-15
Priority: 4
View other active issues in [concept.swappable].
View all other issues in [concept.swappable].
View all issues with New status.
Discussion:
18.4.9 [concept.swappable] bullet (2.3.1) currently requires T to be a literal type in order
to make the swapping expression a constant expression in that case. The requirement was likely automatically
enforced by the core language rules in C++20 and thus essentially redundant.
T is not a literal type.
E.g. the following program is accepted by GCC/libstdc++ in C++23 mode
(demo).
#include <concepts>
struct NonLiteral {
NonLiteral() {} // non-constexpr
constexpr NonLiteral(const NonLiteral&) noexcept {};
constexpr NonLiteral& operator=(const NonLiteral&) noexcept { return *this; };
};
int main()
{
NonLiteral x;
static_assert((std::ranges::swap(x, x), true));
}
IMO there's no good reason to additionally require literal types since C++23, which would complicate implementations.
[2024-03-15; Reflector poll]
Set priority to 4 after reflector poll.
Concerned about 7.7 [expr.const]/5.16 (can only modify non-volatile lvalues of literal type in constant expressions). Unable see a non-contrived case where this issue matters.
N.B. ranges::swap needs the "reified object" treatment;
the repetitions of E1 and E2 are not pure textual repetitions of the
argument expressions.
Can we just eliminate all uses of "literal type"?
Wouldn't we still require a constexpr destructor?
Proposed resolution:
This wording is relative to N4971.
Modify 18.4.9 [concept.swappable] as indicated:
-2- The name
ranges::swapdenotes a customization point object (16.3.3.3.5 [customization.point.object]). The expressionranges::swap(E1, E2)for subexpressionsE1andE2is expression-equivalent to an expressionSdetermined as follows:
(2.1) — […]
(2.2) — […]
(2.3) — Otherwise, if
E1andE2are lvalues of the same typeTthat modelsmove_constructible<T>andassignable_from<T&, T>,Sis an expression that exchanges the denoted values.Sis a constant expression if
(2.3.1) —Tis a literal type (6.9.1 [basic.types.general]),(2.3.2) — both
E1 = std::move(E2)andE2 = std::move(E1)are constant subexpressions (3.15 [defns.const.subexpr]), and(2.3.3) — the full-expressions of the initializers in the declarations
T t1(std::move(E1)); T t2(std::move(E2));are constant subexpressions.
noexcept(S)is equal tois_nothrow_move_constructible_v<T> && is_nothrow_move_assignable_v<T>.(2.4) — Otherwise,
ranges::swap(E1, E2)is ill-formed.[…]
Section: 23.2.7.2 [associative.reqmts.except], 23.6.8.1 [flat.map.overview], 23.6.9.1 [flat.multimap.overview], 23.6.11.1 [flat.set.overview], 23.6.12.1 [flat.multiset.overview] Status: New Submitter: Jiang An Opened: 2024-01-23 Last modified: 2024-03-15
Priority: 2
View all issues with New status.
Discussion:
The issue was originally discovered while implementing flat_(multi_)set in MSVC STL
(microsoft/STL#4059).
vector or a deque generally needs to assign elements,
which is not guaranteed to be non-throwing, it seems unreasonable to require erase of flat container
adaptors not to throw in such a case. Likewise, as inserting a single value into deque is not
guaranteed to be effectless when an exception is thrown, so flat container adaptors are unlike to provide such
a guarantee.
Perhaps we should conditionally relax the requirements when the underlying containers can't provide them.
[2024-03-15; Reflector poll]
Set priority to 2 after reflector poll.
P2767 §3.5 is related/conflicting.
Proposed resolution:
std::swap should not be supportedSection: 16.4.5.2.1 [namespace.std] Status: New Submitter: Jiang An Opened: 2024-01-25 Last modified: 2024-03-15
Priority: 4
View other active issues in [namespace.std].
View all other issues in [namespace.std].
View all issues with New status.
Discussion:
std::swap overloads are separately declared in many standard library headers. It is likely expected
that when a program includes correct headers for standard library types to be swapped, a std::swap call,
with template argument deduced from arguments, selects the correct overload.
std::swap call, there would be possibly
undesired overloads for which templates arguments are fully obtained, and thus undesired instantiation and
ill-formed may be triggered.
Perhaps we should explicitly state that explicitly specifying templates for std::swap is unsupported
(having unspecified effects and possibly ill-formed), like taking address of non-address functions
(16.4.5.2.1 [namespace.std]/6) and explicitly specifying template arguments for most standard algorithms
(26.2 [algorithms.requirements]/15), to avoid possible reading that there are some guarantees, derived
from signatures of overloads, for doing so.
[2024-03-15; Reflector poll]
Set priority to 4 after reflector poll.
NAD - if a user does this and it breaks, so be it.
NAD as an issue creating a new kind of library policy for the first time. Needs a paper.
Not unique to std::swap. We should forbid this throughout
the standard library.
Closely related to LWG 2146(i).
Proposed resolution:
std::flat_map/std::flat_setSection: 23.6.11 [flat.set], 23.6.8.1 [flat.map.overview] Status: New Submitter: Jiang An Opened: 2024-01-26 Last modified: 2024-03-15
Priority: 2
View all issues with New status.
Discussion:
The preconditions for transparent insertion of associative containers (23.4.3.4 [map.modifiers]/13,
23.4.3.4 [map.modifiers]/29, and 23.4.6.4 [set.modifiers]/3) detect the results of equal_range,
while those for std::flat_set and std::flat_map (23.6.11.5 [flat.set.modifiers]/2 and
23.6.8.7 [flat.map.modifiers]/20) currently detect the results of find, which is inconsistent.
std::flat_set in MSVC STL, it was reported
(microsoft/STL#4105) that the current preconditions
for std::flat_set::insert can lead to inconsistent results. Tim Song told that the current preconditions
were copied from old revisions of P2363. So, presumably we should change these preconditions for
flat container adaptors to consistently use equal_range.
[2024-03-15; Reflector poll]
Set priority to 2 after reflector poll.
We didn't want equal_range(k) == equal_range(u) because the two sides of the
== need to be evaluated at different times.
It seems to me that equal_range(k) == equal_range(u) ought to be
true before the insertion, and still true after the insertion.
The wording in 23.4.6.4 [set.modifiers]/3 and
23.4.3.4 [map.modifiers]/13 requires only that the condition be true
before the insertion.
We either want to change the wording here to be consistent with those places;
or else change 23.4.6.4 [set.modifiers] and 23.4.3.4 [map.modifiers]
to be consistent with the saner wording here. I'd vote for the latter.
Proposed resolution:
This wording is relative to N4971.
Modify 23.6.8.7 [flat.map.modifiers] as indicated:
template<class K, class... Args> pair<iterator, bool> try_emplace(K&& k, Args&&... args); template<class K, class... Args> iterator try_emplace(const_iterator hint, K&& k, Args&&... args);[…]
-20- Preconditions: The conversion fromkintokey_typeconstructs an objectu, for whichisfind(k) == find(u)equal_range(k) == equal_range(u)true.
Modify 23.6.11.5 [flat.set.modifiers] as indicated:
template<class K> pair<iterator, bool> insert(K&& x); template<class K> iterator insert(const_iterator hint, K&& x);[…]
-2- Preconditions: The conversion fromxintovalue_typeconstructs an objectu, for whichisfind(x) == find(u)equal_range(x) == equal_range(u)true.
<foo.h> headers not in freestandingSection: 16.4.2 [organization] Status: New Submitter: Ben Craig Opened: 2024-01-26 Last modified: 2024-08-21
Priority: 3
View all issues with New status.
Discussion:
There are many C-ish <cfoo> headers in freestanding. There isn't anything that
requires the matching C <foo.h> headers to be in freestanding.
[2024-08-21; Reflector poll]
Set priority to 3 after reflector poll.
Some discomfort requiring errno.h and wchar.h to be freestanding in C++
when they are not in C23. string.h is not freestanding in C17, but is in C23
(but C++ is still based on C17).
stdlib.h is "conditionally partially freestanding" in C23, but not in C17.
Suggestion to incorporate this into P3348.
Proposed resolution:
This wording is relative to N4971.
Modify 16.4.2.3 [headers] as indicated:
-9- 17.15 [support.c.headers], C standard library headers, describes the effects of using the
name.h(C header) form in a C++ program. Thename.hform is the corresponding C header form.
Modify 16.4.2.5 [compliance] as indicated:
-2- A freestanding implementation has an implementation-defined set of headers. This set shall include at least the headers shown in Table 27 and the corresponding C header form (16.4.2.3 [headers]) of each of the C++ headers for C library facilities (Table 25) included in Table 27.
Section: 17.12.2 [cmp.categories] Status: New Submitter: Corentin Jabot Opened: 2024-01-31 Last modified: 2025-10-23
Priority: 3
View all other issues in [cmp.categories].
View all issues with New status.
Discussion:
Comparison categories can only be compared to the literal 0.
It does not make sense for them to be comparable to anything else, so conceptually, the design of
P0515 makes sense, however in practice it's a pain point from users and implementations
alike, as the desired semantics is barely implementable.
One implementation strategy is to use nullptr_t. This produces warnings in implementations
because using a 0 pointer constant is not recommended, and filtering out these warnings would be unreliable.
Another implementation strategy is to require a consteval expression but
This permits 1-1, which ought to be totally fine and yet it exposes users to UB
It is not SFINAE friendly (and attempts at SFINAE are presumably UB).
And there are use cases where SFINAE friendliness is important, notably testing frameworks.
The status quo has engendered multiple issues being reported to at least 3 vendors Suggestion:Allow any 0 constant expression
Require compile magic to SFINAE on non zero values
Remove the gratuitous UB
The proposed wording requires compiler magic and has been implemented in clang. (Other possible way to implement that would for example be a magic type attribute, or a magic type)
Related vendor issues: GCC Bugzilla issue 96278 GCC Bugzilla issue 100903 LLVM issue 43670 LLVM pulls request 79465 Microsoft STL issue 4359 Microsoft STL pull request 3581 Visual Studio issue 10509214 snitch issue 140[2025-10-23; Reflector poll.]
Set priority to 3 after reflector poll.
We should prefer rejecting the undefined expression at compile time, rather than leading to UB at runtime.
There is implementation divergence for SFINAE checks between the standard
library implementations. The has_a_value(std::strong_ordering::equal)
is false for libstdc++, and true for libc++, MSVC:
auto has_a_value(const auto& t) {
if constexpr (requires { t.has_value(); }) {
return t.has_value();
} else if (constexpr (requires { t != nullptr; }) {
return t != nullptr;
} else {
return true;
}
}
Proposed resolution:
This wording is relative to N4971.
Modify 17.12.2.1 [cmp.categories.pre] as indicated:
-3- The relational and equality operators for the comparison category types are specified with an anonymous parameter of unspecified type. This type shall be selected by the implementation such that these parameters can only accept an integral constant expression evaluating to
literal0as a corresponding argument.[Example 1:nullptr_tmeets this requirement. — end example]In this context, the behavior of a program that supplies an argument other than a literal0is undefined.
piecewise_linear_distributionSection: 29.5.9.6.2 [rand.dist.samp.pconst], 29.5.9.6.3 [rand.dist.samp.plinear] Status: New Submitter: Jonathan Wakely Opened: 2024-02-05 Last modified: 2025-10-11
Priority: 4
View all other issues in [rand.dist.samp.pconst].
View all issues with New status.
Discussion:
In the second constructor of 29.5.9.6.3 [rand.dist.samp.plinear], P1719R2 replaced:
withtemplate<class InputIteratorB, class InputIteratorW> piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB, InputIteratorW firstW);Effects: [...] Moreover, the id-expressions
iterator_traits<InputIteratorB>::value_typeanditerator_traits<InputIteratorW>::value_typeshall each denote a type that is convertible todouble.
template<class InputIteratorB, class InputIteratorW> piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB, InputIteratorW firstW);Mandates:
is_invocable_r_v<double, UnaryOperation&, double>istrue.Preconditions: [...]
Effects: [...]
This was a copy & paste error from the next constructor,
and was obviously not intended. There is no UnaryOperation
in that constructor.
A less obviously wrong issue is the use of double there
in the first place. Shouldn't it be RealType instead?
That seems to be incorrect throughout both
29.5.9.6.2 [rand.dist.samp.pconst]
and
29.5.9.6.3 [rand.dist.samp.plinear],
and was only partially fixed by LWG 1439(i).
Finally, the preconditions also say:
Preconditions: [...] IffirstB == lastBor++firstB == lastB, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, [firstB,lastB) forms a sequence b of length n + 1, the length of the sequence w starting fromfirstWis at least n, and any wk for k ≥ n are ignored by the distribution.
These are not preconditions. I think it is trying to say something like:
Preconditions: [...] [
firstB,lastB) is a valid range and [firstW,firstW + (lastB - firstB)) is a valid range.Effects: If
firstB == lastBor++firstB == lastB, let n = 1, ρ0 = ρ1 = 1, b0 = 0, and b1 = 1. Otherwise, let [firstB,lastB) form a sequence b0, …, bn, and let wk =*firstW++for k = 0, …, n.
The equivalent constructor for piecewise_constant_distribution
has similar problems with its preconditions in terms of n + 1.
[2024-03-12; Reflector poll]
Set priority to 4 after reflector poll. The copy & paste error was fixed editorially.
[2025-10-06; Hewill comments and provides wording]
We should fix the constructor, otherwise the following should be rejected according to the standard:
#include <random>
struct Op {
float operator()(float);
void operator()(auto) = delete;
};
static_assert(!std::is_invocable_r_v<double, Op&, double>);
int main() {
std::initializer_list<float> l;
std::piecewise_linear_distribution<float> dist(l, Op{});
}
This is, because it violates Mandates. However, all three compilers accept it because
Op& can take float and return float.
Proposed resolution:
This wording is relative to N5014.
Modify 29.5.9.6.2 [rand.dist.samp.pconst] as indicated:
template<class InputIteratorB, class InputIteratorW> piecewise_constant_distribution(InputIteratorB firstB, InputIteratorB lastB, InputIteratorW firstW);-4- Mandates: Both of
(4.1) —
is_convertible_v<iterator_traits<InputIteratorB>::value_type, result_typedouble>(4.2) —
is_convertible_v<iterator_traits<InputIteratorW>::value_type, result_typedouble>are
[…]true.template<class UnaryOperation> piecewise_constant_distribution(initializer_list<RealType> bl, UnaryOperation fw);-7- Mandates:
[…]is_invocable_r_v<result_typeisdouble, UnaryOperation&, result_typedouble>true.template<class UnaryOperation> piecewise_constant_distribution(size_t nw, RealType xmin, RealType xmax, UnaryOperation fw);-10- Mandates:
[…]is_invocable_r_v<result_typeisdouble, UnaryOperation&, result_typedouble>true.
Modify 29.5.9.6.3 [rand.dist.samp.plinear] as indicated:
template<class InputIteratorB, class InputIteratorW> piecewise_linear_distribution(InputIteratorB firstB, InputIteratorB lastB, InputIteratorW firstW);-4- Mandates: Both of
(4.1) —
is_convertible_v<iterator_traits<InputIteratorB>::value_type, result_typedouble>(4.2) —
is_convertible_v<iterator_traits<InputIteratorW>::value_type, result_typedouble>are
[…]true.template<class UnaryOperation> piecewise_linear_distribution(initializer_list<RealType> bl, UnaryOperation fw);-7- Mandates:
[…]is_invocable_r_v<result_typeisdouble, UnaryOperation&, result_typedouble>true.template<class UnaryOperation> piecewise_linear_distribution(size_t nw, RealType xmin, RealType xmax, UnaryOperation fw);-10- Mandates:
[…]is_invocable_r_v<result_typeisdouble, UnaryOperation&, result_typedouble>true.
std::distance is missing a preconditionSection: 24.4.3 [iterator.operations] Status: New Submitter: Jan Schultke Opened: 2024-02-25 Last modified: 2024-03-12
Priority: 4
View other active issues in [iterator.operations].
View all other issues in [iterator.operations].
View all issues with New status.
Discussion:
std::distance for random access iterators is defined in terms of
(last - first) (24.4.3 [iterator.operations] p5) for
Cpp17RandomAccessIterators.
Table 91: Cpp17RandomAccessIterator requirements (in addition to Cpp17BidirectionalIterator) [tab:randomaccessiterator] Expression Return type Operational semantics Assertion/note
pre-/post-condition[…]b - adifference_typereturn n;Preconditions: there exists a
valuenof typedifference_type
such thata + n == b.
b == a + (b - a).
For example, pointer subtraction is undefined if the result isn't representable
as std::ptrdiff_t, and user-defined types with random access iterators
aren't required to have a difference which is always representable by
difference_type.
std::distance(&a, &b) can't be well-defined when
&b - &a is not, so std::distance is missing a precondition.
[2024-03-12; Reflector poll]
Set priority to 4 after reflector poll.
The proposed change is wrong, the new wording only associates with the second
condition, but should also apply when "last is reachable from
first".
Previous resolution [SUPERSEDED]:
This wording is relative to N4971.
Modify 24.4.3 [iterator.operations] as indicated:
template<class InputIterator> constexpr typename iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last);-4-Preconditions:
-5- Effects: Iflastis reachable fromfirst, orInputIteratormeets the Cpp17RandomAccessIterator requirements,andfirstis reachable fromlast, andtypename iterator_traits<InputIterator>::difference_typecan represent the result of this function call.InputIteratormeets the Cpp17RandomAccessIterator requirements, returns(last - first); otherwise, incrementsfirstuntillastis reached and returns the number of increments.
[2024-03-12; Jonathan provides improved wording]
Proposed resolution:
This wording is relative to N4971.
Modify 24.4.3 [iterator.operations] as indicated:
template<class InputIterator> constexpr typename iterator_traits<InputIterator>::difference_type distance(InputIterator first, InputIterator last);-4-Preconditions:
-5- Effects: Iflastis reachable fromfirst, orInputIteratormeets the Cpp17RandomAccessIterator requirements andfirstis reachable fromlast. The return type can represent the result.InputIteratormeets the Cpp17RandomAccessIterator requirements, returns(last - first); otherwise, incrementsfirstuntillastis reached and returns the number of increments.
generator::iterator's operator* is not noexcept when it can beSection: 25.8.6 [coro.generator.iterator] Status: New Submitter: S. B. Tam Opened: 2024-03-01 Last modified: 2024-09-17
Priority: 3
View other active issues in [coro.generator.iterator].
View all other issues in [coro.generator.iterator].
View all issues with New status.
Discussion:
generator::iterator's operator* is specified to have the following noexcept-specifier:
noexcept(is_nothrow_copy_constructible_v<reference>)
When reference is an rvalue reference type, is_nothrow_copy_constructible_v<reference>
is false (because reference is not copy constructible), which means operator*
is not noexcept.
operator* doesn't perform any potentially-throwing operation in this case. It's effect is
equivalent to return static_cast<reference>(*p.value_); (where the type of p.value_
is effectively add_pointer_t<reference>). Neither the dereference nor the cast to rvalue
reference can throw an exception.
I think the expression inside the noexcept-specifier should be changed to
noexcept(static_cast<reference>(*p.value_)).
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll.
"Just remove the noexcept-specifier."
"Maybe add is_reference_v<reference> || ..."
[2024-09-16; Casey provides wording]
In the prioritization thread, LWG discussed a couple of alternatives without much enthusiasm. One of the alternatives was to simply remove the noexcept-specifier, which seems to be the most efficient use of LWG's time.
Proposed resolution:
This wording is relative to N4988.
Modify 25.8.6 [coro.generator.iterator] as indicated:
namespace std { template<class Ref, class Val, class Allocator> class generator<Ref, Val, Allocator>::iterator { […] reference operator*() const[…]noexcept(is_nothrow_copy_constructible_v<reference>); […] }; }-3- Returns:
*this.reference operator*() constnoexcept(is_nothrow_copy_constructible_v<reference>l);-4- Preconditions: For some generator object
x,coroutine_is in*x.active_andx.active_->top()refers to a suspended coroutine with promise objectp.
std::to_address() should be SFINAE-friendlySection: 20.2.4 [pointer.conversion] Status: LEWG Submitter: Peter Kasting Opened: 2024-03-13 Last modified: 2025-10-23
Priority: 3
View all other issues in [pointer.conversion].
View all issues with LEWG status.
Discussion:
LWG 3545(i) made std::pointer_traits SFINAE-friendly.
However, std::to_address is still not required to be SFINAE-friendly.
This requires callers who wish to accept both pointer-like and non-pointer-like
types to guard calls with a constraint that reimplements the core logic of
to_address, such as the following:
template<typename T>
concept IsPointerLike = requires { typename std::pointer_traits<T>::pointer; }
|| requires (const T& t) { t.operator->(); };
Making to_address not be SFINAE-friendly
was seen as desirable,
so std::contiguous_iterator would produce a hard error for types marked with
contiguous_iterator_tag that do not properly support to_address.
Thus any fix should not regress that unless LWG explicitly decides to do so.
Also note that libc++'s current implementation of to_address,
which is SFINAE-friendly,
does not produce such a hard error.
[2025-10-23; Reflector poll; Status changed: New → LEWG and P3.]
There is support for both having std::to_address SFINAE friendly,
and producing hard error early, for iterators that define iterator_concept
as contiguous but fail to satisfy the concept due to not providing operator->().
LEWG should decide on which direction is prefered.
Proposed resolution:
This wording is relative to N4971.
Modify 20.2.4 [pointer.conversion] as indicated:
template<class Ptr> constexpr auto to_address(const Ptr& p) noexcept;-?- Constraints: Either the expression
pointer_traits<Ptr>::to_address(p)is well-formed (see 20.2.3.4 [pointer.traits.optmem]), or the expressionp.operator->()is well-formed.-3- Returns:
pointer_traits<Ptr>::to_address(p)if that expression is well-formed(see 20.2.3.4 [pointer.traits.optmem]), otherwiseto_address(p.operator->()).
Modify 24.3.4.14 [iterator.concept.contiguous] as indicated:
-1- The
contiguous_iteratorconcept provides a guarantee that the denoted elements are stored contiguously in memory.template<class I> concept contiguous_iterator = random_access_iterator<I> && derived_from<ITER_CONCEPT(I), contiguous_iterator_tag> && is_lvalue_reference_v<iter_reference_t<I>> && same_as<iter_value_t<I>, remove_cvref_t<iter_reference_t<I>>> &&requires(const I& i) { { to_address(i) } -> same_as<add_pointer_t<iter_reference_t<I>>>; }std::same_as<decltype([] { return std::to_address(std::declval<I>()); }()), std::add_pointer_t<std::iter_reference_t<I>>>;
[The submitter welcomes less awkward alternatives to achieve the desired hard error for contiguous_iterator.]
join_with_view's iteratorSection: 25.7.15.3 [range.join.with.iterator] Status: New Submitter: S. B. Tam Opened: 2024-03-23 Last modified: 2024-08-11
Priority: 3
View other active issues in [range.join.with.iterator].
View all other issues in [range.join.with.iterator].
View all issues with New status.
Discussion:
join_with_view's iterator is defined in terms of variant, visit and get,
which implies that the iterator operations throw bad_variant_access if the underlying variant is
valueless-by-exception (which can happen if an underlying iterator has a throwing copy constructor).
variant is an implementation detail and shouldn't be exposed. It's confusing for users to
get bad_variant_access when user code does not deal with variant.
The spec of join_with_view is also inconsistent with concat_view as recently added to the
working draft by P2542R8. The latter has "it_.valueless_by_exception() is
false" as a precondition.
I believe that join_with_view should similarly require that inner_it_.valueless_by_exception()
is false for each iterator operation.
(FWIW, MSVC STL implements join_with_view with a trimmed-down version of variant, and having
to throw bad_variant_access causes a small maintenance burden.)
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll. We like the suggestion to require "not valueless" as a precondition.
[2024-08-08, Inbal Levi provides wording]
Proposed resolution:
This wording is relative to N4988.
Modify 25.7.15.3 [range.join.with.iterator] as indicated:
constexpr iterator(iterator<!Const> i) requires Const && convertible_to<iterator_t<V>, OuterIter> && convertible_to<iterator_t<InnerRng>, InnerIter> && convertible_to<iterator_t<Pattern>, PatternIter>;-?- Preconditions:
-10- Effects: […]inner_it_.valueless_by_exception()isfalse.constexpr decltype(auto) operator*() const;-?- Preconditions:
-12- Effects: Equivalent to: […]inner_it_.valueless_by_exception()isfalse.constexpr iterator& operator++();-?- Preconditions:
-13- Effects: Equivalent to: […]inner_it_.valueless_by_exception()isfalse.constexpr void operator++(int);-?- Preconditions:
-14- Effects: Equivalent to:inner_it_.valueless_by_exception()isfalse.++*this.constexpr iterator operator++(int) requires ref-is-glvalue && forward_iterator<OuterIter> && forward_iterator<InnerIter>;-?- Preconditions:
-15- Effects: Equivalent to: […]inner_it_.valueless_by_exception()isfalse.constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> && bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;-?- Preconditions:
-16- Effects: Equivalent to: […]inner_it_.valueless_by_exception()isfalse.
ranges::empty has no semantic requirements for forward_rangesSection: 25.3.13 [range.prim.empty] Status: New Submitter: Hewill Kang Opened: 2024-03-30 Last modified: 2025-10-20
Priority: 3
View all issues with New status.
Discussion:
This is a small part of issues raised by already-closed P3156.
Currently,ranges::empty (25.3.13 [range.prim.empty]) is always a valid expression when applied
to forward_ranges, because even if it does not have .empty() or .size() we can always
check whether it is empty by comparing its begin() and end(), which is reflected in the
standard preference to use it to check whether a certain forward_range is empty,
such as in the Effects of split_view::find-next() (25.7.17.2 [range.split.view])
and cartesian_product_view::end() (25.7.33.2 [range.cartesian.view]).
In addition, MSVC-STL also uses ranges::empty in the implementation
of ranges::contains_subrange for the check.
However, unlike ranges::size, which has a sized_range concept to ensure semantics,
ranges::empty has no corresponding one. This makes it lack of time complexity guarantees and semantics
for the meaning of the returned value when using the bullets (2.2) and (2.3) of 25.3.13 [range.prim.empty]
to check emptiness.
Perhaps we need to add semantic requirements for ranges::empty, but this seems
inconsistent with the current wording as no other CPOs have.
Alternatively, maybe we could move bullets targeting forward_ranges to the very beginning so that
ranges::empty always has correct semantics with bool(ranges::begin(t) == ranges::end(t)) when
applied to forward_ranges.
[2025-10-20; Reflector poll.]
Set priority to 3 after reflector poll.
"This is the part of P3156 that is actually a defect.
We need to require ranges::empty to actually mean what we think it means.
Would be reasonable to add a sematic requirement to the range concept
that if ranges::empty(r) is well-formed then it does what it should do
(equal to bool(begin == end), has amortized constant complexity,
does not modify r, is not required to be valid after begin for non-forward)."
Proposed resolution:
std::char_traits<char>::eof depends on non-freestanding EOFSection: 27.2.4.2 [char.traits.specializations.char] Status: New Submitter: Jiang An Opened: 2024-04-03 Last modified: 2024-05-08
Priority: 2
View all other issues in [char.traits.specializations.char].
View all issues with New status.
Discussion:
Currently EOF is not yet freestanding, because it's only specified to be provided in
<stdio.h> in C, or together with <cstdio> in C++.
std::char_traits<char>::eof (required to be freestanding since C++26 by
P2338R4) is required to return the value of EOF. It's unclear what is
expected to be done in a freestanding implementation.
Related to LLVM issue #85158.
[2024-05-08; Reflector poll]
Set priority to 2 after reflector poll.
"The eof, not_eof, to_int_type and to_char_type members of char_traits
are only needed by iostreams, so I see no reason for them to be freestanding.
In libstdc++ eof and not_eof depend on hosted, so are not freestanding.
Now that we can have partially freestanding classes in the library,
that seems like the right solution here."
"int_type/char_type/eof is kind of poor-man (or C)'s
optional<char_type>. Inclined to support it in freestanding
on that basis, even if the only standard component that uses it is iostreams."
"No implementer is confused about the value of eof()/EOF.
Having the value specified in the non-freestanding part of the standard
should be good enough."
"Prefer to make EOF freestanding, but don't feel strongly about it.
The important thing is keeping string_view working."
Proposed resolution:
Section: 16.4.4.6.1 [allocator.requirements.general] Status: New Submitter: Jiang An Opened: 2024-04-07 Last modified: 2024-05-01
Priority: 3
View other active issues in [allocator.requirements.general].
View all other issues in [allocator.requirements.general].
View all issues with New status.
Discussion:
Currently, 16.4.4.6.1 [allocator.requirements.general]/2.12 imposes a precondition for
pointer_traits::pointer_to that r denotes an object whose storage is allocated
by the allocator.
pointer_traits::pointer_to
works on any lvalue.
[2024-05-01; Reflector poll]
Set priority to 3 after reflector poll.
"This applies to small-object optimizations too."
"An alternative way to support P0773's "near pointers" would be to require that a sentinel-node container using a "near allocator" must itself be constructed in the near segment. (This is P0773 (B) (2).)"
Proposed resolution:
This wording is relative to N4971.
Modify 16.4.4.6.1 [allocator.requirements.general] as indicated:
-2- In subclause 16.4.4.6 [allocator.requirements],
[…]
(2.10) —
pdenotes a value of typeXX::pointerobtained by callinga1.allocate, wherea1 == a,(2.11) — […],
(2.12) —
rdenotes an lvalue of typeT&obtained by the expression,*p[…]
[…]
pointer_traits<XX::pointer>::pointer_to(r)-33- Result:
-34- Postconditions:XX::pointerSame asLetpp1denote the return value.*p1refers to the same object asr.
ranges::to should reserve when sized_sentinel_for is satisfiedSection: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: Hewill Kang Opened: 2024-04-13 Last modified: 2025-10-20
Priority: 4
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with New status.
Discussion:
ranges::to currently only reserves when r satisfies sized_range.
However, we can also extract its size when r is an input_range that its sentinel-iterator
pair satisfies sized_sentinel_for.
ranges::begin(r) anyway.
[2025-02-27; post-Hagenberg status]
The proposed resolution needs rebasing after P2846R6 was approved in Hagenberg. It also conflicts with LWG 3722(i).
[2025-10-20; Reflector poll.]
Set priority to 4 after reflector poll. Most votes were "P4 or NAD".
"I do not see a motivating example of input_range that would produce
sized_sentinel_for for begin/end but would not be sized itself.
However, the fix seems correct.
Would prefer if would check if produced subrange is sized,
instead of using a requires expression."
"Might as well do it, even though a sized_sentinel_for-but-not-sized
input range feels like an extreme corner case."
"This is just applying the contrived case mentioned in LWG 3737(i)
to ranges::to."
Previous resolution [SUPERSEDED]:
This wording is relative to N4981.
Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);-1- Mandates:
Cis a cv-unqualified class type.-2- Returns: An object of type
Cconstructed from the elements ofrin the following manner:
(2.1) — […]
(2.1.1) — […]
(2.1.2) — […]
(2.1.3) — […]
(2.1.4) — Otherwise, if
(2.1.4.1) —
constructible_from<C, Args...>istrue, and(2.1.4.2) —
container-appendable<C, range_reference_t<R>>istrue:C c(std::forward<Args>(args)...); subrange s{r}; if constexpr (sized_range<R>requires { s.size(); } && reservable-container<C>) c.reserve(static_cast<range_size_t<C>>(ranges::size(r)s.size())); ranges::for_each(rs, container-append(c));
[2025-10-20; Jonathan provides updated wording]
Proposed resolution:
This wording is relative to N5014.
Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<class C, input_range R, class... Args> requires (!view<C>) constexpr C to(R&& r, Args&&... args);-1- Mandates:
Cis a cv-unqualified class type.-2- Returns: An object of type
Cconstructed from the elements ofrin the following manner:
(2.1) — […]
(2.1.1) — […]
(2.1.2) — […]
(2.1.3) — […]
(2.1.4) — Otherwise, if
(2.1.4.1) —
constructible_from<C, Args...>istrue, and(2.1.4.2) —
container-appendable<C, range_reference_t<R>>istrue:C c(std::forward<Args>(args)...); subrange s{r}; if constexpr (sized_range<Rdecltype(s)> && reservable-container<C>) c.reserve(static_cast<range_size_t<C>>(ranges::size(r)s.size())); ranges::for_each(rs, container-append(c));
std::chrono::zoned_time's constructorsSection: 30.11.7.2 [time.zone.zonedtime.ctor] Status: New Submitter: Jiang An Opened: 2024-04-14 Last modified: 2024-06-24
Priority: 3
View all other issues in [time.zone.zonedtime.ctor].
View all issues with New status.
Discussion:
Currently, there are no requirements on a program-defined std::chrono::zoned_traits specialization.
So a zoned_traits<UserDefinedTzPtr>::locate_zone possibly returns a string_view,
which leads to infinite meta-recursion when checking the constraints of zoned_time's constructors
that take a string_view.
zoned_time taking only one string_view is inconsistent with others
and even self-inconsistent. For other constructors taking string_view, it is expected that the return
value of locate_zone is only implicitly converted to a TimeZonePtr. But the return value is
permitted to be convertible to zoned_time or sys_time<Duration> in this constructor.
And given the locate_name function may be an overload set that distinguishes lvalues and rvalues of
string_view, the use of locate_time(string_view{}) in the constraints doesn't always reflect
locate_time(name) in the effects as the return types can be different.
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll.
"Such a UserDefinedTzPtr would be ridiculous and not useful,
but it wouldn't hurt to tighten the spec a bit."
"Would prefer a much simpler resolution saying 'dont do that'.
I don't like that locatable-as-tzptr doesn't look like
a dependent expression, but it is really.
Saying 'TimeZonePtr is implicit convertible from its return type'
should be phrased in terms of converting to, and should talk about
conversion from an expression to a type (see LWG 3105(i))."
Proposed resolution:
This wording is relative to N4971.
Modify 30.11.7.2 [time.zone.zonedtime.ctor] as indicated:
[Drafting note: Add the following two paragraphs to the very beginning of subclause 30.11.7.2 [time.zone.zonedtime.ctor]]
-?- Let
-?- In every constructor that takes alocatable-as-tzptrbetrueif given an lvaluesvof typestring_view,traits::locate_zone(sv)is well-formed andTimeZonePtris implicitly convertible from its return type, andfalseotherwise. Only the validity of the immediate context of the invocation and conversion is considered.string_viewparametername, letconverted-tzptrbe a variable of typeTimeZonePtrthat is copy-initialized fromtraits::locate_zone(name). […]explicit zoned_time(string_view name);-7- Constraints:
-8- Effects: Initializestraits::locate_zone(string_view{})is a well-formed expression andzoned_timeis constructible from the return type oftraits::locate_zone(string_view{})locatable-as-tzptristrue.zone_withtraits::locate_zone(name)and default constructstp_.[…]
zoned_time(string_view name, const sys_time<Duration>& st);-13- Constraints:
-14- Effects: Equivalent to construction withzoned_timeis constructible from the return type oftraits::locate_zone(name)andstlocatable-as-tzptristrue.{.traits::locate_zone(name)std::move(converted-tzptr), st}[…]
zoned_time(string_view name, const local_time<Duration>& tp);-18- Constraints:
-19- Effects: Equivalent to construction withlocatable-as-tzptristrueandzoned_timeis constructible fromthe return type oftraits::locate_zone(name)TimeZonePtrandtp.{.traits::locate_zone(name)std::move(converted-tzptr), tp}[…]
zoned_time(string_view name, const local_time<Duration>& tp, choose c);-23- Constraints:
-24- Effects: Equivalent to construction withlocatable-as-tzptristrueandzoned_timeis constructible fromthe return type oftraits::locate_zone(name)TimeZonePtr,local_time<Duration>, andchoose.{.traits::locate_zone(name)std::move(converted-tzptr), tp, c}[…]
template<class Duration2, class TimeZonePtr2> zoned_time(string_view name, const zoned_time<Duration2, TimeZonePtr2>& y);-32- Constraints:
-33- Effects: Equivalent to construction withlocatable-as-tzptristrueandzoned_timeis constructible fromthe return type oftraits::locate_zone(name)TimeZonePtrand the typezoned_time<Duration2, TimeZonePtr2>.{.traits::locate_zone(name)std::move(converted-tzptr), y}template<class Duration2, class TimeZonePtr2> zoned_time(string_view name, const zoned_time<Duration2, TimeZonePtr2>& y, choose c);-34- Constraints:
-35- Effects: Equivalent to construction withlocatable-as-tzptristrueandzoned_timeis constructible fromthe return type oftraits::locate_zone(name)TimeZonePtr, the typezoned_time<Duration2, TimeZonePtr2>, and the typechoose.{.traits::locate_zone(name)std::move(converted-tzptr), y, c}
Section: 16.4.5.11 [res.on.requirements], 16.3.2.3 [structure.requirements] Status: New Submitter: Jonathan Wakely Opened: 2024-04-19 Last modified: 2025-10-14
Priority: 3
View other active issues in [res.on.requirements].
View all other issues in [res.on.requirements].
View all issues with New status.
Discussion:
During P2300 review the suggestion was raised that it might
be helpful to be able to talk about "an object that models Concept"
instead of "an object whose type models Concept" or other more verbose forms.
[2025-10-14; Reflector poll]
Set priority to 3 after reflector poll.
"OK with finding a way to shorten this, but not as suggested above. Overloading the term "models Concept" for both objects and types would be confusing."
"Just say 'a stoppable-source object',
or 'a stoppable_token object'"
Proposed resolution:
std::formatter<std::filesystem::path>Section: 31.12.6.9.2 [fs.path.fmtr.funcs] Status: Open Submitter: Jonathan Wakely Opened: 2024-04-19 Last modified: 2025-10-10
Priority: 2
View all issues with Open status.
Discussion:
31.12.6.9.2 [fs.path.fmtr.funcs] says:
IfcharTischar,path::value_typeiswchar_t, and the literal encoding is UTF-8, then the escaped path is transcoded from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with u+fffd replacement character per the Unicode Standard [...]. Otherwise, transcoding is implementation-defined.
This seems to mean that the Unicode substitutions are only done
for an escaped path, i.e. when the ? option is used. Otherwise, the form
of transcoding is completely implementation-defined.
However, this makes no sense.
An escaped string will have no ill-formed subsequences, because they will
already have been replaced as per 28.5.6.5 [format.string.escaped]:
Otherwise (X is a sequence of ill-formed code units), each code unit U is appended to E in order as the sequence\x{hex-digit-sequence}, wherehex-digit-sequenceis the shortest hexadecimal representation of U using lower-case hexadecimal digits.
So only unescaped strings can have ill-formed sequences by the time
we do transcoding to char, but whether or not any
u+fffd substitution
occurs is just implementation-defined.
I believe we want to specify the substitutions are done when transcoding an unescaped path (and it doesn't matter whether we specify it for escaped paths, because it's a no-op if escaping happens first, as is apparently intended).
It does matter whether we escape first or perform substitutions first.
If we escape first then every code unit in an ill-formed sequence is
individually escaped as \x{hex-digit-sequence}.
So an ill-formed sequence of two wchar_t values will be escaped as
two \x{...} strings, which are then transcoded to UTF-8.
If we transcode (with substitutions first) then the entire
ill-formed sequence is replaced with a single replacement character,
which will then be escaped as \x{fffd}.
SG16 should be asked to confirm that escaping first is intended,
so that an escaped string shows the original invalid code units.
For a non-escaped string, we want the ill-formed sequence to be
formatted as �, which the proposed resolution tries to ensure.
[2024-05-08; Reflector poll]
Set priority to 2 after reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N4981.
Modify 31.12.6.9.2 [fs.path.fmtr.funcs] as indicated:
template<class FormatContext> typename FormatContext::iterator format(const filesystem::path& p, FormatContext& ctx) const;-5- Effects: Letsbep.generic_string<filesystem::path::value_type>()if thegoption is used, otherwisep.native(). Writessintoctx.out(), adjusted according to the path-format-spec. IfcharTischar,path::value_typeiswchar_t, and the literal encoding is UTF-8, then theescaped path(possibly escaped) string is transcoded from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with u+fffd replacement character per the Unicode Standard, Chapter 3.9 u+fffd Substitution in Conversion. IfcharTandpath::value_typeare the same then no transcoding is performed. Otherwise, transcoding is implementation-defined.- Modify the entry in the index of implementation-defined behavior as indicated:
transcoding of a formattedpathwhencharTandpath::value_typediffer and not converting fromwchar_tto UTF-8
[2025-06-11; SG16 comments and improves wording]
The "and not converting from wchar_t to UTF-8" wording added in the index of implementation-defined
behavior by the current proposed resolution should be changed to "and the literal encoding is not UTF-8".
wchar_t to UTF-8" with "and the literal encoding
is not UTF-8". The optional change is to insert "ordinary" before "literal encoding" as well. Once that is done,
I'll have SG16 confirm they are content with the new proposed resolution.
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 31.12.6.9.2 [fs.path.fmtr.funcs] as indicated:
template<class FormatContext> typename FormatContext::iterator format(const filesystem::path& p, FormatContext& ctx) const;-5- Effects: Let
sbep.generic_string<filesystem::path::value_type>()if thegoption is used, otherwisep.native(). Writessintoctx.out(), adjusted according to the path-format-spec. IfcharTischar,path::value_typeiswchar_t, and the ordinary literal encoding is UTF-8, then theescaped path(possibly escaped) string is transcoded from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with u+fffd replacement character per the Unicode Standard, Chapter 3.9 u+fffd Substitution in Conversion. IfcharTandpath::value_typeare the same then no transcoding is performed. Otherwise, transcoding is implementation-defined.- Modify the entry in the index of implementation-defined behavior as indicated:
transcoding of a formattedpathwhencharTandpath::value_typediffer and the ordinary literal encoding is not UTF-8
[2025-07-30; SG16 meeting]
SG16 unanimously approved new wording produced during the discussion. The group concluded that the intended behavior would be best specified by introducing additional names to denote the sequence of transformations that produce the intended effect. Status updated SG16 → Open.
Proposed resolution:
This wording is relative to N5014.
Modify 31.12.6.9.2 [fs.path.fmtr.funcs] as indicated:
template<class FormatContext> typename FormatContext::iterator format(const filesystem::path& p, FormatContext& ctx) const;-5- Effects: Let
sbep.generic_stringif the<filesystem::path::value_type>()goption is used, otherwisep.native(). Lets2besadjusted according to the path-format-spec. Lets3be defined as follows:Writes
- (5.1) — If
charTischar,path::value_typeiswchar_t, and the ordinary literal encoding is UTF-8,s3is the result of transcodings2from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with U+FFFD REPLACEMENT CHARACTER per the Unicode Standard, Chapter 3.9 U+FFFD Substitution in Conversion.- (5.2) — If
charTandpath::value_typeare the same, thens3is the same ass2.- (5.3) — Otherwise,
s3is the result of an implementation-defined transcoding ofs2.s3intoctx.out().Writessintoctx.out(), adjusted according to the path-format-spec. IfcharTischar,path::value_typeiswchar_t, and the literal encoding is UTF-8, then the escaped path is transcoded from the native encoding for wide character strings to UTF-8 with maximal subparts of ill-formed subsequences substituted with u+fffd replacement character per the Unicode Standard, Chapter 3.9 u+fffd Substitution in Conversion. IfcharTandpath::value_typeare the same then no transcoding is performed. Otherwise, transcoding is implementation-defined.
transcoding of a formattedpathwhencharTandpath::value_typediffer and the ordinary literal encoding is not UTF-8
concat_view::size may overflowSection: 25.7.18.2 [range.concat.view] Status: New Submitter: Hewill Kang Opened: 2024-04-21 Last modified: 2024-06-24
Priority: 4
View other active issues in [range.concat.view].
View all other issues in [range.concat.view].
View all issues with New status.
Discussion:
Currently, concat_view::size returns the sum of sizes of all underlying ranges,
and the return type is specified to be the common type of all sizes, which may lead to
potential overflow:
auto r = std::views::iota(0uz, SIZE_MAX);
std::print("{}\n", r.size()); // 18446744073709551615 (size_t)
std::print("{}\n", r.end() - r.begin()); // 18446744073709551615 (__int128)
auto c = std::views::concat(r, r);
std::print("{}\n", c.size()); // 18446744073709551614 (size_t)
std::print("{}\n", c.end() - c.begin()); // 36893488147419103230 (__int128)
[2024-06-24; Reflector poll]
Set priority to 4 after reflector poll.
Same behaviour as join_view, was there some discussion of this being
covered by general wording?
Proposed resolution:
Section: 16.4.4 [utility.requirements] Status: SG1 Submitter: Detlef Vollmann Opened: 2024-04-21 Last modified: 2024-06-24
Priority: 3
View other active issues in [utility.requirements].
View all other issues in [utility.requirements].
View all issues with SG1 status.
Discussion:
P0260R8 had a requirement for constructors and destructors of concurrent queue value types to return on the same thread where it was called.
During the discussion in Tokyo, SG1 decided this to be a general issue and not specific to concurrent queues.[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll. Send to SG1.
Proposed resolution:
std::ranges::generate_randomSection: 26.12.2 [alg.rand.generate] Status: New Submitter: Jiang An Opened: 2024-04-21 Last modified: 2024-08-02
Priority: 2
View all other issues in [alg.rand.generate].
View all issues with New status.
Discussion:
Per discussion in cplusplus/draft#6904,
the preconditions of std::ranges::generate_random are specified in an unusual way.
It also appears that the "shall be equivalent" imposes requirements on both implementation and user code,
and the requirements are not very clear.
Perhaps we should clarify the conditions in usual Preconditions paragraphs.
[2024-08-02; Reflector poll]
Set priority to 2 after reflector poll. When reviewing this we were clear this imposes requirements on users.
Proposed resolution:
std::format_to?Section: 28.5.5 [format.functions] Status: New Submitter: Jan Schultke Opened: 2024-04-24 Last modified: 2025-02-07
Priority: 4
View all other issues in [format.functions].
View all issues with New status.
Discussion:
int main() {
int x = 12345;
// note: this is "awoo" followed by 28 zeros (9.5.3 [dcl.init.string] p3)
char buffer[32] = "awoo";
std::format_to(buffer, "{}{}", x, buffer);
std::println("{}", buffer);
}
The output of this code is unspecified to be either "1234512345" or "12345awoo",
where GCC currently outputs the former. Formatting occurs through function calls
(see 28.5.6.4 [format.formatter.spec] p1) and those cannot be unsequenced, however, it's also
nowhere stated in what order the arguments get formatted and how the output iterator is advanced
between calls to the formatters.
f.format(u, fc)).
[2025-02-07; Reflector poll]
Set priority to 4 after reflector poll.
We should prohibit aliasing, so this is undefined.
Proposed resolution:
Section: 24.3.2 [iterator.assoc.types] Status: New Submitter: Jiang An Opened: 2024-04-26 Last modified: 2024-06-24
Priority: 3
View all issues with New status.
Discussion:
Given an iterator type I, currently both std::iter_value_t<I> and
std::iterator_traits<I>::value_type can be called the value type of I.
These two types can be different if one specializes std::indirect_readable_traits for
I such that std::indirect_readable_traits<I>::value_type is different
from I::value_type.
std::iter_value_t<I> and non-ranges algorithms,
along with other legacy iterator-related mechanisms, use std::iterator_traits<I>::value_type.
But this doesn't seem clarified anywhere.
P2408R5 made the situation more complicated. In C++23, the type requirements of
non-mutating iterators passed to many legacy algorithms were changed to C++20 iterator concepts,
which are strongly associated with std::iter_value_t. It didn't seem intended that
implementations were expected to use possibly different value types since C++23, but such reading
might be suggested by the use of concepts.
Likewise, a non-random-access iterator type I can have two different difference types
if one specializes std::incrementable_traits for I such that
std::incrementable_traits<I>::difference_type is different from I::difference_type.
It's also unclear which one should be used for non-mutating iterator in algorithms.
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
concat_view::iterator::operator- is overconstrainedSection: 25.7.18.3 [range.concat.iterator] Status: New Submitter: Hewill Kang Opened: 2024-04-26 Last modified: 2024-08-02
Priority: 3
View other active issues in [range.concat.iterator].
View all other issues in [range.concat.iterator].
View all issues with New status.
Discussion:
Currently, two concat_view::iterators can only be subtracted if
concat-is-random-access is satisfied, which seems overconstrained since the implementation
does not rely on all underlying ranges being random_access_range or common_range.
operator- mainly based on whether it satisfies
sized_sentinel_for rather than being category-specific.
For example, counted_iterators that only model input_iterator can still be subtracted.
We have no reason to reject the following:
std::list l = {1, 2, 3};
auto r = l | std::views::take(3);
auto c = std::ranges::concat_view{r};
auto it = c.begin();
it++;
auto d = it - c.begin(); // error: no match for 'operator-'
The proposed resolution lists a minimal constraint formula based on the implementation details of operator-.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.18.3 [range.concat.iterator] as indicated:
[…]namespace std::ranges { template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && concatable<Views...> template<bool Const> class concat_view<Views...>::iterator { public: […] friend constexpr difference_type operator-(const iterator& x, const iterator& y) requiresconcat-is-random-access<Const, Views...>see below; […] }; }friend constexpr difference_type operator-(const iterator& x, const iterator& y) requiresconcat-is-random-access<Const, Views...>see below;-32- Preconditions:
x.it_.valueless_by_exception()andy.it_.valueless_by_exception()are eachfalse.-33- Effects: Let
ixdenotex.it_.index()andiydenotey.it_.index().
(33.1) — if
ix > iy, letdyberanges::distance(std::get<iy>(y.it_), ranges::end(std::get<iy>(y.parent_->views_))),dxberanges::distance(ranges::begin(std::get<ix>(x.parent_->views_)), std::get<ix>(x.it_)). Letsdenote the sum of the sizes of all the rangesstd::get<i>(x.parent_->views_)for every integeriin the range[iy + 1, ix)if there is any, and0otherwise, of typedifference_type, equivalent to:return dy + s + dx;(33.2) — otherwise, if
ix < iyistrue, equivalent to:return -(y - x);(33.3) — otherwise, equivalent to:
return std::get<ix>(x.it_) - std::get<iy>(y.it_);-?- Remarks: Let
Fsbe the pack that consists of all elements ofViewsexcept the last element, and letRsbe the pack that consists of all elements ofViewsexcept the first element. The expression in the requires-clause is equivalent to:(sized_sentinel_for<iterator_t<maybe-const<Const, Views>>, iterator_t<maybe-const<Const, Views>>> && ...) && (sized_sentinel_for<sentinel_t<maybe-const<Const, Fs>>, iterator_t<maybe-const<Const, Fs>>> && ...) && all-forward<Const, Rs...>
Section: 17.9.3 [exception] Status: SG16 Submitter: Victor Zverovich Opened: 2024-04-28 Last modified: 2024-05-08
Priority: 3
View all other issues in [exception].
View all issues with SG16 status.
Discussion:
The null-terminated multibyte string returned by the what method of std::exception
and its subclasses in the standard has an unspecified encoding. The closest thing in the specification
is the "suitable for conversion and display as a wstring" part in Remarks
(17.9.3 [exception] p6) but it is too vague to be useful because anything can be converted to
wstring in one way or another:
virtual const char* what() const noexcept;Returns: An implementation-defined ntbs.
Remarks: The message may be a null-terminated multibyte string (16.3.3.3.4.3 [multibyte.strings]), suitable for conversion and display as awstring(27.4 [string.classes], 28.3.4.2.5 [locale.codecvt]). The return value remains valid until the exception object from which it is obtained is destroyed or a non-constmember function of the exception object is called.
As a result, it is impossible to portably use the exception message, e.g. print it. Since exception messages are commonly combined with string literals and are often constructed from string literals, at the very least the standard should say that the message is compatible with them, i.e. that it is in the ordinary literal encoding or its subset.
To give a specific example of this problem, consider the following code compiled on Windows with Microsoft Visual C++, the ordinary literal encoding of UTF-8 and the system locale set to Belarusian (the language of the text in this example):
std::uintmax_t size = 0;
try {
size = std::filesystem::file_size(L"Шчучыншчына");
} catch (const std::exception& e) {
std::print("Памылка: {}", e.what());
}
Since both std::filesystem::path and std::print support Unicode one would expect this
to work and, when run, print a readable error message if the file "Шчучыншчына" doesn't exist. However,
the output will be corrupted instead. The reason for the corruption is that filesystem_error
requires including the path in the message but doesn't say that it should be transcoded
(31.12.7.2 [fs.filesystem.error.members] p7):
virtual const char* what() const noexcept;Returns: An ntbs that incorporates the
what_argargument supplied to the constructor. The exact format is unspecified. Implementations should include thesystem_error::what()string and the pathnames ofpath1andpath2in the native format in the returned string.
Therefore, the message will contain literal text in the ordinary literal encoding (UTF-8) combined with a
path, most likely in the operating system dependent current encoding for pathnames which in this case is CP1251.
So different parts of the output will be in two incompatible encodings and therefore unusable with
std::print or any other facility.
print since Microsoft STL doesn't implement std::print yet.
Replacing std::print with another output facility produces a different but equally unusable form
of mojibake.
[2024-05-04; Daniel comments]
The proposed wording is incomplete. There are about 12 other what specifications in the Standard
Library with exactly the same specification as exception::what that would either need to get the
same treatment or we would need general wording somewhere that says that the specification "contract" of
exception::what extends to all of its derived classes. A third choice could be that we introduce
a new definition such as an lntbs (or maybe "literal
ntbs") that is essentially an
ntbs in the ordinary literal encoding.
[2024-05-08; Reflector poll]
Set priority to 3 after reflector poll. Send to SG16.
Proposed resolution:
This wording is relative to N4981.
Modify 17.9.3 [exception] as indicated:
virtual const char* what() const noexcept;Returns: An implementation-defined ntbs in the ordinary literal encoding.
Remarks: The message may be a null-terminated multibyte string (16.3.3.3.4.3 [multibyte.strings]), suitable for conversion and display as awstring(27.4 [string.classes], 28.3.4.2.5 [locale.codecvt]). The return value remains valid until the exception object from which it is obtained is destroyed or a non-constmember function of the exception object is called.
concat_view::iterator's iter_swap is overconstrainedSection: 25.7.18.3 [range.concat.iterator] Status: New Submitter: Hewill Kang Opened: 2024-04-30 Last modified: 2024-06-24
Priority: 3
View other active issues in [range.concat.iterator].
View all other issues in [range.concat.iterator].
View all issues with New status.
Discussion:
When the underlying iterator of two concat_view::iterators are of different types,
their iter_swap will dispatch ranges::swap to swap elements, which is reflected
in its constraints of swappable_with<iter_reference_t<iterator>,
iter_reference_t<iterator>>.
ranges::swap will never
be invoked, making checking for swappable_with unnecessary in this case.
This results in the current wording rejecting the following:
struct I {
using value_type = int;
using difference_type = int;
std::reference_wrapper<int> operator*() const;
I& operator++();
void operator++(int);
bool operator==(const I&) const;
friend void iter_swap(const I&, const I&); // custom iter_swap
};
static_assert(std::indirectly_swappable<I>);
int main() {
std::ranges::subrange<I, I> s1, s2;
std::ranges::swap_ranges(s1, s2);
std::ranges::concat_view c1{s1}, c2{s2};
std::ranges::swap_ranges(c1, c2); // ill-formed
}
Because reference_wrapper does not satisfy swappable_with, concat_view::iterator
does not have a valid iter_swap, leading to the constraints of swap_ranges being unsatisfied.
iter_swap specialization
for concat_view::iterator is reasonable.
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll. An extreme corner case, probably not worth caring about.
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.18.3 [range.concat.iterator] as indicated:
namespace std::ranges { template<input_range... Views> requires (view<Views> && ...) && (sizeof...(Views) > 0) && concatable<Views...> template<bool Const> class concat_view<Views...>::iterator { public: using iterator_category = see below; // not always present. using iterator_concept = see below; using value_type = concat-value-t<maybe-const<Const, Views>...>; using difference_type = common_type_t<range_difference_t<maybe-const<Const, Views>>...>; private: static constexpr bool concat-is-homogeneous = see below; // exposition only using base-iter = // exposition only variant<iterator_t<maybe-const<Const, Views>>...>; […] friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below) requires concat-is-homogeneous && indirectly_swappable<iterator_t<maybe-const<Const, Views...[0]>>>; friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below) requiressee below(!concat-is-homogeneous) && swappable_with<iter_reference_t<iterator>, iter_reference_t<iterator>> && (... && indirectly_swappable<iterator_t<maybe-const<Const, Views>>>); } […] }-?-
-1-concat-is-homogeneousistrueif and only if the pack of typesiterator_t<maybe-const<Const, Views>>...are all the same.iterator::iterator_conceptis defined as follows: […]friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below) requires concat-is-homogeneous && indirectly_swappable<iterator_t<maybe-const<Const, Views...[0]>>>;-?- Preconditions:
-?- Effects: Equivalent to:x.it_.valueless_by_exception()andy.it_.valueless_by_exception()are eachfalse.std::visit(ranges::iter_swap, x.it_, y.it_);-?- Remarks: The exception specification is equivalent to
noexcept(ranges::iter_swap(std::get<0>(x.it_), std::get<0>(y.it_)))friend constexpr void iter_swap(const iterator& x, const iterator& y) noexcept(see below) requiressee below(!concat-is-homogeneous) && swappable_with<iter_reference_t<iterator>, iter_reference_t<iterator>> && (... && indirectly_swappable<iterator_t<maybe-const<Const, Views>>>);-42- Preconditions:
-43- Effects: Equivalent to:x.it_.valueless_by_exception()andy.it_.valueless_by_exception()are eachfalse.std::visit([&](const auto& it1, const auto& it2) { if constexpr (is_same_v<decltype(it1), decltype(it2)>) { ranges::iter_swap(it1, it2); } else { ranges::swap(*x, *y); } }, x.it_, y.it_);-44- Remarks: The exception specification is equivalent to
(noexcept(ranges::swap(*x, *y)) && ... && noexcept(ranges::iter_swap(its, its)))where
itsis a pack of lvalues of typeconst iterator_t<maybe-const<Const, Views>>respectively.
The expression in the requires-clause is equivalent toswappable_with<iter_reference_t<iterator>, iter_reference_t<iterator>> && (... && indirectly_swappable<iterator_t<maybe-const<Const, Views>>>)
std::formatSection: 28.5.2.2 [format.string.std] Status: Open Submitter: Jens Maurer Opened: 2024-04-30 Last modified: 2025-10-10
Priority: 3
View other active issues in [format.string.std].
View all other issues in [format.string.std].
View all issues with Open status.
Discussion:
There are std::format variants that take an explicit std::locale parameter.
There is the "L" format specifier that uses that locale (or some environment
locale) for formatting, according to 28.5.2.2 [format.string.std] p17:
"For integral types, the locale-specific form causes the context's locale to be used to insert the appropriate digit group separator characters."
It is unclear which specific facets are used to make this happen. This is important, because users can install their own facets into a given locale. Specific questions include:
Is num_put<> being used? Or just numpunct<>?
Are any of the _byname facets being used?
Assuming the encoding for char is UTF-8, the use of a user-provided
num_put<> facet (as opposed to std::format creating the output based on
numpunct<>) would allow digit separators that are not expressibly as a
single UTF-8 code unit.
[2024-05-08; Reflector poll]
Set priority to 3 after reflector poll.
[2024-06-12; SG16 meeting]
The three major implementations all use numpunct but not num_put,
clarify that this is the intended behaviour.
[2025-06-12; Jonathan provides wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 28.5.2.2 [format.string.std] as indicated:
-17- When theLoption is used, the form used for the conversion is called the locale-specific form. TheLoption is only valid for arithmetic types, and its effect depends upon the type.
- (17.1) — For integral types, the locale-specific form causes the context’s locale to be used to insert the appropriate digit group separator characters as if obtained with
numpunct<charT>::groupingandnumpunct<charT>::thousands_sep.- (17.2) — For floating-point types, the locale-specific form causes the context’s locale to be used to insert the appropriate digit group and radix separator characters as if obtained with
numpunct<charT>::grouping,numpunct<charT>::thousands_sep, andnumpunct<charT>::decimal_point.- (17.3) — For the textual representation of
bool, the locale-specific form causes the context’s locale to be used to insert the appropriate string as if obtained withnumpunct<charT>::truenameornumpunct<charT>::falsename.
[2025-08-27; SG16 meeting]
SG16 unanimously approved new wording from Victor. The new wording incorporates similar wording as added by P2419R2 to address 3565(i). Status updated SG16 → Open.
Proposed resolution:
This wording is relative to N5014.
Modify 28.5.2.2 [format.string.std] as indicated:
-17- When theLoption is used, the form used for the conversion is called the locale-specific form. TheLoption is only valid for arithmetic types, and its effect depends upon the type.If the string literal encoding is a Unicode encoding form and the locale is among an implementation-defined set of locales, each replacement that depends on the locale is performed as if the replacement character sequence is converted to the string literal encoding.
- (17.1) — For integral types, the locale-specific form causes the context’s locale to be used to insert the appropriate digit group separator characters as if obtained with
numpunct<charT>::groupingandnumpunct<charT>::thousands_sep.- (17.2) — For floating-point types, the locale-specific form causes the context’s locale to be used to insert the appropriate digit group and radix separator characters as if obtained with
numpunct<charT>::grouping,numpunct<charT>::thousands_sep, andnumpunct<charT>::decimal_point.- (17.3) — For the textual representation of
bool, the locale-specific form causes the context’s locale to be used to insert the appropriate string as if obtained withnumpunct<charT>::truenameornumpunct<charT>::falsename.
concat_view rejects non-movable referencesSection: 25.7.18.2 [range.concat.view] Status: New Submitter: Hewill Kang Opened: 2024-05-01 Last modified: 2024-06-24
Priority: 4
View other active issues in [range.concat.view].
View all other issues in [range.concat.view].
View all issues with New status.
Discussion:
In order to prevent non-equality-preserving behavior of operator* and iter_move,
concat_view introduces the concat-indirectly-readable concept, part of which is:
template<class Ref, class RRef, class It>
concept concat-indirectly-readable-impl = // exposition only
requires (const It it) {
{ *it } -> convertible_to<Ref>;
{ ranges::iter_move(it) } -> convertible_to<RRef>;
};
This isn't quite right because convertible_to checks is_convertible_v which doesn't
understand copy elision. This makes the current concat_view unable to work with ranges whose
reference is non-movable prvalue:
auto r = std::views::iota(0, 5)
| std::views::transform([](int) { return NonMovable{}; });
auto c1 = std::ranges::concat_view(r); // ill-formed, concat_indirectly_readable not satisfied
auto c2 = std::ranges::concat_view(r, r); // ditto
Since std::visit<R> is used in the implementation to perform reference conversion for the
underlying iterator, the more accurate one should be is_invocable_r which does understand guaranteed
elision.
join_with_view has the same issue because compatible-joinable-ranges
requires that the value_type of the inner range and pattern range must satisfy common_with,
which always fails for non-movable types. However, this can be automatically resolved by LWG
4074(i)'s resolution.
[2024-06-24; Reflector poll]
Set priority to 4 after reflector poll. "Proposed resolution loses the existing requirement that the conversion is equality-preserving." "Don't care about rejecting non-movable reference types."
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.18.2 [range.concat.view] as indicated:
-1- The exposition-only
concat-indirectly-readableconcept is equivalent to:template<class Ref, class CRef> concept concat-ref-compatible-with = is_invocable_r_v<CRef, Ref()>; // exposition only template<class Ref, class RRef, class It> concept concat-indirectly-readable-impl = // exposition only requires (const It it) { { *it } ->convertible_toconcat-ref-compatible-with<Ref>; { ranges::iter_move(it) } ->convertible_toconcat-ref-compatible-with<RRef>; }; template<class... Rs> concept concat-indirectly-readable = // exposition only common_reference_with<concat-reference-t<Rs...>&&, concat-value-t<Rs...>&> && common_reference_with<concat-reference-t<Rs...>&&, concat-rvalue-reference-t<Rs...>&&> && common_reference_with<concat-rvalue-reference-t<Rs...>&&, concat-value-t<Rs...> const&> && (concat-indirectly-readable-impl<concat-reference-t<Rs...>, concat-rvalue-reference-t<Rs...>, iterator_t<Rs>> && ...);
common_iterator::operator== is underconstrainedSection: 24.5.5.1 [common.iterator], 24.5.5.6 [common.iter.cmp] Status: New Submitter: Hewill Kang Opened: 2024-05-01 Last modified: 2024-07-21
Priority: 3
View other active issues in [common.iterator].
View all other issues in [common.iterator].
View all issues with New status.
Discussion:
common_iterator has the following equality operator:
template<class I2, sentinel_for<I> S2>
requires sentinel_for<S, I2>
friend constexpr bool operator==(
const common_iterator& x, const common_iterator<I2, S2>& y);
which is quite useful when wrapping a C++20 input_iterator that does not model equality_comparable
so that the quality operator required by the Cpp17InputIterator can still be synthesized.
I2 and I,
which allows a common_iterator wrapping two completely unrelated iterators to validly compare
(demo):
common_iterator<string::iterator, unreachable_sentinel_t> i1;
common_iterator<list<int>::iterator, unreachable_sentinel_t> i2;
i1 == i2; // unfortunately compile
The proposed resolution requires common_with<I, I2> to be satisfied to enhance semantics,
which is also consistent with the signature of counted_iterator::operator==.
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll.
Seems unlikely to be a problem in practice.
Proposed resolution would make the two operator== overloads ambiguous.
Previous resolution [SUPERSEDED]:
This wording is relative to N4981.
Modify 24.5.5.1 [common.iterator] as indicated:
namespace std { template<input_or_output_iterator I, sentinel_for<I> S> requires (!same_as<I, S> && copyable<I>) class common_iterator { public: […] template<classcommon_with<I> I2, sentinel_for<I> S2> requires sentinel_for<S, I2> friend constexpr bool operator==( const common_iterator& x, const common_iterator<I2, S2>& y); […] }; […] }Modify 24.5.5.6 [common.iter.cmp] as indicated:
template<classcommon_with<I> I2, sentinel_for<I> S2> requires sentinel_for<S, I2> friend constexpr bool operator==( const common_iterator& x, const common_iterator<I2, S2>& y);-1- Preconditions:
[…]x.v_.valueless_by_exception()andy.v_.valueless_by_exception()are eachfalse.
[2024-07-09; Hewill provides improved wording]
Proposed resolution:
This wording is relative to N4986.
Modify 24.5.5.1 [common.iterator] as indicated:
namespace std {
template<input_or_output_iterator I, sentinel_for<I> S>
requires (!same_as<I, S> && copyable<I>)
class common_iterator {
public:
[…]
template<classcommon_with<I> I2, sentinel_for<I> S2>
requires sentinel_for<S, I2>
friend constexpr bool operator==(
const common_iterator& x, const common_iterator<I2, S2>& y);
template<classcommon_with<I> I2, sentinel_for<I> S2>
requires sentinel_for<S, I2> && equality_comparable_with<I, I2>
friend constexpr bool operator==(
const common_iterator& x, const common_iterator<I2, S2>& y);
[…]
};
[…]
}
Modify 24.5.5.6 [common.iter.cmp] as indicated:
template<classcommon_with<I> I2, sentinel_for<I> S2> requires sentinel_for<S, I2> friend constexpr bool operator==( const common_iterator& x, const common_iterator<I2, S2>& y);-1- Preconditions:
[…]x.v_.valueless_by_exception()andy.v_.valueless_by_exception()are eachfalse.template<classcommon_with<I> I2, sentinel_for<I> S2> requires sentinel_for<S, I2> && equality_comparable_with<I, I2> friend constexpr bool operator==( const common_iterator& x, const common_iterator<I2, S2>& y);-3- Preconditions:
[…]x.v_.valueless_by_exception()andy.v_.valueless_by_exception()are eachfalse.
ranges::fold_left_first_with_iter incorrectly constructs optional<U>Section: 26.6.18 [alg.fold] Status: New Submitter: Hewill Kang Opened: 2024-05-03 Last modified: 2024-06-24
Priority: 3
View other active issues in [alg.fold].
View all other issues in [alg.fold].
View all issues with New status.
Discussion:
ranges::fold_left_first_with_iter constructs the initial value through
optional<U>(in_place, *first) which is not quite right, because the exposure
constraint only ensures that U can be constructed from the value type of the iterator
rather than its reference (demo):
#include <ranges>
#include <algorithm>
struct Op {
Op() = default;
Op(std::tuple<int>);
Op(std::tuple<int&>) = delete;
Op operator()(Op, std::tuple<int&>) const;
};
int main() {
std::ranges::fold_left_first_with_iter(
std::views::zip(std::views::single(0)),
Op{}
); // hard error in libstdc++ and MSVC-STL
}
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll. Unclear what "exposure constraint" means. "Don't want to require an extra copy to accommodate this corner case."
Proposed resolution:
This wording is relative to N4981.
Modify 26.6.18 [alg.fold] as indicated:
template<input_iterator I, sentinel_for<I> S indirectly-binary-left-foldable<iter_value_t<I>, I> F> requires constructible_from<iter_value_t<I>, iter_reference_t<I>> constexpr see below ranges::fold_left_first_with_iter(I first, S last, F f); template<input_range R, indirectly-binary-left-foldable<range_value_t<R>, iterator_t<R>> F> requires constructible_from<range_value_t<R>, range_reference_t<R>> constexpr see below ranges::fold_left_first_with_iter(R&& r, F f);-9- Let
Ubedecltype(ranges::fold_left(std::move(first), last, iter_value_t<I>(*first), f))-10- Effects: Equivalent to:
if (first == last) return {std::move(first), optional<U>()}; optional<U> init(in_place, iter_value_t<I>(*first)); for (++first; first != last; ++first) *init = invoke(f, std::move(*init), *first); return {std::move(first), std::move(init)};-11- Remarks: The return type is
fold_left_first_with_iter_result<I, optional<U>>for the first overload andfold_left_first_with_iter_result<borrowed_iterator_t<R>, optional<U>>for the second overload.
ranges::fold_meow is overconstrainedSection: 26.6.18 [alg.fold] Status: New Submitter: Hewill Kang Opened: 2024-05-03 Last modified: 2024-06-24
Priority: 3
View other active issues in [alg.fold].
View all other issues in [alg.fold].
View all issues with New status.
Discussion:
The two convertible_to constraints required by ranges::fold_meow mainly
check whether the decayed result type of the binary operator can be constructed by both the invoke type
and the initial value type.
constructible_from seems more appropriate here because we don't need to care whether
the two types can be converted implicitly and explicitly.
Taking string and string_view as examples, the former can only be explicitly constructed by
the latter and can be assigned by the latter, which makes ranges::fold_meow unable to fold
ranges whose elements are of type string with an initial value of type string_view:
vector<string> vs{"a", "b", "c"};
string_view init{"d"};
auto result = ranges::fold_right(vs, init, plus{}); // still ill-formed after P2591R5 as the constraint is not satisfied
In addition, the two movable constraints in the function signature seem to be too strict as well since we
only need to check that the decayed result type and the initial type can be move-constructible (one for returned by
elidable move and one for move into other overloads) instead of whether they can be move-assignable.
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll.
"NAD. Constraining algorithms is not about limiting the constraints to
the bare minimum required by implementation details. That's what the C++0x
attempt did - in an effort to maintain backward compatibility - and it's a mess.
While the string/string_view case might look superficially appealing,
it would also allow vector<string>/int,
which is much less so."
Proposed resolution:
This wording is relative to N4981.
Modify 26.4 [algorithm.syn], header <algorithm> synopsis, as indicated:
#include <initializer_list> // see 17.11.2 [initializer.list.syn] namespace std { […] namespace ranges { […] template<class F, class T, class I, class U> concept indirectly-binary-left-foldable-impl = // exposition onlymovablemove_constructible<T> &&movablemove_constructible<U> &&convertible_toconstructible_from<T,U, T> && invocable<F&, U, iter_reference_t<I>> && assignable_from<U&, invoke_result_t<F&, U, iter_reference_t<I>>>; template<class F, class T, class I> concept indirectly-binary-left-foldable = // exposition only copy_constructible<F> && indirectly_readable<I> && invocable<F&, T, iter_reference_t<I>> &&convertible_toconstructible_from<decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>,decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>>&& indirectly-binary-left-foldable-impl<F, T, I, decay_t<invoke_result_t<F&, T, iter_reference_t<I>>>>; […] } […] }
Modify 26.6.18 [alg.fold] as indicated:
template<bidirectional_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-right-foldable<T, I> F> constexpr auto ranges::fold_right(I first, S last, T init, F f); template<bidirectional_range R, class T = range_value_t<R>, indirectly-binary-right-foldable<T, iterator_t<R>> F> constexpr auto ranges::fold_right(R&& r, T init, F f);-3- Effects: Equivalent to:
[…]using U = decay_t<invoke_result_t<F&, iter_reference_t<I>, T>>; if (first == last) return U(std::move(init)); I tail = ranges::next(first, last); U accum=(invoke(f, *--tail, std::move(init))); while (first != tail) accum = invoke(f, *--tail, std::move(accum)); return accum;template<input_iterator I, sentinel_for<I> S, class T = iter_value_t<I>, indirectly-binary-left-foldable<T, I> F> constexpr see below ranges::fold_left_with_iter(I first, S last, T init, F f); template<input_range R, class T = range_value_t<R>, indirectly-binary-left-foldable<T, iterator_t<R>> F> constexpr see below ranges::fold_left_with_iter(R&& r, T init, F f);-6- Let
-7- Effects: Equivalent to:Ubedecay_t<invoke_result_t<F&, T, iter_reference_t<I>>>.if (first == last) return {std::move(first), U(std::move(init))}; U accum=(invoke(f, std::move(init), *first)); for (++first; first != last; ++first) accum = invoke(f, std::move(accum), *first); return {std::move(first), std::move(accum)};-8- Remarks: The return type is
fold_left_with_iter_result<I, U>for the first overload andfold_left_with_iter_result<borrowed_iterator_t<R>, U>for the second overload.
views::reverse should be specialized for some view typesSection: 25.7.21.1 [range.reverse.overview] Status: LEWG Submitter: Hewill Kang Opened: 2024-05-09 Last modified: 2024-06-24
Priority: 3
View all issues with LEWG status.
Discussion:
Currently, when views::reverse is applied to empty_view, single_view, or
repeat_view, a reverse_view with the iterator type reverse_iterator will
be produced.
views::repeat(0) | views::reverse | views::take(5) no longer timeout,
which seems to be an improvement.
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll. Send to LEWG for review.
The repeat part is related to LWG 4019(i).
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.21.1 [range.reverse.overview] as indicated:
-1-
-2- The namereverse_viewtakes a bidirectional view and produces another view that iterates the same elements in reverse order.views::reversedenotes a range adaptor object (25.7.2 [range.adaptor.object]). Given a subexpressionE, the expressionviews::reverse(E)is expression-equivalent to:
(2.1) — If the type of
Eis a (possibly cv-qualified) specialization ofreverse_view, equivalent toE.base().(2.?) — Otherwise, if the type of
Eis a (possibly cv-qualified) specialization ofempty_view(25.6.2.2 [range.empty.view]),single_view(25.6.3.2 [range.single.view]), orrepeat_view(25.6.5.2 [range.repeat.view]), equivalent todecay-copy(E).(2.2) — Otherwise, if the type of
Eis cvsubrange<reverse_iterator<I>, reverse_iterator<I>, K>for some iterator typeIand valueKof typesubrange_kind,
(2.2.1) — if
Kissubrange_kind::sized, equivalent to:subrange<I, I, K>(E.end().base(), E.begin().base(), E.size())(2.2.2) — otherwise, equivalent to:
subrange<I, I, K>(E.end().base(), E.begin().base())However, in either case
Eis evaluated only once.(2.3) — Otherwise, equivalent to
reverse_view{E}.
views::as_rvalue and views::common are not strictly correctSection: 25.7.7.1 [range.as.rvalue.overview], 25.7.20.1 [range.common.overview] Status: New Submitter: Hewill Kang Opened: 2024-05-10 Last modified: 2024-06-24
Priority: 4
View all other issues in [range.as.rvalue.overview].
View all issues with New status.
Discussion:
Currently, these two adaptors return views::all(E) when the type of subexpression E
models common_range or its reference is the same as the rvalue reference.
const-qualified object of a range
type must have the same non-commonality or value category of the reference as the non-const one,
this makes it theoretically possible for these two to still return non-common ranges or ranges whose reference
are lvalue when acting on a const-qualified lvalue view object, because
views::all(E) will remove the const-qualifier.
The proposed wording pedantically checks views::all_t<decltype((E))> instead of decltype((E)).
[2024-06-24; Reflector poll]
Set priority to 4 after reflector poll.
"Not aware of any use case for making const affect those properties".
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.7.1 [range.as.rvalue.overview] as indicated:
-2- The name
views::as_rvaluedenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEbe an expression and letTbedecltype((E)). The expressionviews::as_rvalue(E)is expression-equivalent to:
(2.1) —
views::all(E)ifsame_as<range_rvalue_reference_t<views::all_t<T>>, range_reference_t<views::all_t<T>>>istrue.(2.2) — Otherwise,
as_rvalue_view(E).
Modify 25.7.20.1 [range.common.overview] as indicated:
-3- The name
views::commondenotes a range adaptor object (25.7.2 [range.adaptor.object]). Given a subexpressionE, the expressionviews::common(E)is expression-equivalent to:
(3.1) —
views::all(E), ifdecltype((E))views::all_t<T>modelscommon_rangeand.views::all(E)is a well-formed expression(3.2) — Otherwise,
common_view{E}.
Section: 16.4.6.4 [global.functions] Status: New Submitter: Jiang An Opened: 2024-05-11 Last modified: 2024-08-02
Priority: 3
View all other issues in [global.functions].
View all issues with New status.
Discussion:
Currently, std::from_chars and std::to_chars are specified with default arguments.
Some implementors want to split them into more overloads to determine the base 10 at compile time
(LLVM issue #91268). However, the
current standard wording doesn't clearly allow such technique.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll. "It's a lot of new text just to say you can't do that, lose the example?" "Might be NAD, is this already disallowed?" "Only for member functions."
Proposed resolution:
This wording is relative to N4981.
Modify 16.4.6.4 [global.functions] as indicated:
[…]
-2- A call to a non-member function signature described in 17 [support] through 32 [thread] and D [depr] shall behave as if the implementation declared no additional non-member function signatures. However, for each non-member function specified with at least one default argument, an implementation may declare additional function signatures with one or more trailing parameters that have default arguments removed. [Example 1: The following program is possibly ill-formed because of ambiguity in overload resolution.#include <cstddef> #include <string> // One overload of std::stoi ([string.conversions]) is specified as // int stoi(const string& str, size_t* idx = nullptr, int base = 10); // An implementation can declare these overloads instead of using default arguments: // int stoi(const string& str, size_t* idx, int base); // int stoi(const string& str, size_t* idx); // int stoi(const string& str); namespace usr { int stoi(const std::string&); int stoi(const std::string&, std::size_t*); } int main() { using std::stoi; using usr::stoi; int (*p1)(const std::string&) = stoi; // possibly ill-formed int (*p2)(const std::string&, std::size_t*) = stoi; // possibly ill-formed }— end example]
Section: 31.7.6.3.2 [ostream.inserters.arithmetic] Status: New Submitter: Jonathan Wakely Opened: 2024-05-10 Last modified: 2024-05-15
Priority: 3
View all other issues in [ostream.inserters.arithmetic].
View all issues with New status.
Discussion:
LWG 117(i) fixed insertion of a float into an ostream by
requiring a cast to double. This gives a surprising result on RISC-V when
inserting a negative NaN, because RISC-V floating-point instructions do
not preserve the sign or payload of NaN values. This means that
std::cout << -std::numeric_limits<float>::quiet_NaN()
prints "nan" rather than "-nan" as most users probably expect.
11.3 section of the RISC-V ISA manual (20191213):
Except when otherwise stated, if the result of a floating-point operation is NaN, it is the canonical NaN. The canonical NaN has a positive sign and all significand bits clear except the MSB, a.k.a. the quiet bit. For single-precision floating-point, this corresponds to the pattern 0x7fc00000.
This is allowed by IEEE 754 (2019), as per section 6.3:
When either an input or result is a NaN, this standard does not interpret the sign of a NaN.
So it is standard-conforming for static_cast<double>(val)
to lose the sign (and payload) of a NaN.
This might also affect Apple M1 chips, if they use the ARMv8 default-NaN mode.
The current wording does not permit an implementation to use something like
std::copysign(static_cast<double>(val), std::signbit(val) ? -1.0 : +1.0)
to restore the sign bit. Should this be allowed? Maybe we should say that it's
unspecified whether the cast to double preserves the sign of a NaN?
If not, should we add a note about the surprising behaviour?
Previous resolution [SUPERSEDED]:
This wording is relative to N4981.
Modify 31.7.6.3.2 [ostream.inserters.arithmetic] as indicated:
-3- Whenvalis of typefloatthe formatting conversion occurs as if it performed the following code fragment:[Note ?: Whenbool failed = use_facet< num_put<charT, ostreambuf_iterator<charT, traits>> >(getloc()).put(*this, *this, fill(), static_cast<double>(val)).failed();valis a NaN value, the conversion todoublecan alter the sign and payload. — end note]
[2024-05-15; Reflector poll]
Set priority to 3 after reflector poll.
[2024-05-15; Peter Dimov provides improved wording]
Jonathan observes that the same problem exists for
operator<<(extended-floating-point-type).
Proposed resolution:
This wording is relative to N4981.
Modify 31.7.6.3.2 [ostream.inserters.arithmetic] as indicated:
-3- Whenvalis of typefloatthe formatting conversion occurs as if it performed the following code fragment:wherebool failed = use_facet< num_put<charT, ostreambuf_iterator<charT, traits>> >(getloc()).put(*this, *this, fill(),static_cast<double>(val)dval).failed();dvalisvalconverted to typedoubleas if bystatic_cast<double>(val), except that the sign and payload of NaN values may be preserved rather than discarded.
string_view(Iter, Iter) constructor breaks existing codeSection: 27.3.3.2 [string.view.cons] Status: New Submitter: Derek Zhang Opened: 2024-05-14 Last modified: 2024-08-02
Priority: 2
View other active issues in [string.view.cons].
View all other issues in [string.view.cons].
View all issues with New status.
Discussion:
As a result of the new constructor added by P1391, this stopped working in C++20:
void fun(string_view);
void fun(vector<string_view>);
fun({"a", "b"});
Previously the first fun wasn't viable, so it constructed a
vector<string_view>
of two elements using its initializer-list constructor
and then called the second fun.
Now {"a", "b"} could also be a call to the new string_view(Iter, Iter),
so it's ambiguous and fails to compile.
The following case is arguably worse as it doesn't become ill-formed in C++20, it still compiles but now has undefined behaviour:
fun({{"a", "b"}});
Previously the first fun wasn't viable, so this constructed a
vector<string_view> of two elements
(via somewhat bizarre syntax, but using the same initializer-list constructor
as above).
Now it constructs a vector from an initializer_list with one
element, where that element is constructed from the two const char*
using string_view(Iter, Iter).
But those two pointers are unrelated and do not form a valid range,
so this violates the constructor's precondition and has undefined behaviour.
If you're lucky it crashes at runtime when trying to reach "b" from "a",
but it could also form a string_view that reads arbitrary secrets from the
memory between the two pointers.
[Jonathan comments]
At the very least, we should have an Annex C entry documenting the change.
Making the new string_view(Iter, Iter) constructor explicit would prevent
the runtime behaviour change for the second example,
but GCC thinks the first example would still be ambiguous
(it seems to depend on how list-initialization handles explicit constructors,
which has implementation divergence).
Maybe we should have a deleted constructor matching string literals:
template<size_t N1, size_t N2>
basic_string_view(const charT(&)[N1], const charT(&)[N2]) = delete;
Or to handle both const char[N] and char[N]:
template<class A1, class A2>
requires (rank_v<A1> == 1) && (rank_v<A2> == 1)
basic_string_view(A1&, A2&) = delete;
Both options would prevent this currently valid (but weird) code:
const char arr[] = "str";
std::string_view s(arr, arr); // s.size() == 0 and s.data() == arr
That seems acceptable, because std::string_view s(arr, 0)
is simpler and clearer anyway.
[2024-08-02; Reflector poll]
Set priority to 2 after reflector poll.
"The constructor should be made explicit as part of any resolution for this."
Proposed resolution:
ranges::unique_copy's constraints for the case where result is an
input_iterator are not quite rightSection: 26.7.9 [alg.unique] Status: New Submitter: Hewill Kang Opened: 2024-05-14 Last modified: 2024-06-24
Priority: 3
View other active issues in [alg.unique].
View all other issues in [alg.unique].
View all issues with New status.
Discussion:
When r is only an input_range and result is also an
input_iterator, ranges::unique_copy writes the elements of r
into result and reads the value of result in the next iteration.
value_type of r
and the value_type of result are the same, which seems too loose because
the value_type is not particularly useful in the ranges world compared to the
reference, which is also reflected in the fact that the implementation applies the
compare function on both dereferenced values (demo):
#include <algorithm>
#include <iostream>
#include <ranges>
#include <vector>
int main() {
auto r = std::views::istream<bool>(std::cin);
std::vector<bool> v(10);
auto proj = [](std::same_as<bool> auto b) { return b; }; // ban vector<bool>::reference
std::ranges::unique_copy(r, v.begin(), {}, proj); // hard error in libstdc++, libc++ and MSVC-STL
}
[2024-06-24; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 26.4 [algorithm.syn], header <algorithm> synopsis, as indicated:
#include <initializer_list> // see [initializer.list.syn] namespace std { […] namespace ranges { template<class I, class O> using unique_copy_result = in_out_result<I, O>; template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Proj = identity, indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to> requires indirectly_copyable<I, O> && (forward_iterator<I> || (input_iterator<O> &&same_as<iter_value_t<I>, iter_value_t<O>>indirect_equivalence_relation<C, projected<I, Proj>, projected<O, Proj>>) || indirectly_copyable_storable<I, O>) constexpr unique_copy_result<I, O> unique_copy(I first, S last, O result, C comp = {}, Proj proj = {}); template<input_range R, weakly_incrementable O, class Proj = identity, indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to> requires indirectly_copyable<iterator_t<R>, O> && (forward_iterator<iterator_t<R>> || (input_iterator<O> &&same_as<range_value_t<R>, iter_value_t<O>>indirect_equivalence_relation<C, projected<iterator_t<R>, Proj>, projected<O, Proj>>) || indirectly_copyable_storable<iterator_t<R>, O>) constexpr unique_copy_result<borrowed_iterator_t<R>, O> unique_copy(R&& r, O result, C comp = {}, Proj proj = {}); } […] }
Modify 26.7.9 [alg.unique] as indicated:
template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Proj = identity, indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to> requires indirectly_copyable<I, O> && (forward_iterator<I> || (input_iterator<O> &&same_as<iter_value_t<I>, iter_value_t<O>>indirect_equivalence_relation<C, projected<I, Proj>, projected<O, Proj>>) || indirectly_copyable_storable<I, O>) constexpr ranges::unique_copy_result<I, O> ranges::unique_copy(I first, S last, O result, C comp = {}, Proj proj = {}); template<input_range R, weakly_incrementable O, class Proj = identity, indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to> requires indirectly_copyable<iterator_t<R>, O> && (forward_iterator<iterator_t<R>> || (input_iterator<O> &&same_as<range_value_t<R>, iter_value_t<O>>indirect_equivalence_relation<C, projected<iterator_t<R>, Proj>, projected<O, Proj>>) || indirectly_copyable_storable<iterator_t<R>, O>) constexpr ranges::unique_copy_result<borrowed_iterator_t<R>, O> ranges::unique_copy(R&& r, O result, C comp = {}, Proj proj = {});-6- Let
predbeequal_to{}for the overloads in namespacestdwith no parameterpred, […]
basic_const_iterator<volatile int*> is not a contiguous_iteratorSection: 24.5.3 [const.iterators] Status: New Submitter: Hewill Kang Opened: 2024-05-14 Last modified: 2024-06-24
Priority: 4
View other active issues in [const.iterators].
View all other issues in [const.iterators].
View all issues with New status.
Discussion:
Although volatile int* satisfies contiguous_iterator, due to the formula design of
iter_const_reference_t, its result for the former will be int, which makes
basic_const_iterator<volatile int*> no longer a contiguous_iterator even though its
iterator_concept is defined as contiguous_iterator_tag and it has a valid
operator->() that returns const volatile int*.
basic_const_iterator to preserve the behavior of the underlying
iterator (except for indirection operators). The same goes for
basic_const_iterator<const volatile int*>:
#include <iterator>
#include <span>
int main() {
int i = 42;
const volatile int* p = &i;
static_assert(std::contiguous_iterator<decltype(p)>);
std::span sp1{p, 1}; // ok
std::basic_const_iterator it{p};
static_assert(std::same_as<decltype(it.operator->()), const volatile int*>);
static_assert(std::same_as<decltype(it)::iterator_concept, std::contiguous_iterator_tag>);
static_assert(std::contiguous_iterator<decltype(it)>); // failed
std::span sp2{it, 1}; // failed
}
[2024-06-24; Reflector poll]
Set priority to 4 after reflector poll. See also LWG 3813(i).
Proposed resolution:
pair/tuple formattersSection: 28.5.7.4 [format.range.fmtmap] Status: New Submitter: Victor Zverovich Opened: 2024-05-18 Last modified: 2025-02-07
Priority: 3
View all issues with New status.
Discussion:
Consider the following example:
#include <format>
#include <map>
#include <print>
struct x {};
template<typename K>
struct std::formatter<std::pair<K, x>> : std::formatter<std::string_view> {
auto format(const std::pair<K, x>& p, auto& ctx) const {
return std::format_to(ctx.out(), "x/x");
}
};
int main() {
std::print("{}", std::map<x, x>());
}
It doesn't compile because the formatter for maps requires the element formatter to have
set_brackets and set_separator (28.5.7.4 [format.range.fmtmap]):
underlying_.underlying().set_brackets({}, {});
underlying_.underlying().set_separator(STATICALLY-WIDEN<charT>(": "));
The specialization std::formatter<std::pair<K, x>> itself is allowed according to
16.4.5.2.1 [namespace.std]:
Unless explicitly prohibited, a program may add a template specialization for any standard library class template to namespace
stdprovided that
the added declaration depends on at least one program-defined type, and
the specialization meets the standard library requirements for the original template.
but it's unclear what exactly the part "the specialization meets the standard library requirements for
the original template" means for this formatter. Does it mean that the specialization must provide
set_brackets and set_separator and does the output have to be consistent with the main
template? The latter would render the specialization useless. On the other hand if users are allowed to
customize pair and tuple formatting the current specification of the map formatter is broken.
pairs
and tuples, and make map be responsible for its own structural formatting rather than delegating
part of it to other formatters in an arbitrary way. This resolution has been applied to {fmt}'s implementation
of range formatting to address #3685.
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
Several votes for NAD (a specialization must provide set_brackets etc.),
and suggestions it's a design change that LEWG should see.
Proposed resolution:
This wording is relative to N4981.
Modify 28.5.8.3 [format.args] as indicated:
namespace std { template<ranges::input_range R, class charT> struct range-default-formatter<range_format::map, R, charT> { private: using maybe-const-map = fmt-maybe-const<R, charT>; // exposition only using element-type = // exposition only remove_cvref_t<ranges::range_reference_t<maybe-const-map>>;range_formatter<element-type, charT> underlying_; // exposition onlyusing key-type = tuple_element_t<0, element-type>; // exposition only using value-type = tuple_element_t<1, element-type>; // exposition only formatter<key-type, charT> key-formatter_; // exposition only formatter<value-type, charT> value-formatter_; // exposition only public: constexpr range-default-formatter(); template<class ParseContext> constexpr typename ParseContext::iterator parse(ParseContext& ctx); template<class FormatContext> typename FormatContext::iterator format(maybe-const-map& r, FormatContext& ctx) const; }; }constexpr range-default-formatter();-1- Mandates: Either:
(1.1) —
element-typeis a specialization ofpair, or(1.2) —
element-typeis a specialization oftupleandtuple_size_v<element-type> == 2.
-2- Effects: Equivalent to:underlying_.set_brackets(STATICALLY-WIDEN<charT>("{"), STATICALLY-WIDEN<charT>("}")); underlying_.underlying().set_brackets({}, {}); underlying_.underlying().set_separator(STATICALLY-WIDEN<charT>(": "));template<class ParseContext> constexpr typename ParseContext::iterator parse(ParseContext& ctx);-3- Effects:
IfEquivalent to:Parses the format specifiers as a range-format-spec and stores the parsed specifiers inreturn underlying_.parse(ctx);*this.key-formatter_.set_debug_format()is a valid expression, and there is no range-underlying-spec, then callskey-formatter_.set_debug_format(). Ifvalue-formatter_.set_debug_format()is a valid expression, and there is no range-underlying-spec, then callsvalue-formatter_.set_debug_format(). -?- Returns: An iterator past the end of the range-format-spec.template<class FormatContext> typename FormatContext::iterator format(maybe-const-map& r, FormatContext& ctx) const;-4- Effects:
Equivalent to:Writes the following intoreturn underlying_.format(r, ctx);ctx.out(), adjusted according to the range-format-spec:
—
STATICALLY-WIDEN<charT>("{")unless thenoption is specified,— for each element
eof the ranger:
— the result of writing
get<0>(e)viakey-formatter_,—
STATICALLY-WIDEN<charT>(": "),— the result of writing
get<1>(e)viavalue-formatter_,—
STATICALLY-WIDEN<charT>(", "), unlesseis the last element ofr, and—
STATICALLY-WIDEN<charT>("}")unless thenoption is specified.-?- Returns: An iterator past the end of the output range.
lazy_split_view should be sized_range when pattern is empty tiny-rangeSection: 25.7.16.2 [range.lazy.split.view], 25.7.17.2 [range.split.view] Status: SG9 Submitter: Hewill Kang Opened: 2024-05-23 Last modified: 2024-08-02
Priority: 4
View other active issues in [range.lazy.split.view].
View all other issues in [range.lazy.split.view].
View all issues with SG9 status.
Discussion:
When the pattern range is empty, lazy_split_view will split each element into individual subranges,
which means its size is equal to the size of the underlying range.
tiny-range that can determine whether the range is empty by its type,
it seems valuable to provide a size for lazy_split_view in this case, given that we already
specifically checked for it by Pattern::size() == 0 in inner-iterator::operator++().
[2024-08-02; Reflector poll]
Set priority to 4 after reflector poll. Set Status to SG9.
"Design change".
"tiny-range should accept span<T, 0|1> or ref_view<array<T, 0|1>>,
see related paper P1419."
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.16.2 [range.lazy.split.view] as indicated:
namespace std::ranges {
template<auto> struct require-constant; // exposition only
template<class R>
concept tiny-range = // exposition only
sized_range<R> &&
requires { typename require-constant<remove_reference_t<R>::size()>; } &&
(remove_reference_t<R>::size() <= 1);
template<input_range V, forward_range Pattern>
requires view<V> && view<Pattern> &&
indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> &&
(forward_range<V> || tiny-range<Pattern>)
class lazy_split_view : public view_interface<lazy_split_view<V, Pattern>> {
[…]
constexpr auto size()
requires sized_range<V> &&
tiny-range<Pattern> && (Pattern::size() == 0) {
return ranges::size(base_);
}
constexpr auto size() const
requires sized_range<const V> &&
tiny-range<Pattern> && (Pattern::size() == 0) {
return ranges::size(base_);
}
};
[…]
}
Modify 25.7.17.2 [range.split.view] as indicated:
namespace std::ranges {
template<forward_range V, forward_range Pattern>
requires view<V> && view<Pattern> &&
indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to>
class split_view : public view_interface<split_view<V, Pattern>> {
[…]
constexpr auto size()
requires sized_range<V> &&
tiny-range<Pattern> && (Pattern::size() == 0) {
return ranges::size(base_);
}
constexpr auto size() const
requires sized_range<const V> &&
tiny-range<Pattern> && (Pattern::size() == 0) {
return ranges::size(base_);
}
};
[…]
}
int8_t/uint8_t is undefined behaviorSection: 29.5.3.1 [rand.req.genl] Status: New Submitter: Peter Dimov Opened: 2024-05-25 Last modified: 2025-10-21
Priority: 3
View all other issues in [rand.req.genl].
View all issues with New status.
Discussion:
As pointed out in LWG issue 2326(i) (closed as NAD) and
on reddit,
instantiating e.g. uniform_int_distribution<uint8_t> is undefined behavior
because of the requirement 29.5.3.1 [rand.req.genl] bullet (1.5):
-1- Throughout this subclause 29.5 [rand], the effect of instantiating a template:
[…]
(1.4) — that has a template type parameter named
RealTypeis undefined unless the corresponding template argument is cv-unqualified and is one offloat,double, orlong double.(1.5) — that has a template type parameter named
IntTypeis undefined unless the corresponding template argument is cv-unqualified and is one ofshort,int,long,long long,unsigned short,unsigned int,unsigned long, orunsigned long long.(1.6) — that has a template type parameter named
UIntTypeis undefined unless the corresponding template argument is cv-unqualified and is one ofunsigned short,unsigned int,unsigned long, orunsigned long long.
This is, in my opinion, a defect; such uses should either be rejected at compile time (made ill-formed), or permitted (as 2326 proposes.)
UB here has undesirable safety implications, because it's possible to write code that produces a random, or a seemingly random, sequence ofuint8_t numbers on platform A, but an arbitrarily non-random
sequence on platform B (e.g. all zeroes.)
If that sequence is then used in e.g. a cryptographic algorithm, bad
things will happen on platform B, and the tests on platform A won't
catch the issue.
[2024-05-26; Daniel comments]
I think that all violations of the bullets 29.5.3.1 [rand.req.genl] (1.4), (1.5), and (1.6)
are missed opportunities of Mandates (That is: Make the program ill-formed), because
they can be all checked (easily) at compile-time, regardless whether we agree on
the question to support int8_t/uint8_t (Violations of (1.1), (1.2), and (1.3)
still have to be remain undefined because of additional runtime requirements imposed).
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
"The only benefit of making it UB is allowing it as an extension."
"Does anybody actually want to support a BigInt as an extension? If not, non-integral types should be ill-formed and integral types not in the list should be conditionally-supported."
"I'd like extended arithmetic types to work in general, it's useful for writing generic math libraries to be able to generate random numbers of any arithmetic type that the implementation provides, including short integers and all floating-point types."
"Can have overflows here, differences between whether char is signed or
unsigned lead to subtle bugs. Most users are better off picking int or
unsigned int (which is what they get from integral promotions anyway)
and just using values that are in range for their intended destination type."
Proposed resolution:
This wording is relative to N4981.
[Drafting Note: Two mutually exclusive options are prepared, depicted below by Option A and Option B, respectively.]
Option A: As suggested in issue LWG 2326(i)
Modify 29.5.3.1 [rand.req.genl] as indicated:
-1- Throughout this subclause 29.5 [rand], the effect of instantiating a template:
[…]
(1.4) — that has a template type parameter named
RealTypeis undefined unless the corresponding template argument is cv-unqualified and is one offloat,double, orlong double.(1.5) — that has a template type parameter named
IntTypeis undefined unless the corresponding template argument is cv-unqualified and is a standard integer type (6.9.2 [basic.fundamental])one of.short,int,long,long long,unsigned short,unsigned int,unsigned long, orunsigned long long(1.6) — that has a template type parameter named
UIntTypeis undefined unless the corresponding template argument is cv-unqualified and is a standard unsigned integer type (6.9.2 [basic.fundamental])one of.unsigned short,unsigned int,unsigned long, orunsigned long long
Option B: Make ill-formed.
Modify 29.5.3.1 [rand.req.genl] as indicated:
-1- Throughout this subclause 29.5 [rand], the effect of instantiating a template:
[…]
(1.4) — that has a template type parameter named
RealTypeis undefined unless the corresponding template argument is cv-unqualified and is one offloat,double, orlong double.(1.5) — that has a template type parameter named
IntTyperenders the program ill-formedis undefinedunless the corresponding template argument is cv-unqualified and is one ofshort,int,long,long long,unsigned short,unsigned int,unsigned long, orunsigned long long.(1.6) — that has a template type parameter named
UIntTyperenders the program ill-formedis undefinedunless the corresponding template argument is cv-unqualified and is one ofunsigned short,unsigned int,unsigned long, orunsigned long long.
shared_ptr(nullptr_t, Deleter) is overconstrained, breaking some sensible deletersSection: 20.3.2.2.2 [util.smartptr.shared.const] Status: New Submitter: Louis Dionne Opened: 2024-06-11 Last modified: 2025-10-22
Priority: 3
View other active issues in [util.smartptr.shared.const].
View all other issues in [util.smartptr.shared.const].
View all issues with New status.
Discussion:
The following code doesn't compile on conforming implementations:
#include <memory>
void f() {
std::shared_ptr<int>(new int, [](auto pointer) { delete pointer; });
}
(Godbolt)
This is caused by the constraint on shared_ptr(nullptr_t p, D d);
being that d(p) is valid (20.3.2.2.2 [util.smartptr.shared.const] p9),
which leads to a hard error inside the lambda since it is called with a
nullptr_t. This seems unintended.
See LLVM issue 93071 comment for additional context.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
"I don't agree with the proposed resolution. As a general principle,
shared_ptr<T>(p, d) always calls d(p) and never
d(static_cast<T*>(p)).
If we really want to make this work, which is not unreasonable, even though
the fix on the user side is trivial, we should make the nullptr_t constructor
templated on same_as<nullptr_t>
(or convertible_to<nullptr_t>?)."
"That would break passing NULL, only nullptr would work.
It can be made to work by checking that d(p) is well-formed in a
function parameter with a default argument,
instead of as a template parameter:"
struct shared_ptr {
template<class Y, class D>
shared_ptr(Y* p, D d, std::void_t<decltype(d(p))>* = nullptr) {}
template<class D>
shared_ptr(std::nullptr_t p, D d, std::void_t<decltype(d(p))>* = nullptr) {}
};
shared_ptr s(new int, [](auto p) {delete p;});
"Ugh. We don't have to use it everywhere, only these two specific constructors."
Proposed resolution:
This wording is relative to N4981.
shared_ptr(nullptr_t p, D d); checks whether
d(static_cast<T*>(nullptr))
is well-formed. This requires expressing the the constraints for
the Y* constructors and the nullptr_t constructors separately,
which is mostly editorial:
template<class Y, class D> shared_ptr(Y* p, D d); template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);template<class D> shared_ptr(nullptr_t p, D d); template<class D, class A> shared_ptr(nullptr_t p, D d, A a);-9- Constraints:
is_move_constructible_v<D>istrue, andd(p)is a well-formed expression.For the first two overloads:
- (9.1) If
Tis an array type, then eitherTisU[N]andY(*)[N]is convertible toT*, orTisU[]andY(*)[]is convertible toT*.- (9.2) If
Tis not an array type, thenY*is convertible toT*.template<class D> shared_ptr(nullptr_t p, D d); template<class D, class A> shared_ptr(nullptr_t p, D d, A a);-?- Constraints:
is_move_constructible_v<D>istrue, andd(static_cast<T*>(p))is a well-formed expression.
Section: 26.8.4 [alg.binary.search], 26.8.1 [alg.sorting.general] Status: New Submitter: Jiang An Opened: 2024-06-18 Last modified: 2024-06-24
Priority: 3
View all other issues in [alg.binary.search].
View all issues with New status.
Discussion:
LWG 270(i) relaxed the requirements for comp of binary search algorithms
(26.8.4 [alg.binary.search]). However, the relaxation doesn't seem automatically applied
to ranges versions, because ranges versions of these algorithms constrain the
Comp types with indirect_strict_weak_order.
indirect_strict_weak_order
aren't imposed for these algorithms or change the constraints.
[St. Louis 2024-06-24; set priority to P3]
Proposed resolution:
elements_view::iterator::operator* missing conditional noexcept specificationSection: 25.7.23.3 [range.elements.iterator] Status: New Submitter: Hewill Kang Opened: 2024-07-02 Last modified: 2024-08-02
Priority: 3
View other active issues in [range.elements.iterator].
View all other issues in [range.elements.iterator].
View all issues with New status.
Discussion:
views::elements, which can be seen as a specialization of views::transform,
is used to transform the original tuple into its elements.
iter_move, the customization point is dispatched
to the default implementation. However, unlike the latter, elements_view::iterator::operator*
does not have a noexcept specification, which makes calls to iter_move never
noexcept, which seems to be an oversight (demo):
#include <ranges>
#include <vector>
std::vector v{std::pair{1, "a"}, {2, "b"}};
auto r1 = v | std::views::keys;
auto i1 = r1.begin();
static_assert(noexcept(*i1)); // failed
static_assert(noexcept(std::ranges::iter_move(i1))); // failed
auto get_key = [](auto& t) noexcept -> auto& { return std::get<0>(t); };
auto r2 = v | std::views::transform(get_key);
auto i2 = r2.begin();
static_assert(noexcept(*i2));
static_assert(noexcept(std::ranges::iter_move(i2)));
The proposed resolution is aligned with the strengthened implementation of MSVC-STL.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.23.3 [range.elements.iterator] as indicated:
namespace std::ranges {
template<input_range V, size_t N>
requires view<V> && has-tuple-element<range_value_t<V>, N> &&
has-tuple-element<remove_reference_t<range_reference_t<V>>, N> &&
returnable-element<range_reference_t<V>, N>
template<bool Const>
class elements_view<V, N>::iterator {
[…]
static constexpr decltype(auto) get-element(const iterator_t<Base>& i); // exposition only
noexcept(noexcept(std::get<N>(*i))) requires is_reference_v<range_reference_t<Base>> {
return std::get<N>(*i);
}
static constexpr decltype(auto) get-element(const iterator_t<Base>& i) // exposition only
noexcept(noexcept(std::get<N>(*i)) &&
is_nothrow_move_constructible_v<tuple_element_t<N, range_reference_t<Base>>>) {
using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>;
return static_cast<E>(std::get<N>(*i));
}
public:
[…]
constexpr decltype(auto) operator*() const
noexcept(noexcept(get-element(current_)))
{ return get-element(current_); }
[…]
};
}
[…]
static constexpr decltype(auto) get-element(const iterator_t<Base>& i);
-3- Effects: Equivalent to:
if constexpr (is_reference_v<range_reference_t<Base>>) { return std::get<N>(*i); } else { using E = remove_cv_t<tuple_element_t<N, range_reference_t<Base>>>; return static_cast<E>(std::get<N>(*i)); }
move_iterator::operator* should have conditional noexcept specificationSection: 24.5.4.6 [move.iter.elem] Status: New Submitter: Hewill Kang Opened: 2024-07-03 Last modified: 2024-08-02
Priority: 4
View all other issues in [move.iter.elem].
View all issues with New status.
Discussion:
For move_iterator, dereferencing it is actually equivalent to applying iter_move to it.
move_iterator's dereference operator lacks a noexcept
specification, which seems to be an oversight given that the standard goes to such great lengths to
preserve the noexceptness of iter_move, and the main purpose of move_iterator is
precisely to apply iter_move to the underlying iterator via dereferencing.
[2024-08-02; Reflector poll]
Set priority to 4 after reflector poll.
"Do we have evidence conditional noexcept matters here?
Do we have a policy that operator* should be noexcept whenever possible?
What criteria are we using to decide here?"
Proposed resolution:
This wording is relative to N4981.
Modify 24.5.4.2 [move.iterator] as indicated:
namespace std {
template<class Iterator>
class move_iterator {
public:
[…]
constexpr reference operator*() const noexcept(noexcept(ranges::iter_move(current)));
[…]
};
}
Modify 24.5.4.6 [move.iter.elem] as indicated:
constexpr reference operator*() const noexcept(noexcept(ranges::iter_move(current)));-1- Effects: Equivalent to:
return ranges::iter_move(current);
enumerate_view::iterator and cartesian_product_view::iterator should not
always provide iterator_categorySection: 25.7.24.3 [range.enumerate.iterator], 25.7.33.3 [range.cartesian.iterator] Status: New Submitter: Hewill Kang Opened: 2024-07-07 Last modified: 2025-10-21
Priority: 3
View other active issues in [range.enumerate.iterator].
View all other issues in [range.enumerate.iterator].
View all issues with New status.
Discussion:
These two iterators do not support *r++ for non-forward iterators,
so we should not provide iterator_category as they are not C++17 iterators.
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
Six votes for Tentatively Ready, but not unanimous.
Proposed resolution:
This wording is relative to N4981.
Modify 25.7.24.3 [range.enumerate.iterator] as indicated:
namespace std::ranges {
template<view V>
requires range-with-movable-references<V>
template<bool Const>
class enumerate_view<V>::iterator {
using Base = maybe-const<Const, V>; // exposition only
public:
using iterator_category = input_iterator_tag; // present only if Base
// models forward_range
using iterator_concept = see below;
[…]
};
}
Modify 25.7.33.3 [range.cartesian.iterator] as indicated:
namespace std::ranges {
template<input_range First, forward_range... Vs>
requires (view<First> && ... && view<Vs>)
template<bool Const>
class cartesian_product_view<First, Vs...>::iterator {
public:
using iterator_category = input_iterator_tag; // present only if maybe-const<Const, First>
// models forward_range
using iterator_concept = see below;
[…]
};
}
generator::iterator should provide iterator_conceptSection: 25.8.6 [coro.generator.iterator] Status: New Submitter: Hewill Kang Opened: 2024-07-07 Last modified: 2024-08-02
Priority: 4
View other active issues in [coro.generator.iterator].
View all other issues in [coro.generator.iterator].
View all issues with New status.
Discussion:
generator::iterator currently does not provide an iterator_concept.
While this does not affect it being an input_iterator, providing iterator_concept
does improve consistency given that other C++20 iterators have such a member type alias.
[2024-08-02; Reflector poll]
Set priority to 4 after reflector poll. Six votes for P0 (tentatively ready) but one NAD vote saying that if this has no effect on the category then there's no reason to add it.
Proposed resolution:
This wording is relative to N4981.
Modify 25.8.6 [coro.generator.iterator] as indicated:
namespace std {
template<class Ref, class V, class Allocator>
class generator<Ref, V, Allocator>::iterator {
public:
using iterator_concept = input_iterator_tag;
using value_type = value;
using difference_type = ptrdiff_t;
[…]
};
}
duration formatters format custom rep types?Section: 30.12 [time.format] Status: New Submitter: Jonathan Wakely Opened: 2024-07-08 Last modified: 2024-07-31
Priority: 3
View other active issues in [time.format].
View all other issues in [time.format].
View all issues with New status.
Discussion:
The formatter<chrono::duration<Rep, Period>, charT>
partial specialization needs to be able to format the Rep type, because
the %Q conversion specifier says to format the value returned by .count()
which is of type Rep. This implies that the Rep type must be formattable,
although the precise method of formatting it is not specified. Presumably
either format("{}", d.count()) or
ostrm << d.count() needs to work.
28.5.6.4 [format.formatter.spec] p2 (2.3) says:
For eachHowever, nothing prevents the excluded types being used as thecharT, for each cv-unqualified arithmetic typeArithmeticTother thanchar,wchar_t,char8_t,char16_t, orchar32_t, a specialization:template<> struct formatter<ArithmeticT, charT>;
rep for a
chrono::duration.
This means you can use chrono::duration<wchar_t> and
chrono::duration<char8_t> as durations,
but you can't format them to char strings.
I think only the %Q conversion specifier formats the rep type directly
(without converting durations to formattable types like hours or seconds),
and so I don't think this problem exists for other chrono formatter
specializations, because %Q can only be used for durations
(that's not entirely clear, since %q and %Q are specified to format
"the duration's unit suffix" and "the duration's numeric value",
but presumably that means they can only be used for duration types).
Should the specialization of formatter for chrono::duration be constrained
to require that the rep type can be formatted?
Or should the %Q conversion specifier say that the numeric value is
formatted by inserting into an ostream (which would treat wchar_t and
char8_t rep types as characters, not integers)?
Or should %Q say that the numeric value is converted to an integral type,
which we know how to format?
This is somewhat related to issue 953(i), since it's unclear which operations "a class emulating an arithmetic type" needs to support.
[2024-07-31; Reflector poll]
Set priority to 3 after reflector poll.
"Don't convert to an integer type, that would be wrong for
duration<long double> and could overflow for
duration<BigInt>."
"%Q could format using +d.count()"
Some requests to disallow using code unit types as duration reps,
e.g. duration<char>. Alternatively it just shouldn't
be formattable. Alternatively, don't bother preventing dumb things.
There's a similar issue in operator<< for
duration, which writes d.count() to the stream. For a custom rep type
that might be ill-formed. For character types it might print as a character
not an integer.
Proposed resolution:
This wording is relative to N4986.
Modify 30.2 [time.syn] as indicated:
namespace std { template<class Rep, class Period, class charT> requires formattable<Rep, charT> struct formatter<chrono::duration<Rep, Period>, charT>; template<class Duration, class charT> struct formatter<chrono::sys_time<Duration>, charT>;
move_iterator should provide iterator_category only when it models
forward_iteratorSection: 24.5.4.2 [move.iterator] Status: New Submitter: Hewill Kang Opened: 2024-07-12 Last modified: 2024-08-31
Priority: 3
View other active issues in [move.iterator].
View all other issues in [move.iterator].
View all issues with New status.
Discussion:
Following up LWG 4116(i), when the underlying iterator does not satisfy forward_iterator,
move_iterator::operator++(int) will return void, which fails to meet the C++17 iterator
requirements, which should not provide iterator_category.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll.
"NAD, would be a major breaking change to something present since C++11."
"We're knowingly breaking the rules here for backwards compatibility. Should have a note here indicating that the rule is broken for good reason, and that you should do as the Standard says and not as the Standard does."
Proposed resolution:
This wording is relative to N4986.
Modify 24.5.4.2 [move.iterator] as indicated:
[Drafting note:
Iteratorthat modelsforward_iteratorimplies thatiterator_traits<Iterator>::iterator_categoryis always valid as it satisfies the exposition-only conceptcpp17-iterator.]
-2- The member typedef-name
iterator_categoryis defined if and only ifthe qualified-iditerator_traits<Iterator>::iterator_categoryis valid and denotes a typeIteratormodelsforward_iterator. In that case,iterator_categorydenotes
(2.1) —
random_access_iterator_tagif the typeiterator_traits<Iterator>::iterator_categorymodelsderived_from<random_access_iterator_tag>, and(2.2) —
iterator_traits<Iterator>::iterator_categoryotherwise.
ranges::to constructs associative containers via c.emplace(c.end(), *it)Section: 25.5.7.1 [range.utility.conv.general] Status: New Submitter: Hewill Kang Opened: 2024-07-16 Last modified: 2025-03-17
Priority: 2
View all issues with New status.
Discussion:
When ranges::to constructs an associative container, if there is no range version constructor
for C and the input range is not common range, ranges::to will dispatch to the
bullet 25.5.7.2 [range.utility.conv.to] (2.1.4), which first default-constructs the container
and emplaces the element through c.emplace(c.end(), *it).
emplace() on an associative container as it does
not expect an iterator as the first argument, and since map::emplace(), for instance, is not
constrained, which turns out a hard error because we are trying to make a pair with
{it, pair}.
Given that libstdc++ currently does not implement the range constructor for associative containers, the following
illustrates the issue:
#include <ranges>
#include <set>
auto s = std::views::iota(0)
| std::views::take(5)
| std::ranges::to<std::set>(); // hard error
The proposed resolution simply removes the emplace() branch. Although this means that we always use
insert() to fill associative containers, such an impact seems negligible.
[2024-07-23; This was caused by LWG 4016(i).]
[2024-08-02; Reflector poll]
Set priority to 2 after reflector poll.
"Would like to preserve the ability to use emplace. Tim suggested trying
emplace_hint first, then emplace."
"I tried it, it gets very verbose, because we might also want to try
insert(*it) instead of insert(c.end(), *it) if emplace(*it) is not valid
for associative containers, because c.end() might not be a good hint."
"It might be suboptimal, but it still works."
Previous resolution [SUPERSEDED]:
This wording is relative to N4986.
Modify 25.5.7.1 [range.utility.conv.general] as indicated:
-4- Let
container-appendablebe defined as follows:template<class Container, class Ref> constexpr bool container-appendable = // exposition only requires(Container& c, Ref&& ref) { requires (requires { c.emplace_back(std::forward<Ref>(ref)); } || requires { c.push_back(std::forward<Ref>(ref)); } ||requires { c.emplace(c.end(), std::forward<Ref>(ref)); } ||requires { c.insert(c.end(), std::forward<Ref>(ref)); }); };-5- Let
container-appendbe defined as follows:template<class Container> constexpr auto container-append(Container& c) { // exposition only return [&c]<class Ref>(Ref&& ref) { if constexpr (requires { c.emplace_back(declval<Ref>()); }) c.emplace_back(std::forward<Ref>(ref)); else if constexpr (requires { c.push_back(declval<Ref>()); }) c.push_back(std::forward<Ref>(ref));else if constexpr (requires { c.emplace(c.end(), declval<Ref>()); }) c.emplace(c.end(), std::forward<Ref>(ref));else c.insert(c.end(), std::forward<Ref>(ref)); }; };
[2025-03-13; Jonathan provides improved wording for Tim's suggestion]
It's true that for some cases it might be optimal to use c.emplace(ref)
instead of c.emplace_hint(c.end(), ref) but I don't think I care.
A bad hint is not expensive, it's just an extra comparison then the hint
is ignored.
And this code path isn't going to be used for std::set or std::map,
only for user-defined associative containers that don't have a from_range_t
constructor or a C(Iter,Sent) constructor.
I think just fixing the original issue is all we need,
rather than trying to handle every possible way to insert elements.
This is a simpler, portable reproducer that doesn't depend on the current
implementation status of std::set in libstdc++:
#include <ranges> #include <set> struct Set : std::set<int> { Set() { }; // No other constructors }; int main() { int a[1]; auto okay = std::ranges::to<std::set<int>>(a); auto ohno = std::ranges::to<Set>(a); }
Proposed resolution:
This wording is relative to N5001.
Modify 25.5.7.1 [range.utility.conv.general] as indicated:
-4- Let
container-appendablebe defined as follows:template<class Container, class Ref> constexpr bool container-appendable = // exposition only requires(Container& c, Ref&& ref) { requires (requires { c.emplace_back(std::forward<Ref>(ref)); } || requires { c.push_back(std::forward<Ref>(ref)); } || requires { c.emplace_hint(c.end(), std::forward<Ref>(ref)); } || requires { c.emplace(c.end(), std::forward<Ref>(ref)); } || requires { c.insert(c.end(), std::forward<Ref>(ref)); }); };-5- Let
container-appendbe defined as follows:template<class Container> constexpr auto container-append(Container& c) { // exposition only return [&c]<class Ref>(Ref&& ref) { if constexpr (requires { c.emplace_back(declval<Ref>()); }) c.emplace_back(std::forward<Ref>(ref)); else if constexpr (requires { c.push_back(declval<Ref>()); }) c.push_back(std::forward<Ref>(ref)); else if constexpr (requires { c.emplace_hint(c.end(), declval<Ref>()); }) c.emplace_hint(c.end(), std::forward<Ref>(ref)); else if constexpr (requires { c.emplace(c.end(), declval<Ref>()); }) c.emplace(c.end(), std::forward<Ref>(ref)); else c.insert(c.end(), std::forward<Ref>(ref)); }; };
operator<=> can cause hard error when instantiating std::inplace_vectorSection: 23.3.16.1 [inplace.vector.overview] Status: New Submitter: Jiang An Opened: 2024-07-20 Last modified: 2024-08-02
Priority: 2
View all issues with New status.
Discussion:
This is almost the same problem as LWG 4071(i) except that it happens to std::inplace_vector.
As the operator<=> overload for std::inplace_vector is a non-template function whose return
type (synth-three-way-result<T>) is not deduced, when the return type is ill-formed, hard
error occurs in the instantiation of the enclosing std::inplace_vector<T, N>.
[2024-08-02; Reflector poll]
Set priority to 2 after reflector poll.
Proposed resolution:
This wording is relative to N4986.
Modify 23.3.16.1 [inplace.vector.overview], class template std::inplace_vector synopsis, as indicated:
namespace std {
template<class T, size_t N>
class inplace_vector {
public:
[…]
constexpr friend bool operator==(const inplace_vector& x,
const inplace_vector& y);
constexpr friend synth-three-way-result<T>auto
operator<=>(const inplace_vector& x, const inplace_vector& y);
requires requires (const T t) { synth-three-way(t, t); }
{
return lexicographical_compare_three_way(x.begin(), x.end(), y.begin(), y.end(),
synth-three-way);
}
constexpr friend void swap(inplace_vector& x, inplace_vector& y)
noexcept(N == 0 || (is_nothrow_swappable_v<T> &&
is_nothrow_move_constructible_v<T>))
{ x.swap(y); }
};
};
Section: 23.3.5.4 [deque.modifiers], 23.3.13.5 [vector.modifiers], 23.3.16.5 [inplace.vector.modifiers] Status: New Submitter: Jonathan Wakely Opened: 2024-07-25 Last modified: 2024-12-06
Priority: 3
View other active issues in [deque.modifiers].
View all other issues in [deque.modifiers].
View all issues with New status.
Discussion:
The spec for deque::erase talks about a exception
"thrown by the assignment operator of T" but it's unclear which
assignment operator this means.
Arguably, this is fine because it means "the assignment operator that
is used when repositioning the remaining elements".
However, deque::append_range, vector::append_range, vector::erase,
inplace_vector::append_range, and inplace_vector::erase talk about
"the assignment operator or move assignment operator" which is just odd.
In C++03 this just said "the assignment operator" and move semantics
added "or the move assignment operator" but we could improve it.
What we should talk about is "an assignment operator", or "the assignment operator selected by overload resolution for the assignment expressions performed by the operation", or something like that.
This is potentially a bigger issue than just assignment:
for append_range we say
"If an exception is thrown other than by the copy constructor,
move constructor, assignment operator, or move assignment operator [...]"
and there's no guarantee that the constructor used for initializing a
Cpp17CopyInsertable type is a copy constructor or move constructor.
It could be some templated constructor that is a better match than any
of the special member functions.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll. Arthur to draft wording.
[2024-12-06; LWG telecon]
23.3.11.4 [list.modifiers] p1 says:
Complexity: Insertion of a single element into a list takes constant time and exactly one call to a constructor ofIn addition to incorrectly talking about "the copy constructor or move constructor", it should not should not talk about any "call to a constructor" because scalars do not have constructors at all. We should talk about calls toT. Insertion of multiple elements into a list is linear in the number of elements inserted, and the number of calls to the copy constructor or move constructor ofTis exactly equal to the number of elements inserted.
allocator_traits::construct not constructors,
or objects being constructed.
Similarly, p5 says:
Complexity: Erasing a single element is a constant time operation with a single call to the destructor ofThis should talk about calls toT. Erasing a range in a list is linear time in the size of the range and the number of calls to the destructor of typeTis exactly equal to the size of the range.
allocator_traits::destroy,
or objects being destroyed.
23.3.5.4 [deque.modifiers] is similar. Look for similar problems elsewhere.
[2024-12-06; Jonathan adds wording, incorporating Arthur's wording]
Proposed resolution:
This wording is relative to N4993.
Modify 23.3.5.3 [deque.capacity] as indicated:
void shrink_to_fit();-5- Preconditions:
Tis CppMoveInsertable intodeque.-6- Effects:
shrink_to_fitis a non-binding request to reduce memory use but does not change the size of the sequence. [Note 1: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] If the size is equal to the old capacity, or if an exception is thrown other than by themove constructormove-construction of one object of a non-Cpp17CopyInsertable typeTfrom another, then there are no effects.
Modify 23.3.5.4 [deque.modifiers] as indicated:
iterator insert(const_iterator position, const T& x); ... template<container-compatible-range<T> R> void append_range(R&& rg);-1- Effects: [...]
-2- Complexity: The complexity is linear in the number of elements inserted plus the lesser of the distances to the beginning and end of the deque. Inserting a single element at either the beginning or end of a deque always takes constant time and
causes a single call to a constructor of Tconstructs a single object of typeT.-3- Remarks: If an exception is thrown other than by the
copy constructor, move constructor, assignment operator, or move assignment operator ofconstruction or assignment of one object of typeTTfrom another , there are no effects. If an exception is thrown while inserting a single element at either end, there are no effects. Otherwise, if an exception is thrown by themove constructor of amove-construction of one object of non-Cpp17CopyInsertable typeTfrom another, the effects are unspecified.[...]
-5- Throws: Nothing unless an exception is thrown bythe assignment operator ofthe assignment of one object of typeTTfrom another .iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last); void pop_front(); void pop_back();-4- Effects: An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements. An erase operation that erases the first element of a deque but not the last element invalidates only iterators and references to the erased elements. An erase operation that erases neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque.
[Note 1:
pop_frontandpop_backare erase operations. — end note]-5- Throws: Nothing unless an exception is thrown by
thean assignment operator ofT.-6- Complexity: The number of
calls to the destructor ofobjects of typeTTdestroyed is the same as the number of elements erased, but the number of calls to the assignment operator ofTis no more than the lesser of the number of elements before the erased elements and the number of elements after the erased elements.
Modify 23.3.7.5 [forward.list.modifiers] as indicated:
-1- [...] Inserting
nelements into aforward_listis linear inn, and the number ofcalls to the copy or move constructor ofobjects of typeTconstructed is exactly equal ton. Erasingnelements from aforward_listis linear innand the number ofcalls to the destructor ofobjects of typeTdestroyed is exactly equal ton.
Modify 23.3.11.4 [list.modifiers] as indicated:
-1- Complexity: Insertion of a single element into a list takes constant time and constructs exactly one
call to a constructor ofobject of typeTT. Insertion of multiple elements into a list is linear in the number of elements inserted and the number ofcalls to the copy constructor or move constructor ofobjects of typeTTconstructed is exactly equal to the number of elements inserted.-2- Remarks: Does not affect the validity of iterators and references. If an exception is thrown, there are no effects.
iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last); void pop_front(); void pop_back(); void clear() noexcept;-3- Effects: Invalidates only the iterators and references to the erased elements.
-4- Throws: Nothing.
-5- Complexity: Erasing a single element is a constant time operation with a single
call to the destructor ofobject of typeTTdestroyed. Erasing a range in a list is linear time in the size of the range and the number ofcalls to the destructor of typeobjects of typeTTdestroyed is exactly equal to the size of the range.
Modify 23.3.13.2 [vector.cons] as indicated:
template<class InputIterator> constexpr vector(InputIterator first, InputIterator last, const Allocator& = Allocator());-9- Effects: Constructs a
vectorequal to the range [first,last), using the specified allocator.-10- Complexity:
Makes only N calls to the copy constructor ofInitializes exactly N elements (where N is the distance betweenTfirstandlast) and no reallocations if iteratorsfirstandlastare of forward, bidirectional, or random access categories. Itmakesinitializes order Ncalls to the copy constructor ofelements and performs order log N reallocations if they are just input iterators.Ttemplate<container-compatible-range<T> R> constexpr vector(from_range_t, R&& rg, const Allocator& = Allocator());-11- Effects: Constructs a
vectorobject with the elements of the rangerg, using the specified allocator.-12- Complexity: Initializes exactly N elements from the results of dereferencing successive iterators of
rg, where N isranges::distance(rg). Performs no reallocations ifRmodelsranges::forward_rangeorranges::sized_range; otherwise, performs order log N reallocations and initializes order Ncalls to the copy or move constructor ofelements.T
Modify 23.3.13.3 [vector.capacity] as indicated:
constexpr void reserve(size_type n);-3- Preconditions:
Tis CppMoveInsertable intovector.-4- Effects: A directive that informs a
vectorof a planned change in size, so that it can manage the storage allocation accordingly. Afterreserve<del>()</del>,capacity()is greater or equal to the argument ofreserveif reallocation happens; and equal to the previous value ofcapacity()otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument ofreserve<del>()</del>. If an exception is thrown other than by themove constructor of amove-construction of one object of non-Cpp17CopyInsertable typeTfrom another, there are no effects.[...]
constexpr shrink_to_fit();-8- Preconditions:
Tis CppMoveInsertable intovector.-9- Effects:
shrink_to_fitis a non-binding request to reducecapacity()tosize(). [Note 2: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] It does not increasecapacity(), but may reducecapacity()by causing reallocation. If an exception is thrown other than by themove constructormove-construction of one object of a non-Cpp17CopyInsertable typeTfrom another, there are no effects.[...]
constexpr void resize(size_type sz);-14- Preconditions:
Tis Cpp17MoveInsertable and Cpp17DefaultInsertable intovector.-15- Effects: [...]
-16- Remarks: If an exception is thrown other than by the
move constructormove-construction of one object of a non-Cpp17CopyInsertable typeTfrom another, there are no effects.
Modify 23.3.13.5 [vector.modifiers] as indicated:
iterator insert(const_iterator position, const T& x); ... template<container-compatible-range<T> R> void append_range(R&& rg);-1- Complexity: [...]
-2- Remarks: Causes reallocation if the new size is greater than the old capacity. Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence, as well as the past-the-end iterator. If no reallocation happens, then references, pointers, and iterators before the insertion point remain valid but those at or after the insertion point, including the past-the-end iterator, are invalidated. If an exception is thrown other than by the
copy constructor, move constructor, assignment operator, or move assignment operator ofthe construction or assignment of one object of typeTor by any \tcode{InputIterator} operationTfrom another, or by anyInputIteratoroperation, there are no effects. If an exception is thrown while inserting a single element at the end andTis Cpp17CopyInsertable oris_nothrow_move_constructible_v<T>istrue, there are no effects. Otherwise, if an exception is thrown by themove constructor of amove-construction of one object of non-Cpp17CopyInsertable typeTfrom another, the effects are unspecified.constexpr iterator erase(const_iterator position); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void pop_back();-3- Effects: Invalidates iterators and references at or after the point of the erase.
-4- Throws: Nothing unless an exception is thrown by the
assignment operator or move assignment operator ofconstruction or assignment of one object of typeTTfrom another.-5- Complexity: The
destructor ofnumber of objects of typeTis called the number of timesTdestroyed is equal to the number of the elements erased, butthean assignment operator ofTis called the number of times equal to the number of elements in the vector after the erased elements.
Modify 23.3.16.5 [inplace.vector.modifiers] as indicated:
constexpr iterator erase(const_iterator position); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void pop_back();-29- Complexity: The
destructor ofnumber of objects of typeTis called the number of timesTdestroyed is equal to the number of the elements erased, butthean assignment operator ofTis called the number of times equal to the number of elements after the erased elements.
move_iterator's default constructor should be constrainedSection: 24.5.4.2 [move.iterator], 24.5.4.4 [move.iter.cons] Status: New Submitter: Hewill Kang Opened: 2024-07-22 Last modified: 2025-02-07
Priority: 3
View other active issues in [move.iterator].
View all other issues in [move.iterator].
View all issues with New status.
Discussion:
Although it is unclear why P2325 did not apply to move_iterator, there is
implementation divergence among the current libraries (demo):
#include <istream>
#include <iterator>
#include <ranges>
using R = std::ranges::istream_view<int>;
using I = std::ranges::iterator_t<R>;
using MI = std::move_iterator<I>;
static_assert(std::default_initializable<MI>); // libstdc++ passes, libc++ fails
As libc++ additionally requires that its default constructors satisfy is_constructible_v<Iterator>.
move_iterator
only requires the underlying iterator to be input_iterator which may not be default_initializable.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll.
Six P0 (tentatively ready) votes but one for NAD:
"design change, needs paper. Not constraining this seems to be intended by
P2325R3. Even if we want to constrain it,
why default_initializable rather than just is_constructible_v?"
Author of P2325R3 had no such intent and voted P0.
Issue submitter used default_initializable to align with forward_iterator
requirements, but would not object to implementers using is_constructible_v
for backported DRs.
Proposed resolution:
This wording is relative to N4986.
Modify 24.5.4.2 [move.iterator] as indicated:
namespace std {
template<class Iterator>
class move_iterator {
public:
[…]
constexpr move_iterator() requires default_initializable<Iterator> = default;
[…]
private:
Iterator current = Iterator(); // exposition only
};
}
Modify 24.5.4.4 [move.iter.cons] as indicated:
constexpr move_iterator();
-1- Effects: Value-initializescurrent.
pred(*i) != falseSection: 22.10.18.3 [func.search.bm], 22.10.18.4 [func.search.bmh], 23.2.7.1 [associative.reqmts.general], 23.3.11.5 [list.ops], 26.6.6 [alg.find], 26.6.9 [alg.find.first.of], 26.6.10 [alg.adjacent.find], 26.6.11 [alg.count], 26.6.12 [alg.mismatch], 26.6.15 [alg.search], 26.8.1 [alg.sorting.general] Status: New Submitter: Hewill Kang Opened: 2024-07-25 Last modified: 2024-08-05
Priority: 3
View other active issues in [func.search.bm].
View all other issues in [func.search.bm].
View all issues with New status.
Discussion:
The boolean-testable concept introduced in P1964R2
places appropriate constraints on boolean-ish types, so that !pred(x) or
i != last && pred(*i) always has a valid definition.
decltype(pred(*first)) should model boolean-testable.
However, boolean-testable does not guarantee that
pred(*i) != false is valid, because the type may overload operator==.
It is necessary to replace this form of expression with an appropriate one as we do in
P1964R2.
[2024-07-27; Daniel comments]
I would recommend to add a wrapping "bool(pred([…]))" and possibly
even extend to "bool(pred([…])) is true"
following our usual wording convention, since an English phrase of the form "if pred([…])"
where pred([…]) is potential non-bool and the English "if" is not a C++ language
if (with built-in conversion semantics) doesn't sound like a meaningful phrase to me.
[2024-08-02; Reflector poll]
Set priority to 3 after reflector poll. "Needs more 'is true' added".
"Would prefer not to have explicit conversions to bool unless
boolean-testable requires that.
The 'Let E be' lists don't need an 'is true' there,
but the use of 'E' should say 'is true'".
[alg.search] and [func.search.bm] have changed editorially in the draft,
the proposed resolution needs updating.
Previous resolution [SUPERSEDED]:
This wording is relative to N4986.
Modify 22.10.18.3 [func.search.bm] as indicated:
boyer_moore_searcher(RandomAccessIterator1 pat_first, RandomAccessIterator1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());-1- Preconditions: The value type of
-2- LetRandomAccessIterator1meets the Cpp17DefaultConstructible, the Cpp17CopyConstructible, and the Cpp17CopyAssignable requirements.Vbeiterator_traits<RandomAccessIterator1>::value_type. For any two valuesAandBof typeV, ifpred(A, B), then== truehf(A) == hf(B)istrue. […]-7- Returns: A pair of iterators
iandjsuch that
(7.1) —
iis the first iterator in the range [first,last - (pat_last_ - pat_first_)) such that for every non-negative integernless thanpat_last_ - pat_first_the following condition holds:pred(*(i + n), *(pat_first_ + n)), and!= false[…]
Modify 22.10.18.4 [func.search.bmh] as indicated:
boyer_moore_horspool_searcher(RandomAccessIterator1 pat_first, RandomAccessIterator1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());-1- Preconditions: The value type of
-2- LetRandomAccessIterator1meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.Vbeiterator_traits<RandomAccessIterator1>::value_type. For any two valuesAandBof typeV, ifpred(A, B), then== truehf(A) == hf(B)istrue. […]-7- Returns: A pair of iterators
iandjsuch that
(7.1) —
iis the first iterator in the range [first,last - (pat_last_ - pat_first_)) such that for every non-negative integernless thanpat_last_ - pat_first_the following condition holds:pred(*(i + n), *(pat_first_ + n)), and!= false[…]
Modify 23.2.7.1 [associative.reqmts.general] as indicated:
-3- The phrase "equivalence of keys" means the equivalence relation imposed by the comparison object. That is, two keys
[…]k1andk2are considered to be equivalent if for the comparison objectcomp,!comp(k1, k2).== false&& !comp(k2, k1)== false-177- The fundamental property of iterators of associative containers is that they iterate through the containers in the non-descending order of keys where non-descending is defined by the comparison that was used to construct them. For any two dereferenceable iterators
iandjsuch that distance fromitojis positive, the following condition holds:!value_comp(*j, *i)== false-178- For associative containers with unique keys the stronger condition holds:
value_comp(*i, *j)!= falseModify 23.3.11.5 [list.ops] as indicated:
size_type remove(const T& value); template<class Predicate> size_type remove_if(Predicate pred);-15- Effects: Erases all the elements in the list referred to by a list iterator
-16- Returns: The number of elements erased. -17- Throws: Nothing unless an exception is thrown byifor which the following conditions hold:*i == value, pred(*i). Invalidates only the iterators and references to the erased elements.!= false*i == valueorpred(*i). […]!= falseModify 26.6.6 [alg.find] as indicated:
-1- Let E be:
(1.1) —
*i == valueforfind;(1.2) —
pred(*i)for!= falsefind_if;(1.3) —
[…]!pred(*i)for== falsefind_if_not;Modify 26.6.9 [alg.find.first.of] as indicated:
-1- Let E be:
(1.1) —
*i == *jfor the overloads with no parameterpred;(1.2) —
[…]pred(*i, *j)for the overloads with a parameter!= falsepredand no parameterproj1;Modify 26.6.10 [alg.adjacent.find] as indicated:
-1- Let E be:
(1.1) —
*i == *(i + 1)for the overloads with no parameterpred;(1.2) —
[…]pred(*i, *(i + 1))for the overloads with a parameter!= falsepredand no parameterproj1;Modify 26.6.11 [alg.count] as indicated:
-1- Let E be:
(1.1) —
*i == valuefor the overloads with no parameterpredorproj;(1.2) —
[…]pred(*i)for the overloads with a parameter!= falsepredbut no parameterproj;Modify 26.6.12 [alg.mismatch] as indicated:
-2- Let E be:
(2.1) —
!(*(first1 + n) == *(first2 + n))for the overloads with no parameterpred;(2.2) —
[…]!pred(*(first1 + n), *(first2 + n))for the overloads with a parameter== falsepredand no parameterproj1;Modify 26.6.15 [alg.search] as indicated:
-1- Returns: The first iterator
[…]iin the range [first1,last1 - (last2-first2)) such that for every non-negative integernless thanlast2 - first2the following corresponding conditions hold:*(i + n) == *(first2 + n), pred(*(i + n), *(first2 + n)). Returns!= falsefirst1if [first2,last2) is empty, otherwise returnslast1if no such iterator is found.-6- Returns: The first iterator
iin the range [first,last-count) such that for every non-negative integernless thancountthe following corresponding conditions hold:*(i + n) == value, pred(*(i + n), value). Returns!= falselastif no such iterator is found.Modify 26.8.1 [alg.sorting.general] as indicated:
-3- For all algorithms that take
Compare, there is a version that usesoperator<instead. That is,comp(*i, *j)defaults to!= false*i < *j. For algorithms other than those described in 26.8.4 [alg.binary.search],!= falsecompshall induce a strict weak ordering on the values.
[2024-08-03; Daniel provides improved wording]
The current wording is inconsistent in regard to explicit conversion to bool
and lack of them in cases of expressions whose value is required to satisfy the
boolean-testable constraints. To realize consistent results for
all subclause references touched by the changes required by this issue I decided
that every E definition remains unconverted but and that every E
evaluation is interpreted as if an implied bool conversion has
been applied based on the reflector preference for that simplified approach.
bool for both expressions, because the
boolean-testable requirements do not impose a no-throw requirement for
that conversion, and they must therefore be included here.
This problem will be handled by a separate issue (LWG 4132(i)), because the
rationale for this change is different from the actual target of this issue and not
related to the other consistency adjustments done by the wording below.
Proposed resolution:
This wording is relative to N4988.
Modify 22.10.18.3 [func.search.bm] as indicated:
boyer_moore_searcher(RandomAccessIterator1 pat_first, RandomAccessIterator1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());-1- Preconditions: The value type of
-2- LetRandomAccessIterator1meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.Vbeiterator_traits<RandomAccessIterator1>::value_type. For any two valuesAandBof typeV, ifpred(A, B)is== truetrue, thenhf(A) == hf(B)istrue. […]template<class RandomAccessIterator2> pair<RandomAccessIterator2, RandomAccessIterator2> operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;[…]
-7- Returns: A pair of iteratorsiandjsuch that
(7.1) —
iis the first iterator in the range [first,last - (pat_last_ - pat_first_)) such that for every non-negative integernless thanpat_last_ - pat_first_the following condition holds:pred(*(i + n), *(pat_first_ + n))is!= falsetrue, and(7.2) —
j == next(i, distance(pat_first_, pat_last_))istrue.
Modify 22.10.18.4 [func.search.bmh] as indicated:
boyer_moore_horspool_searcher(RandomAccessIterator1 pat_first, RandomAccessIterator1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());-1- Preconditions: The value type of
-2- LetRandomAccessIterator1meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.Vbeiterator_traits<RandomAccessIterator1>::value_type. For any two valuesAandBof typeV, ifpred(A, B), then==is truehf(A) == hf(B)istrue. […]template<class RandomAccessIterator2> pair<RandomAccessIterator2, RandomAccessIterator2> operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;[…]
-7- Returns: A pair of iteratorsiandjsuch that
(7.1) —
iis the first iterator in the range [first,last - (pat_last_ - pat_first_)) such that for every non-negative integernless thanpat_last_ - pat_first_the following condition holds:pred(*(i + n), *(pat_first_ + n))is!= falsetrue, and(7.2) —
j == next(i, distance(pat_first_, pat_last_))istrue.
Modify 23.2.7.1 [associative.reqmts.general] as indicated:
-3- The phrase "equivalence of keys" means the equivalence relation imposed by the comparison object. That is, two keys
[…]k1andk2are considered to be equivalent if for the comparison objectcomp,!comp(k1, k2)is== false&& !comp(k2, k1)== falsetrue.-177- The fundamental property of iterators of associative containers is that they iterate through the containers in the non-descending order of keys where non-descending is defined by the comparison that was used to construct them. For any two dereferenceable iterators
iandjsuch that distance fromitojis positive, the following condition holds:
!value_comp(*j, *i)is== falsetrue.-178- For associative containers with unique keys the stronger condition holds:
value_comp(*i, *j)is!= falsetrue.
Modify 23.3.11.5 [list.ops] as indicated:
size_type remove(const T& value); template<class Predicate> size_type remove_if(Predicate pred);-15- Effects: Erases all the elements in the list referred to by a list iterator
-16- Returns: The number of elements erased. -17- Throws: Nothing unless an exception is thrown byifor which the following conditions hold:*i == valueistrue,pred(*i)is!= falsetrue. Invalidates only the iterators and references to the erased elements.*i == valueorpred(*i). […]!= false
Modify 26.6.6 [alg.find] as indicated:
[Drafting note: Sub-bullets (1.4), (1.5), and (1.6) below regarding
invokeexpressions are similarly required modelboolean-testable, as specified by conceptpredicate(18.7.4 [concept.predicate]), therefore the explicit conversion is removed here for consistency]
-1- Let E be:
(1.1) —
*i == valueforfind;(1.2) —
pred(*i)for!= falsefind_if;(1.3) —
!pred(*i)for== falsefind_if_not;(1.4) —
forbool(invoke(proj, *i) == value)ranges::find;(1.5) —
forbool(invoke(pred, invoke(proj, *i)))ranges::find_if;(1.6) —
forbool(!invoke(pred, invoke(proj, *i)))ranges::find_if_not.-2- Returns: The first iterator
iin the range[first, last)for which E istrue. Returnslastif no such iterator is found.
Modify 26.6.9 [alg.find.first.of] as indicated:
-1- Let E be:
(1.1) —
*i == *jfor the overloads with no parameterpred;(1.2) —
pred(*i, *j)for the overloads with a parameter!= falsepredand no parameterproj1;(1.3) —
for the overloads with parametersbool(invoke(pred, invoke(proj1, *i), invoke(proj2, *j)))predandproj1.[…]
-3- Returns: The first iteratoriin the range[first1, last1)such that for some iteratorjin the range[first2, last2)Eholdsistrue. Returnslast1if[first2, last2)is empty or if no such iterator is found.
Modify 26.6.10 [alg.adjacent.find] as indicated:
-1- Let E be:
(1.1) —
*i == *(i + 1)for the overloads with no parameterpred;(1.2) —
pred(*i, *(i + 1))for the overloads with a parameter!= falsepredand no parameterproj;(1.3) —
for the overloads with both parametersbool(invoke(pred, invoke(proj, *i), invoke(proj, *(i + 1))))predandproj.-2- Returns: The first iterator
isuch that bothiandi + 1are in the range[first, last)for which Eholdsistrue. Returnslastif no such iterator is found.
Modify 26.6.11 [alg.count] as indicated:
-1- Let E be:
(1.1) —
*i == valuefor the overloads with no parameterpredorproj;(1.2) —
pred(*i)for the overloads with a parameter!= falsepredbut no parameterproj;(1.3) —
invoke(proj, *i) == valuefor the overloads with a parameterprojbut no parameterpred;(1.4) —
for the overloads with both parametersbool(invoke(pred, invoke(proj, *i)))projandpred.-2- Effects: Returns the number of iterators
iin the range[first, last)for which Eholdsistrue.
Modify 26.6.12 [alg.mismatch] as indicated:
-2- Let E be:
(2.1) —
!(*(first1 + n) == *(first2 + n))for the overloads with no parameterpred;(2.2) —
!pred(*(first1 + n), *(first2 + n))for the overloads with a parameter== falsepredand no parameterproj1;(2.3) —
!invoke(pred, invoke(proj1, *(first1 + n)), invoke(proj2, *(first2 + n)))for the overloads with both parameterspredandproj1.[…]
-4- Returns:{ first1 + n, first2 + n }, wherenis the smallest integer in [0, N) such that Eholdsistrue, or N if no such integer exists.
Modify 26.6.15 [alg.search] as indicated:
-1- Returns: The first iterator
[…]iin the range [first1,last1 - (last2-first2)) such that for every non-negative integernless thanlast2 - first2the following corresponding conditions hold:*(i + n) == *(first2 + n)istrue,pred(*(i + n), *(first2 + n))is!= falsetrue. Returnsfirst1if [first2,last2) is empty, otherwise returnslast1if no such iterator is found.-6- Let E be
-7- Returns: The first iteratorpred(*(i + n), value)for the overloads with a parameter!= falsepred, and*(i + n) == valueotherwise.iin the range[first, last - count)such that for every non-negative integernless thancountthe condition E istrue. Returnslastif no such iterator is found.
Modify 26.8.1 [alg.sorting.general] as indicated:
[Drafting note: The wording below does not require extra "is
true" added to the adjusted expressions. This is comparable to the E cases above, since 26.8.1 [alg.sorting.general] p2 already points out:The return value of the function call operation applied to an object of type
Compare, when converted tobool, yieldstrueif the first argument of the call is less than the second, andfalseotherwise.]
-3- For all algorithms that take
Compare, there is a version that usesoperator<instead. That is,comp(*i, *j)defaults to!= false*i < *j. For algorithms other than those described in 26.8.4 [alg.binary.search],!= falsecompshall induce a strict weak ordering on the values.
Section: 16.4.4.6.1 [allocator.requirements.general] Status: New Submitter: Jonathan Wakely Opened: 2024-08-02 Last modified: 2024-08-21
Priority: 3
View other active issues in [allocator.requirements.general].
View all other issues in [allocator.requirements.general].
View all issues with New status.
Discussion:
The Cpp17Allocator requirements require a non-explicit copy constructor, but do not require a non-explicit converting constructor used for rebinding.
Since C++14 it has been clearly stated that
"An allocator type X shall satisfy the requirements of CopyConstructible".
That requires X u = a; to work as well as X u(a);,
but only when the type of a is X.
Constructing a rebound allocator from another specialization of the same
allocator class template is only required to work using direct-initialization,
X u(b);.
This means it's permitted to make the converting constructor explicit, so that X u = b; is ill-formed.
There seems to be no good reason to allow allocators to make that ill-formed.
In fact, there seems to be a good reason to not allow it.
The uses_allocator trait is defined in terms of is_convertible,
not is_constructible, which means that if Alloc<T>
has an explicit converting constructor,
then uses_allocator_v<X, Alloc<T>> will be false
for a type with X::allocator_type = Alloc<U>,
because you would need to explicitly convert an Alloc<T> to
Alloc<U> before passing it to X's constructor.
That is at least consistent:
the trait gives the right answer even for allocators with explicit conversions.
It doesn't seem very useful though, and if users don't carefully check
uses_allocator with exactly the right types they might get errors unless
they carefully rebind and convert their allocators explicitly.
Or worse, if they rely on other library components to check uses_allocator,
they might silently get the wrong behaviour.
For example, trying to construct an X with an Alloc<T>
(e.g. via make_obj_using_allocator)
could silently fail to pass the allocator to the constructor
because there's no implicit conversion to X::allocator_type,
even though you're providing an allocator of the right "family"
and trying to use it. So a constructor without an allocator argument could be
chosen when you thought you were supplying an allocator for the object to use.
There seemed to be consensus when LWG discussed it that we should just require conversions to be implicit, so that allocators are not silently ignored because they cannot be implicitly converted.
During the discussion it was noted that assigning allocators is only needed when the propagation trait is true, but such assignments are always done between objects of the same allocator type. So the allocator requirements do not need to require converting assignments to work.
[2024-08-21; Reflector poll]
Set priority to 3 after reflector poll. Jonathan to add an Annex C entry about the change.
Proposed resolution:
This wording is relative to N4986.
Modify 16.4.4.6.1 [allocator.requirements.general] as indicated:
X u(a); X u = a;-63- Postconditions:
u == aistrue.-64- Throws: Nothing.
X u(b); X u = b;-65- Postconditions:
Y(u) == bandu == X(b)are bothtrue.-66- Throws: Nothing.
X u(std::move(a)); X u = std::move(a);-67- Postconditions: The value of
ais unchanged and is equal tou.-68- Throws: Nothing.
X u(std::move(b)); X u = std::move(b);-69- Postconditions:
uis equal to the prior value ofX(b).-70- Throws: Nothing.
Section: 16.4.6.10 [res.on.data.races] Status: New Submitter: Jiang An Opened: 2024-07-30 Last modified: 2025-10-14
Priority: 3
View other active issues in [res.on.data.races].
View all other issues in [res.on.data.races].
View all issues with New status.
Discussion:
From PR cplusplus/draft#6748 which was closed as non-editorial.
Currently, 16.4.6.10 [res.on.data.races] is talking about arguments and "includingthis",
but this is not a function argument. Moreover, it seems more appropriate to say that a function
accesses some object via a parameter.
It might need to be considered whether we should use the more general term "range" instead of "container",
which will cover std::span, etc.
[2025-10-14; Reflector poll]
Set priority to 3 after reflector poll.
"'non-const parameters' needs to be fixed, this isn't about
top-level const, e.g. std::string& is not a const type."
Proposed resolution:
This wording is relative to N4986.
Modify 16.4.6.10 [res.on.data.races] as indicated:
-2- A C++ standard library function shall not directly or indirectly access objects (6.10.2 [intro.multithread]) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's
-3- A C++ standard library function shall not directly or indirectly modify objects (6.10.2 [intro.multithread]) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's non-constargumentsparameters, includingthe object parameter (if any).thisargumentsparameters, includingthe object parameter (if any). […] -5- A C++ standard library function shall not access objects indirectly accessible via itsthisargumentsparameters or via elements of its containerargumentsparameters except by invoking functions required by its specification on those container elements.
std::launder might be overly strictSection: 17.6.5 [ptr.launder] Status: Open Submitter: Jiang An Opened: 2024-07-30 Last modified: 2024-11-19
Priority: 3
View all other issues in [ptr.launder].
View all issues with Open status.
Discussion:
From issue cplusplus/draft#4553 which is considered non-editorial.
Currently,std::launder has a precondition that the pointed to object must be within
its lifetime. However, per the example added by P0593R6, it's probably intended
that the result of std::launder should be allowed to point to an array element
subobject whose lifetime has not started yet.
[2024-10-02; Reflector poll]
Set priority to 3 after reflector poll. Needs review by Core.
[Wrocław 2024-11-18; approved by Core]
Proposed resolution:
This wording is relative to N4986.
Modify 17.6.5 [ptr.launder] as indicated:
template<class T> [[nodiscard]] constexpr T* launder(T* p) noexcept;-1- Mandates:
-2- Preconditions:!is_function_v<T> && !is_void_v<T>istrue.prepresents the address A of a byte in memory. An object Xthat is within its lifetime (6.8.4 [basic.life]) andwhose type is similar (7.3.6 [conv.qual]) toTis located at the address A, and is either within its lifetime (6.8.4 [basic.life]) or is an array element subobject whose containing array object is within its lifetime. All bytes of storage that would be reachable through (6.9.4 [basic.compound]) the result are reachable throughp. -3- Returns: A value of typeT*that points to X. […]
<optional> doesn't provide std::begin/endSection: 24.7 [iterator.range] Status: New Submitter: Hewill Kang Opened: 2024-08-02 Last modified: 2024-08-21
Priority: 3
View other active issues in [iterator.range].
View all other issues in [iterator.range].
View all issues with New status.
Discussion:
Since optional now provides begin/end members, it is reasonable
to ensure the validity of std::begin/end after including <optional>.
[2024-08-21; Reflector poll]
Set priority to 3 after reflector poll.
"I would like to hear opinion of P3168 authors, or have the change discussed as a part of P3016.
Proposed resolution:
This wording is relative to N4986.
Modify 24.7 [iterator.range] as indicated:
-1- In addition to being available via inclusion of the
<iterator>header, the function templates in 24.7 [iterator.range] are available when any of the following headers are included:<array>(23.3.2 [array.syn]),<deque>(23.3.4 [deque.syn]),<flat_map>(23.6.7 [flat.map.syn]),<flat_set>(23.6.10 [flat.set.syn]),<forward_list>(23.3.6 [forward.list.syn]),<inplace_vector>(23.3.15 [inplace.vector.syn]),<list>(23.3.10 [list.syn]),<map>(23.4.2 [associative.map.syn]),<optional>(22.5.2 [optional.syn]),<regex>(28.6.3 [re.syn]),<set>(23.4.5 [associative.set.syn]),<span>(23.7.2.1 [span.syn]),<string>(27.4.2 [string.syn]),<string_view>(27.3.2 [string.view.synop]),<unordered_map>(23.5.2 [unord.map.syn]),<unordered_set>(23.5.5 [unord.set.syn]), and<vector>(23.3.12 [vector.syn]).
boolean-testable operationsSection: 23.2.7.1 [associative.reqmts.general], 23.3.11.5 [list.ops], 23.3.7.6 [forward.list.ops] Status: New Submitter: Daniel Krügler Opened: 2024-08-03 Last modified: 2024-10-02
Priority: 3
View other active issues in [associative.reqmts.general].
View all other issues in [associative.reqmts.general].
View all issues with New status.
Discussion:
The standard provides various Throws: elements whose specification refers to boolean-like expressions, for example 23.3.11.5 [list.ops] says:
-17- Throws: Nothing unless an exception is thrown by
*i == valueorpred(*i) != false.
The expressions *i == value (by [tab:cpp17.equalitycomparable]) and pred(*i)
(by 26.2 [algorithms.requirements]) both require to have a type that models
boolean-testable, so their actual evaluation potentially requires an additional
conversion to bool (The part "!= false" was not guaranteed to realize that as
pointed out by LWG 4127(i) and will therefore be removed by that issue).
boolean-testable requirements (18.5.2 [concept.booleantestable]) doesn't
say anything about potential exceptions thrown by this conversion, so we must assume that this
conversion allows an exception to be thrown.
An additional problem arises by the fact that the boolean-testable requirements
do not require that the implicit and explicit conversion to bool have to behave
consistently in regard to exceptions. 18.4.4 [concept.convertible] only says
that "The implicit and explicit conversions are required to produce equal results". But
since we don't want to restrict implementations to a specific bool conversion,
the Throws wording should include it in a manner that applies to any form.
Besides the obviously necessary conversion to bool we should not restrict
implementations in regard to the potential usage of other boolean-testable
operations, e.g. negation, which could potentially also throw an exception.
Therefore the wording for the affected Throws: elements somehow needs to mention
boolean-testable operations to cover the effective operation that is
involved here without actually restricting existing implementations unnecessarily.
Previous resolution [SUPERSEDED]:
This wording is relative to N4986.
Modify 23.2.7.1 [associative.reqmts.general] as indicated:
a.merge(a2)[…]
-116- Throws: Nothing unless the evaluation of the comparison object including every selectedboolean-testable(18.5.2 [concept.booleantestable]) operation throws.Modify 23.3.7.6 [forward.list.ops] as indicated:
size_type remove(const T& value); template<class Predicate> size_type remove_if(Predicate pred);[…]
-15- Throws: Nothing unless an exception is thrown by the equality comparison or the predicate evaluation including every selectedboolean-testable(18.5.2 [concept.booleantestable]) operation.[…]
size_type unique(); template<class BinaryPredicate> size_type unique(BinaryPredicate binary_pred);[…]
-22- Throws: Nothing unless an exception is thrown by the predicate evaluation including every selectedboolean-testable(18.5.2 [concept.booleantestable]) operation.Modify 23.3.11.5 [list.ops] as indicated:
[Drafting note: The adjustment of p17 is presented as if issue LWG 4127(i) proposed wording has already been applied]
size_type remove(const T& value); template<class Predicate> size_type remove_if(Predicate pred);[…]
-17- Throws: Nothing unless an exception is thrown by*i == valueorpred(*i)including every selectedboolean-testable(18.5.2 [concept.booleantestable]) operation. […]size_type unique(); template<class BinaryPredicate> size_type unique(BinaryPredicate binary_pred);[…]
-24- Throws: Nothing unless an exception is thrown by the predicate evaluation including every selectedboolean-testable(18.5.2 [concept.booleantestable]) operation. […]
[2024-09-07; Daniel comments and improves wording]
During LWG reflector discussion it has been proposed to add similar wording to the
throwing prohibition in Cpp17NullablePointer and nothrow-sentinel-for, too.
Furthermore, the existing wording changes are becoming now more harmonized in their
stylistic forms.
[2024-10-02; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4988.
Modify 16.4.4.4 [nullablepointer.requirements] as indicated:
-4- No operation which is part of the Cpp17NullablePointer requirements, including each of its
boolean-testable(18.5.2 [concept.booleantestable]) operations, shall exit via an exception.
Modify 23.2.7.1 [associative.reqmts.general] as indicated:
a.merge(a2)[…]
-116- Throws: Nothing unless an exception is thrown by evaluation of the comparison object, including any selectedboolean-testable(18.5.2 [concept.booleantestable]) operationsthrows.
Modify 23.3.7.6 [forward.list.ops] as indicated:
size_type remove(const T& value); template<class Predicate> size_type remove_if(Predicate pred);[…]
-15- Throws: Nothing unless an exception is thrown by the equality comparison (forremove()) or the predicate (forremove_if()), including any selectedboolean-testable(18.5.2 [concept.booleantestable]) operations.[…]
size_type unique(); template<class BinaryPredicate> size_type unique(BinaryPredicate binary_pred);[…]
-22- Throws: Nothing unless an exception is thrown by evaluation of the predicate, including any selectedboolean-testable(18.5.2 [concept.booleantestable]) operations.
Modify 23.3.11.5 [list.ops] as indicated:
[Drafting note: The adjustment of p17 is presented as if issue LWG 4127(i) proposed wording has already been applied]
size_type remove(const T& value); template<class Predicate> size_type remove_if(Predicate pred);[…]
-17- Throws: Nothing unless an exception is thrown bythe equality comparison (for*i == valueorpred(*i)remove()) or evaluation of the predicate (forremove_if()), including any selectedboolean-testable(18.5.2 [concept.booleantestable]) operations. […]size_type unique(); template<class BinaryPredicate> size_type unique(BinaryPredicate binary_pred);[…]
-24- Throws: Nothing unless an exception is thrown by evaluation of the predicate, including any selectedboolean-testable(18.5.2 [concept.booleantestable]) operations. […]
Modify 26.11.2 [special.mem.concepts] as indicated:
template<class S, class I> concept nothrow-sentinel-for = sentinel_for<S, I>; // exposition only-4- Types
-5- [Note 2: This concept allows someSandImodelnothrow-sentinel-foronly if no exceptions are thrown from copy construction, move construction, copy assignment, move assignment, or comparisons , including each of itsboolean-testable(18.5.2 [concept.booleantestable]) operations, between valid values of typeIandS.sentinel_for(24.3.4.7 [iterator.concept.sentinel]) operations to throw exceptions. — end note]
awaitable-receiver's members are potentially throwingSection: 33.13.1 [exec.as.awaitable] Status: New Submitter: Eric Niebler Opened: 2024-07-30 Last modified: 2024-08-21
Priority: 1
View other active issues in [exec.as.awaitable].
View all other issues in [exec.as.awaitable].
View all issues with New status.
Discussion:
The specification of awaitable-receiver in 33.13.1 [exec.as.awaitable]/p4
as of N4988 is not taking into consideration the fact that the resume()
and promise() member functions on coroutine_handle<P> are not marked noexcept.
awaitable-receiver's member functions must all be noexcept, but they are
specified as being "equivalent to" statement that call resume() and promise()
outside of try/catch blocks.
[2024-08-21; Reflector poll]
Set priority to 1 after reflector poll.
promise() can probably be Throws: Nothing
(along with a bunch of other coroutine_handle members),
but resume() certainly can throw.
Also AS-EXCEPT-PTR can throw for the error_code case
(that might be worth a separate issue though).
Proposed resolution:
is_within_lifetime should mandate is_objectSection: 21.3.12 [meta.const.eval] Status: New Submitter: Corentin Opened: 2024-08-09 Last modified: 2025-10-14
Priority: 3
View all issues with New status.
Discussion:
int f(); std::is_within_lifetime<int()>(f);
This is currently well-formed, and only fails when evaluated because
is_within_lifetime is not constrained. However talking of lifetime
of a non-object does not make sense, and the lack of constraint makes the
implementation and use of that function and underlying built-in more
convoluted than necessary.
[2025-10-14; Reflector poll]
Set priority to 3 after reflector poll.
"What about void*, should that be allowed? The compiler should know which
union member the void* came from, as this is only using during constant
evaluation."
"NAD, a function could have its lifetime constrained by a dynamic loader, so it seems wrong to assume that taling of function lifetimes makes no sense."
Proposed resolution:
This wording is relative to N4988.
Modify 21.3.3 [meta.type.synop], header <type_traits> synopsis, as indicated:
[…] // 21.3.12 [meta.const.eval], constant evaluation context constexpr bool is_constant_evaluated() noexcept; template<class T> consteval bool is_within_lifetime(const Tauto*) noexcept;
Modify 21.3.12 [meta.const.eval] as indicated:
template<class T> consteval bool is_within_lifetime(const Tauto* p) noexcept;-?- Mandates:
-3- Returns:is_object_v<T>istrue.trueifpis a pointer to an object that is within its lifetime (6.8.4 [basic.life]); otherwise,false. -4- Remarks: During the evaluation of an expressionEas a core constant expression, a call to this function is ill-formed unlessppoints to an object that is usable in constant expressions or whose complete object's lifetime began withinE.
<=>Section: 30.11.8 [time.zone.leap] Status: New Submitter: Corentin Opened: 2024-08-18 Last modified: 2024-08-21
Priority: 3
View all other issues in [time.zone.leap].
View all issues with New status.
Discussion:
Consider
decltype(std::declval<std::chrono::leap_second&>() <=> std::chrono::system_clock::now())
There is a <=> operator for leap second defined as
template<class Duration> requires three_way_comparable_with<sys_seconds, sys_time<Duration>> constexpr auto operator<=>(const leap_second& x, const sys_time<Duration>& y) noexcept;
In order to resolve this overload, we need to check the constraints.
three_way_comparable_with will end up checking that sys_seconds{} < sys_time<Duration>{}
is a valid expression.
To do that, we run overload resolution, find a bunch of operator<=>, including the leap_second
overload mentioned above.
We check its constraints... and we find ourselves doing that recursively.
leap_seconds comparisons operators are all hidden
friends (in fact, libstdc++ define hidden friends comparison operators for most of the objects in chrono, which is
nice!)
Suggested resolution:
Specify that leap_seconds operators are hidden friends. This would avoid the recursion, and would be easier
on compilers.
[2024-08-21; Reflector poll]
Set priority to 3 after reflector poll.
Support for changing all relational ops for calendar types to hidden friends,
but only doing it for leap_second would be in scope for this issue.
Proposed resolution:
execution::set_value/set_error/set_stopped/start should always return voidSection: 33.7.2 [exec.set.value], 33.7.3 [exec.set.error], 33.7.4 [exec.set.stopped], 33.8.2 [exec.opstate.start] Status: New Submitter: Jiang An Opened: 2024-08-20 Last modified: 2024-09-18
Priority: 2
View all issues with New status.
Discussion:
In editorial issue #7222,
it was observed that currently execution::start may have a non-void
return value, which possibly interacts with overloaded operator,. But the
return value of execution::start doesn't seem used anywhere.
execution::start, the return values of execution::set_value,
execution::set_error, and execution::set_stopped also seem never used,
and the return type of these CPOs are always void in
stdexec. Perhaps it would be better to
specified in the standard that these CPOs always return void.
[2024-09-18; Reflector poll]
Set priority to 2 after reflector poll.
Should require the expressions to have type void,
rather than just discarding anything that is returned.
Proposed resolution:
This wording is relative to N4988.
Modify 33.7.2 [exec.set.value] as indicated:
-1-
set_valueis a value completion function (33.3 [exec.async.ops]). Its associated completion tag isset_value_t. The expressionset_value(rcvr, vs...)for a subexpressionrcvrand pack of subexpressionsvsis ill-formed ifrcvris an lvalue or an rvalue of const type. Otherwise, it is expression-equivalent toMANDATE-NOTHROW(void(rcvr.set_value(vs...))).
Modify 33.7.3 [exec.set.error] as indicated:
-1-
set_erroris an error completion function (33.3 [exec.async.ops]). Its associated completion tag isset_error_t. The expressionset_error(rcvr, err)for some subexpressionsrcvranderris ill-formed ifrcvris an lvalue or an rvalue of const type. Otherwise, it is expression-equivalent toMANDATE-NOTHROW(void(rcvr.set_error(err))).
Modify 33.7.4 [exec.set.stopped] as indicated:
-1-
set_stoppedis a stopped completion function (33.3 [exec.async.ops]). Its associated completion tag isset_stopped_t. The expressionset_stopped(rcvr)for a subexpressionrcvris ill-formed ifrcvris an lvalue or an rvalue of const type. Otherwise, it is expression-equivalent toMANDATE-NOTHROW(void(rcvr.set_stopped())).
Modify 33.8.2 [exec.opstate.start] as indicated:
-1- The name
startdenotes a customization point object that starts (33.3 [exec.async.ops]) the asynchronous operation associated with the operation state object. For a subexpressionop, the expressionstart(op)is ill-formed ifopis an rvalue. Otherwise, it is expression-equivalent toMANDATE-NOTHROW(void(op.start())).
Section: 16.4.6.10 [res.on.data.races] Status: New Submitter: Tim Song Opened: 2024-08-22 Last modified: 2024-10-02
Priority: 3
View other active issues in [res.on.data.races].
View all other issues in [res.on.data.races].
View all issues with New status.
Discussion:
It's unclear how 16.4.6.10 [res.on.data.races] applies to templated functions that invoke user-supplied operations. In particular, there's no way to meet those requirements when a user-supplied operation can perform arbitrary operations.
Perhaps we need to exclude the invocation of such operations from the scope of the requirements.[2024-10-02; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 28.5.6.4 [format.formatter.spec], 23.6.13 [container.adaptors.format] Status: New Submitter: Casey Carter Opened: 2024-08-31 Last modified: 2025-10-14
Priority: 2
View other active issues in [format.formatter.spec].
View all other issues in [format.formatter.spec].
View all issues with New status.
Discussion:
28.5.6.4 [format.formatter.spec]/3 says that the library provides a specialization of
enable_nonlocking_formatter_optimization with value true corresponding to each library-provided
specialization of formatter, unless otherwise specified. Although it actually states
"for each type T", the intent is that partial specializations are also provided corresponding to
library-provided partial specializations of formatter.
formatter for each of the container adaptor templates priority_queue, queue, and stack.
Together with 28.5.6.4 [format.formatter.spec]/3, that means that e.g.
enable_nonlocking_formatter_optimization<stack<T>> == true. Formatting a stack of
that type will enable the nonlocking optimization even if
enable_nonlocking_formatter_optimization<T> == false. To avoid this, the author of T
must partially specialize enable_nonlocking_formatter_optimization to false for all container
adaptors when they adapt a container of T.
It is clearly not the design intent that programmers must explicitly opt out of the nonlocking
optimization, so this is a defect that LWG should correct. Since P3235R3 was applied
as a Defect Report to C++23, the resolution of this issue should be so applied as well.
Suggested Resolution:
LEWG was reticent to apply the optimization to general ranges — ostensibly due to the possibility
of deadlock in program-defined iterator operations — but apparently unconcerned about the iterators
of program-defined containers nor the fancy pointers of program-defined allocators. It seems consistent
with that design to ignore the "container" part of "container adaptors" and only pay attention to the
elements that are going to be formatted. (I have prototyped this resolution on MSVCSTL, albeit slightly
modified since neither MSVC nor Clang like this partial specialization form.)
[2025-10-14; Reflector poll]
Set priority to 2 after reflector poll.
This is a duplicate of LWG 4398(i), with a different proposed resolution.
Proposed resolution:
This wording is relative to N4988.
Modify 23.6.13 [container.adaptors.format] as indicated:
-1- For each of
queue,priority_queue, andstack, the library provides the followingformatterspecializations whereadaptor-typeis the name of the template:namespace std { template<class charT, class T, formattable<charT> Container, class... U> struct formatter<adaptor-type<T, Container, U...>, charT> { […] }; template<class T, class Container, class... U> constexpr bool enable_nonlocking_formatter_optimization<adaptor-type<T, Container, U...>> = enable_nonlocking_formatter_optimization<T>; }
inplace_vector::swapSection: 23.3.16.5 [inplace.vector.modifiers] Status: New Submitter: Arthur O'Dwyer Opened: 2024-09-07 Last modified: 2024-09-18
Priority: 2
View all issues with New status.
Discussion:
Right now inplace_vector::swap has only a declaration in the class synopsis; it doesn't
specify what the behavior of swap for inplace_vectors actually is. So I think
the behavior ends up being governed by 23.2.2.2 [container.reqmts], which are written from
the point of view of a container that manages an external heap allocation so that swapping
containers doesn't touch the elements at all, just changes their ownership.
inplace_vector::swap actually works more like array::swap, which has its own
specification in 23.3.3.3 [array.members], where it is defined in terms of std::swap_ranges.
The std::swap_ranges algorithm (26.7.3 [alg.swap]) has a precondition! This
precondition is missing from inplace_vector::swap.
That is, I think we currently have no wording that explains why
std::pmr::monotonic_buffer_resource mr1; std::inplace_vector<std::pmr::vector<int>, 2> v1, v2; v1.emplace_back(1, &mr1); v2.emplace_back(1); v1.swap(v2);
is undefined behavior. The current spec seems to say this Just Works, even though it physically
cannot work because v1[0] and v2[0] don't dynamically meet the semantic requirements
of being "swappable with" each other, and v1.swap(v2) is necessarily going to try to swap them.
[2024-09-18; Reflector poll]
Set priority to 2 after reflector poll. The Preconditions: (but not the Effects:) would be changed again if P3160R2 is approved.
Proposed resolution:
This wording is relative to N4988.
Modify 23.3.16.5 [inplace.vector.modifiers] as indicated:
[Drafting note: It is suggested to add the new wording to the end of the existing subclause]
constexpr void swap(inplace_vector& x) noexcept(N == 0 || (is_nothrow_swappable_v<T> && is_nothrow_move_constructible_v<T>));-?- Preconditions: Let
-?- Effects: Exchanges the contents ofMbemin(size(), x.size()). For each non-negative integern < M,(*this)[n]is swappable with (16.4.4.3 [swappable.requirements])x[n].*thisandx.
std::char_traits is totally underspecifiedSection: 27.2.2 [char.traits.require] Status: New Submitter: Jiang An Opened: 2024-09-08 Last modified: 2025-02-07
Priority: 4
View other active issues in [char.traits.require].
View all other issues in [char.traits.require].
View all issues with New status.
Discussion:
Currently, only explicit specializations of std::char_traits are specified in the standard.
Nothing is specified for the primary template except that it's not marked "not defined".
[2025-02-07; Reflector poll]
Set priority to 4 after reflector poll.
27.2.1 [char.traits.general]/1 says that the subclause
"defines a class template char_traits<charT>",
but we never provided a definition.
"Libc++ started to warn that the primary template is deprecated and will be removed. The proposed resolution doesn't improve matters though."
"The definition should be cross-platform. Unspecified doesn't help."
Proposed resolution:
This wording is relative to N4988.
Modify 27.2.2 [char.traits.require] as indicated:
-2- The class template
template<class charT> struct char_traits;is provided in the header
<string>as a basis for explicit and partial specializations. The effect of instantiating the primary template ofchar_traitsis unspecified and possibly makes the program ill-formed.
Section: 16.4.4.4 [nullablepointer.requirements] Status: New Submitter: Jiang An Opened: 2024-09-18 Last modified: 2024-10-02
Priority: 3
View all other issues in [nullablepointer.requirements].
View all issues with New status.
Discussion:
Currently, 16.4.4.4 [nullablepointer.requirements]/3 requires
"An object p of type P can be contextually converted to bool",
while the core language wording (7.3.1 [conv.general]/4) only says
some expressions can be contextually converted to bool.
Moreover, the value category and cv-qualification are not mentioned,
presumably both non-const and const P,
together with all value categories are expected.
[2024-10-02; Reflector poll]
Set priority to 3 after reflector poll.
"This is an improvement, but judging from microsoft/STL 4964, I think we actually want to define a "contextually-boolean-testable" for this."
Proposed resolution:
This wording is relative to N4988.
Modify 16.4.4.4 [nullablepointer.requirements] as indicated:
-3- An objectpof typePcan be contextually converted tobool. The effect shall be as ifp != nullptrhad been evaluated in place ofp.
Add a row to the bottom of [tab:cpp17.nullablepointer]:
Expression Return type Operational semantics ... ... ... a ? true : falseboola != np
error_category messages have unspecified encodingSection: 19.5.3.2 [syserr.errcat.virtuals] Status: SG16 Submitter: Victor Zverovich Opened: 2024-09-18 Last modified: 2025-03-12
Priority: 3
View all issues with SG16 status.
Discussion:
19.5.3.1 [syserr.errcat.overview] says:
The class error_category serves as a base class for types used to identify
the source and encoding of a particular category of error code.
However, this doesn't seem to be referring to a character encoding,
just something about how an error is encoded into an integer value.
The definition of error_category::message
(19.5.3.2 [syserr.errcat.virtuals] p5) just says:
This says nothing about character encoding either.virtual string message(int ev) const = 0;Returns: A string that describes the error condition denoted by
ev.
There is also implementation divergence:
some implementations use variants of strerror which return messages
in the current C locale encoding,
but at least one major implementation doesn't use the current C locale:
MSVC STL issue 4711.
Using the current C locale is obviously problematic. First, it is inconsistent with other C++ APIs that normally use C++ locales. Second, because it is a global state, it may change (possibly from another thread) between the time the message is obtained and the time it needs to be consumed, which may lead to mojibake. At the very least there should be a mechanism that captures the encoding information in a race-free manner and communicates it to the caller if the locale encoding is used although it is better not to use it in the first place.
This is somewhat related to LWG 4087(i) but should probably be addressed first because it may affect how some exceptions are defined.
The proposed resolution is similar to the one of LWG 4087(i).
[2024-09-18; Jonathan comments]
It might make sense to stop using the word "encoding" in 19.5.3.1 [syserr.errcat.overview].
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
"Do we need to say something about name() too? Does this requirement apply
to user overrides? If it does, what's the consequence of a violation? UB?
This 'encoding for strings returned by the library' questions feels like it
should be comprehensively addressed in a paper rather than as a patchwork of
individual issues."
[2025-03-12; update from SG16]
Would be resolved by P3395R1.
Proposed resolution:
This wording is relative to N4988.
Modify 19.5.3.2 [syserr.errcat.virtuals] as indicated:
virtual string message(int ev) const = 0;-5- Returns: A string in the ordinary literal encoding that describes the error condition denoted by
ev.
packaged_task::operator= should abandon its shared stateSection: 32.10.10.2 [futures.task.members] Status: New Submitter: Jonathan Wakely Opened: 2024-09-19 Last modified: 2024-10-03
Priority: 3
View all other issues in [futures.task.members].
View all issues with New status.
Discussion:
The packaged_task move assignment operator is specified to release
the previous shared state. This means it releases ownership, but does
not make the shared state ready. Any future that shares ownership of the
shared state will never receive a result, because the provider is gone.
This means that any thread that waits on the future will block forever.
There is a note on packaged_task::reset() which claims that assignment
abandons the state, which is not supported by any normative wording:
void reset();-26- Effects: As if
*this = packaged_task(std::move(f)), wherefis the task stored in*this.[Note 2: This constructs a new shared state for
*this. The old state is abandoned (32.10.5 [futures.state]). — end note]
Presumably, the intended behaviour of assignment was to abandon the
shared state,
i.e. make it ready with a broken_promise error, and then release it.
That is what the std::promise move assignment does
(see 32.10.6 [futures.promise] p9).
Both libstdc++ and libc++ abandon the state, despite what the standard says.
[2024-10-02; Reflector poll]
Set priority to 3 after reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N4988.
Modify 32.10.10.2 [futures.task.members] as indicated:
packaged_task& operator=(packaged_task&& rhs) noexcept;-11- Effects:
- (11.1) —
ReleasesAbandons any shared state (32.10.5 [futures.state]);- (11.2) — calls
packaged_task(std::move(rhs)).swap(*this).-?- Returns:
*this.
[2024-10-02; Jonathan provides improved wording]
Following reflector discussion, remove the "Releases any shared state" text completely. The remaining effects imply that the state will be abandoned anyway. This makes it safe against self-assignment.
[2024-10-02; LWG telecon]
Agreed to change promise the same way, which is safe for self-assignment
and matches what all three of libstdc++, libc++ and MSVC do today.
Ask SG1 to review.
Proposed resolution:
This wording is relative to N4988.
Modify 32.10.6 [futures.promise] as indicated:
promise& operator=(promise&& rhs) noexcept;-9- Effects:
Abandons any shared state (32.10.5 [futures.state]) and then as ifEquivalent topromise(std::move(rhs)).swap(*this).-10- Returns:
*this.
Modify 32.10.10.2 [futures.task.members] as indicated:
packaged_task& operator=(packaged_task&& rhs) noexcept;-11- Effects:
(11.1) — Releases any shared state (32.10.5 [futures.state]);(11.2) — callsEquivalent topackaged_task(std::move(rhs)).swap(*this).-?- Returns:
*this.
Section: 23.2.5 [container.node], 20.2.8.1 [allocator.uses.trait] Status: New Submitter: Jiang An Opened: 2024-09-21 Last modified: 2024-10-09
Priority: 3
View all issues with New status.
Discussion:
Currently, node handle types (23.2.5 [container.node]) have the member
allocator_type type alias, which makes std::uses_allocator report
true for them. However, they don't have allocator-extended constructors,
and it's unclear to me whether it's better to make them have such constructors.
#include <set>
#include <map>
int main() {
std::pmr::set<int> s{1};
std::pmr::map<int, decltype(s)::node_type> m;
m.emplace(1, s.extract(1));
}
Perhaps we should consistently treat node handles not uses-allocator-constructible.
[2024-10-09; Reflector poll]
Set priority to 3 after reflector poll.
"Strike the carve outs for program-defined specializations."
If it's broken for uses_allocator<NH, A> to be true
when NH is not a program-defined type, then we don't want to allow users
to make it true for a program-defined A, that would still be broken.
Previous resolution [SUPERSEDED]:
This wording is relative to N4988.
Modify 23.2.5.1 [container.node.overview] as indicated:
[…]
-4- If a user-defined specialization ofpairexists forpair<const Key, T>orpair<Key, T>, whereKeyis the container'skey_typeandTis the container'smapped_type, the behavior of operations involving node handles is undefined. -?- For each node handle typeNHand any typeA, ifuses_allocator<NH, A>is not a program-defined specialization, it meets the Cpp17BinaryTypeTrait requirements (21.3.2 [meta.rqmts]) and its base characteristic isfalse_type.
[2024-10-09; Adjust wording as requested in reflector poll]
Proposed resolution:
This wording is relative to N4988.
Modify 23.2.5.1 [container.node.overview] as indicated:
[…]
-4- If a user-defined specialization ofpairexists forpair<const Key, T>orpair<Key, T>, whereKeyis the container'skey_typeandTis the container'smapped_type, the behavior of operations involving node handles is undefined. -?- For each node handle typeNHand any typeA,uses_allocator<NH, A>meets the Cpp17BinaryTypeTrait requirements (21.3.2 [meta.rqmts]) and its base characteristic isfalse_type.
packaged_task should reject rvalue reference return typesSection: 32.10.10.1 [futures.task.general] Status: New Submitter: Casey Carter Opened: 2024-09-28 Last modified: 2025-02-07
Priority: 3
View all issues with New status.
Discussion:
promise, future, and shared_future all refuse rvalue reference types as template arguments
(e.g., 32.10.6 [futures.promise] paragraphs 1 and 2), but packaged_task<meow&&()>
violates no requirements. Its member get_future returns future<meow&&>,
which is ill-formed, but the other member functions appear to be callable. Nonetheless, at least MSVCSTL,
libc++, and libstdc++ all fail to compile simple uses of packaged_task with a function type that has
an rvalue reference return type (see https://www.godbolt.org/z/5E18nn896).
packaged_task
should be ill-formed when get_future is not instantiable. The spec should say so explicitly rather than
relying on the fact that one of the basis operations is unusable.
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
"Shouldn't it be ill-formed only if instantiated?"
Previous resolution [SUPERSEDED]:
This wording is relative to N4988.
Modify 32.10.10.1 [futures.task.general] as indicated:
[…]
-2- When thepackaged_taskobject is invoked, its stored task is invoked and the result (whether normal or exceptional) stored in the shared state. Any futures that share the shared state will then be able to access the stored result.namespace std { template<class> class packaged_task; // not defined template<class R, class... ArgTypes> class packaged_task<R(ArgTypes...)> { […] }; template<class R, class... ArgTypes> packaged_task(R (*)(ArgTypes...)) -> packaged_task<R(ArgTypes...)>; template<class F> packaged_task(F) -> packaged_task<see below>; }-?- The program is ill-formed if
Ris an rvalue reference type.
[2025-02-07; Jonathan provides improved wording]
Proposed resolution:
This wording is relative to N5001.
Modify 32.10.10.1 [futures.task.general] as indicated:
[…]
-2- When thepackaged_taskobject is invoked, its stored task is invoked and the result (whether normal or exceptional) stored in the shared state. Any futures that share the shared state will then be able to access the stored result.namespace std { template<class> class packaged_task; // not defined template<class R, class... ArgTypes> class packaged_task<R(ArgTypes...)> { […] }; template<class R, class... ArgTypes> packaged_task(R (*)(ArgTypes...)) -> packaged_task<R(ArgTypes...)>; template<class F> packaged_task(F) -> packaged_task<see below>; }-?- If the definition of a specialization
packaged_task<R(Args...)>is instantiated for an rvalue reference typeR, the program is ill-formed.
std::complex<NonFloatingPoint>Section: 29.4 [complex.numbers] Status: New Submitter: Jiang An Opened: 2024-09-29 Last modified: 2025-02-07
Priority: 3
View all other issues in [complex.numbers].
View all issues with New status.
Discussion:
std::complex<NonFloatingPoint> is possibly a program-defined specialization and thus
instantiation of such a specialization can have determined effect. However, it's improbable for implementations
to ensure that some free functions, e.g. abs, sin, exp, work for such a specialization.
std::complex specializations tuple-like types, despite that
std::get overloads need to touch implementation details and thus don't work for program-defined specializations.
It seems better to make only complex<floating-point> tuple-like types, and add Constraints
to some free functions that can't be guaranteed to work to only accept cases where T is a cv-unqualified
floating-point type. However, it's unclear to me how to handle cases where the implementation intentionally supports
complex<X> (where X is not a program-defined type) as extension, and whether Mandates
should be used.
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
"Program-defined specializations of std::complex
(i.e. explicit specializations and program-defined partial specializations)
seem unsupportably broken. We should say so explicitly until somebody writes
a paper to fix them."
Proposed resolution:
nth_element is underspecifiedSection: 26.8.3 [alg.nth.element] Status: New Submitter: Jiang An Opened: 2024-09-29 Last modified: 2024-10-09
Priority: 3
View all other issues in [alg.nth.element].
View all issues with New status.
Discussion:
Currently, 26.8.3 [alg.nth.element] doesn't specify the worst time complexity for nth_element
without ExecutionPolicy parameter, which seemingly allows a complexity that is
𝒪(N2) or even worse. Presumably we should make the worst time complexity
consistent between parallel and non-parallel versions. For sort, LWG 713(i) already
strengthened complexity requirements.
[2024-10-09; Reflector poll]
Set priority to 3 after reflector poll.
"This seems to require changes to implementations for them to meet this complexity guarantee."
Proposed resolution:
This wording is relative to N4988.
Modify 26.8.3 [alg.nth.element] as indicated:
template<class RandomAccessIterator> constexpr void nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last); template<class ExecutionPolicy, class RandomAccessIterator> void nth_element(ExecutionPolicy&& exec, RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last); template<class RandomAccessIterator, class Compare> constexpr void nth_element(RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last, Compare comp); template<class ExecutionPolicy, class RandomAccessIterator, class Compare> void nth_element(ExecutionPolicy&& exec, RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last, Compare comp); template<random_access_iterator I, sentinel_for<I> S, class Comp = ranges::less, class Proj = identity> requires sortable<I, Comp, Proj> constexpr I ranges::nth_element(I first, I nth, S last, Comp comp = {}, Proj proj = {});[…]
-5- Complexity:For the overloads with noExecutionPolicy, linear on average. For the overloads with anExecutionPolicy,𝒪(N)applications of the predicate,and𝒪(N log N)swaps, whereN = last - first. For the overloads with noExecutionPolicy,𝒪(N)on average.
std::array with itself result in UB?Section: 18.4.9 [concept.swappable], 22.2.2 [utility.swap], 23.3.3.3 [array.members] Status: New Submitter: Jiang An Opened: 2024-10-13 Last modified: 2025-06-13
Priority: 3
View other active issues in [concept.swappable].
View all other issues in [concept.swappable].
View all issues with New status.
Discussion:
Currently, the std::swap overload for built-in arrays, the swap member function of
std::array, the std::swap overload for std::array, and the expression-equivalent-to
operation of std::ranges::swap for built-in arrays are specified to be equivalent to performing
std::(ranges::)swap_ranges. As swap_ranges functions require that the swapped ranges shall
not overlap (26.7.3 [alg.swap]/2), swapping a built-in array or a nonzero-length
std::array with itself results in undefined behavior.
swap or ranges::swap on
elements.
[2025-06-13; Reflector poll]
Set priority to 3 after reflector poll.
Instead of a loop I'd prefer something like:
Effects: Ifaddressof(a) == addressof(b), no effects. Otherwise, equivalent toswap_ranges(begin(a), end(a), begin(b)).
Proposed resolution:
min,
max, and minmax is unclearSection: 26.8.9 [alg.min.max] Status: New Submitter: Casey Carter Opened: 2024-10-20 Last modified: 2025-06-13
Priority: 3
View other active issues in [alg.min.max].
View all other issues in [alg.min.max].
View all issues with New status.
Discussion:
Editorial issue #6747
finds it inconsistent that
the wording for the max, min, and minmax algorithms
uses "larger" and "smaller"
- terms normally applied to physical quantities -
to refer to relationships between values
which we term "greater" and "lesser"
everywhere else in the Working Paper.
Using "greater" and "lesser" would make it no less (pun intended)
of a problem that we describe the ordering imposed by
an arbitrary binary predicate as if it is a less-than ordering.
For example, 26.8.9 [alg.min.max] para 2 says that
std::ranges::min(13, 42, std::greater{})
"Returns: The smaller value.
Returns the first argument when
the arguments are equivalent."
The smaller of 13 and 42 is 13, which is not what this call yields.
The reader is supposed to somehow know that "The smaller value" actually means
"the value we'd call the lesser if the arguments were numbers and
comp described a less-then ordering."
It would be clearer and more concise to simply say
it returns b if
invoke(comp, invoke(proj, b), invoke(proj, a)) yields true
and a otherwise.
[2025-06-13; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N4993.
Modify 26.8.9 [alg.min.max] as indicated:
template<class T> constexpr const T& min(const T& a, const T& b); template<class T, class Compare> constexpr const T& min(const T& a, const T& b, Compare comp); template<class T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr const T& ranges::min(const T& a, const T& b, Comp comp = {}, Proj proj = {});
-?- Let comp be less{} and
proj be identity{}
for the overloads with no parameters by those names.
-1- Preconditions:
For the first form, T meets the
Cpp17LessThanComparable requirements
(Table [tab:cpp17.lessthancomparable]).
-2- Returns: The smaller value.
Returns the first argument
when the arguments are equivalent.
Effects: Equivalent to:
return invoke(comp, invoke(proj, b), invoke(proj, a)) ? b : a;
-3- Complexity: Exactly one comparison
and two applications of the projection, if any.
-4- Remarks: An invocation may explicitly specify
an argument for the template parameter T
of the overloads in namespace std.
template<class T> constexpr T min(initializer_list<T> r); template<class T, class Compare> constexpr T min(initializer_list<T> r, Compare comp); template<copyable T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr T ranges::min(initializer_list<T> r, Comp comp = {}, Proj proj = {}); template<input_range R, class Proj = identity, indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less> requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*> constexpr range_value_t<R> ranges::min(R&& r, Comp comp = {}, Proj proj = {});
-?- Let comp be less{} and proj be
identity{} for the overloads with no parameters by those names.
-5- Preconditions: ranges::distance(r) > 0.
For the overloads in namespace std, T meets the
Cpp17CopyConstructible requirements
(Table [tab:cpp17.copyconstructible]).
For the first form, T meets the
Cpp17LessThanComparable requirements
(Table [tab:cpp17.lessthancomparable]).
-6- Returns: The smallest value in the input range.
Returns a copy of the leftmost element
when several elements are equivalent to the smallest.
A copy of the leftmost element e
in the input range r for which
bool(invoke(comp, invoke(proj, x), invoke(proj, e)))
is false for all elements x in r.
-7- Complexity: Exactly ranges::distance(r) - 1 comparisons and
twice as many applications of the projection, if any.
-8- Remarks: An invocation may explicitly specify an argument
for the template parameter T
of the overloads in namespace std.
template<class T> constexpr const T& max(const T& a, const T& b); template<class T, class Compare> constexpr const T& max(const T& a, const T& b, Compare comp); template<class T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr const T& ranges::max(const T& a, const T& b, Comp comp = {}, Proj proj = {});
-?- Let comp be less{}
and proj be identity{}
for the overloads with no parameters by those names.
-9- Preconditions: For the first form,
T meets the Cpp17LessThanComparable requirements
(Table [tab:cpp17.lessthancomparable]).
-10- Returns: The larger value.
Returns the first argument when the arguments are equivalent.
Effects: Equivalent to:
return invoke(comp, invoke(proj, a), invoke(proj, b)) ? b : a;
-11- Complexity: Exactly one comparison
and two applications of the projection, if any.
-12- Remarks: An invocation may explicitly specify an argument
for the template parameter T
of the overloads in namespace std.
template<class T> constexpr T max(initializer_list<T> r); template<class T, class Compare> constexpr T max(initializer_list<T> r, Compare comp); template<copyable T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr T ranges::max(initializer_list<T> r, Comp comp = {}, Proj proj = {}); template<input_range R, class Proj = identity, indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less> requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*> constexpr range_value_t<R> ranges::max(R&& r, Comp comp = {}, Proj proj = {});
-?- Let comp be less{}
and proj be identity{}
for the overloads with no parameters by those names.
-13- Preconditions: ranges::distance(r) > 0.
For the overloads in namespace std,
T meets the Cpp17CopyConstructible requirements
(Table [tab:cpp17.copyconstructible]).
For the first form, T meets
the Cpp17LessThanComparable requirements
(Table [tab:cpp17.lessthancomparable]).
-14- Returns: The largest value in the input range.
Returns a copy of the leftmost element
when several elements are equivalent to the largest.
Returns a copy of the leftmost element e
in the input range r for which
bool(invoke(comp, invoke(proj, e), invoke(proj, x)))
is false for all elements x in r.
-15- Complexity: Exactly ranges::distance(r) - 1 comparisons
and twice as many applications of the projection, if any.
-16- Remarks: An invocation may explicitly specify an argument
for the template parameter T
of the overloads in namespace std.
template<class T> constexpr pair<const T&, const T&> minmax(const T& a, const T& b); template<class T, class Compare> constexpr pair<const T&, const T&> minmax(const T& a, const T& b, Compare comp); template<class T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr ranges::minmax_result<const T&> ranges::minmax(const T& a, const T& b, Comp comp = {}, Proj proj = {});
-?- Let comp be less{}
and proj be identity{}
for the overloads with no parameters by those names.
-17- Preconditions: For the first form,
T meets the Cpp17LessThanComparable requirements
(Table [tab:cpp17.lessthancomparable]).
-18- Returns: {b, a}
if b is smaller than abool(invoke(comp, invoke(proj, b), invoke(proj, a))) is true
,
and {a, b} otherwise.
-19- Complexity: Exactly one comparison
and two applications of the projection, if any.
-20- Remarks: An invocation may explicitly specify an argument
for the template parameter T
of the overloads in namespace std.
template<class T> constexpr pair<T, T> minmax(initializer_list<T> t); template<class T, class Compare> constexpr pair<T, T> minmax(initializer_list<T> t, Compare comp); template<copyable T, class Proj = identity, indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less> constexpr ranges::minmax_result<T> ranges::minmax(initializer_list<T> r, Comp comp = {}, Proj proj = {}); template<input_range R, class Proj = identity, indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less> requires indirectly_copyable_storable<iterator_t<R>, range_value_t<R>*> constexpr ranges::minmax_result<range_value_t<R>> ranges::minmax(R&& r, Comp comp = {}, Proj proj = {});
-?- Let comp be less{}
and proj be identity{}
for the overloads with no parameters by those names.
-21- Preconditions: ranges::distance(r) > 0.
For the overloads in namespace std,
T meets the Cpp17CopyConstructible requirements
(Table [tab:cpp17.copyconstructible]).
For the first form, T meets
the Cpp17LessThanComparable requirements
(Table [tab:cpp17.lessthancomparable]).
-22- Returns: Let X be the return type.
Returns X{x, y},
where x is a copy of the leftmost element
with the smallest value
in the input range r for which
bool(invoke(comp, invoke(proj, e), invoke(proj, x)))
is false for all elements e in r,
and y is a copy of the rightmost element
with the largest value in the input range
in r for which
bool(invoke(comp, invoke(proj, y), invoke(proj, e)))
is false for all elements e in r
.
-23- Complexity: At most (3/2)ranges::distance(r)
applications of the corresponding predicatecomparisons
and twice as many applications of the projection, if any.
-24- Remarks: An invocation may explicitly specify an argument
for the template parameter T
of the overloads in namespace std.
std::start_lifetime_as inadvertently has undefined behavior due to use of std::bit_castSection: 20.2.6 [obj.lifetime] Status: New Submitter: Jan Schultke Opened: 2024-10-23 Last modified: 2025-10-22
Priority: 3
View other active issues in [obj.lifetime].
View all other issues in [obj.lifetime].
View all issues with New status.
Discussion:
Consider the motivating example from P2590R2: Explicit lifetime management:
struct X { int a, b; };
X* make_x() {
X* p = std::start_lifetime_as<X>(myMalloc(sizeof(struct X));
p->a = 1;
p->b = 2;
return p;
}
Assuming that myMalloc does not initialize the bytes of storage, this example has undefined behavior because
the value of the resulting object of trivially copyable type X is determined as if by calling
std::bit_cast<X>(a) for the implicitly-created object a of type X
(20.2.6 [obj.lifetime] paragraph 3), whose object representation is filled with indeterminate bytes
obtained from myMalloc. Such a call to std::bit_cast has undefined behavior because std::bit_cast
does not tolerate the creation of an int where bits in the value representation are indeterminate
(22.11.3 [bit.cast] paragraph 2), and such an int is the smallest enclosing object of some of the
indeterminate bits.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
"NAD, it says the storage is not accessed, so no UB from reading indeterminate bits."
"The UB comes from bit_cast's Preconditions, not from reading an indeterminate value."
Core should review any change here.
Proposed resolution:
This wording is relative to N4993.
Modify 20.2.6 [obj.lifetime] as indicated:
[Drafting note: The proposed resolution does not alter the behavior for erroneous bits. Therefore, a call to
std::start_lifetime_asmay have erroneous behavior when used on storage with indeterminate bits, despite not accessing that storage. An alternative resolution would be to produce objects whose value is erroneous.]
template<class T> T* start_lifetime_as(void* p) noexcept; template<class T> const T* start_lifetime_as(const void* p) noexcept; template<class T> volatile T* start_lifetime_as(volatile void* p) noexcept; template<class T> const volatile T* start_lifetime_as(const volatile void* p) noexcept;-1- Mandates: […]
-2- Preconditions: […] -3- Effects: Implicitly creates objects (6.8.2 [intro.object]) within the denoted region consisting of an objectaof typeTwhose address isp, and objects nested withina, as follows: The object representation ofais the contents of the storage prior to the call tostart_lifetime_as. The value of each created objectoof trivially copyable type (6.9.1 [basic.types.general])Uis determined in the same manner as for a call tobit_cast<U>(E)(22.11.3 [bit.cast]), whereEis an lvalue of typeUdenotingo, except that the storage is not accessed and that for each indeterminate bitbin the value representation of the result, the smallest object containing that bitbhas indeterminate value where the behavior would otherwise be undefined. The value of any other created object is unspecified.
Section: 23 [containers] Status: New Submitter: Jonathan Wakely Opened: 2024-11-21 Last modified: 2025-02-07
Priority: 4
View other active issues in [containers].
View all other issues in [containers].
View all issues with New status.
Discussion:
The Containers clause often uses "references, pointers, or iterators" which is verbose, and needs to be said in full whenever talking about iterator invalidation. It would be helpful to have a term of art that refers to all of them, something like "element references" or to avoid any confusion with actual references, "element indicators". Maybe "element handles" but that could be confused with node handles for associative containers, and (based on Wikipedia) has connotations of additional indirection, and something which would not be invalidated when the underlying storage changes.
[2025-02-07; Reflector poll]
Set priority to 4 after reflector poll.
"Maybe 'pointer to elements' or a longer phrase that includes the verb 'invalidates', which has special meaning in this section."
"Note that there are cases where we invalidate iterators but not pointers/references."
"Maybe define 'addresses' to mean 'pointers and references' since they're always invalidated at the same time, but iterators are sometimes separate."
"Referential element accessors"
"Define 'pointer-invalidating' (for both pointers and references) and 'iterator-invalidating', and say that the former always implies the latter. Maybe also introduce antonyms 'pointer-preserving' and 'iterator-preserving'."
"Should be defined in terms of affected elements, e.g. 'pointer-invalidating for any erased elements'."
Proposed resolution:
Section: 32.5.4 [atomics.order] Status: SG1 Submitter: jim x Opened: 2024-11-13 Last modified: 2025-02-07
Priority: 3
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with SG1 status.
Discussion:
Consider this example
std::atomic<int> v = 0; // thread 1: v.load(std::memory_order::seq_cst); //thread 2: v.store(1,std::memory_order::seq_cst);
If the load operation reads the value 0, how are load and store operations ordered in the single total order?
According to 32.5.4 [atomics.order] p3 (emphasize mine)
An atomic operation A on some atomic object M is coherence-ordered before another atomic operation B on M if
[…]
(3.3) — A and B are not the same atomic read-modify-write operation, and there exists an atomic modification X of M such that A reads the value stored by X and X precedes B in the modification order of M, or
According to 32.5.8.2 [atomics.types.operations] p3 (emphasize mine)
Effects: Initializes the object with the value desired. Initialization is not an atomic operation
(6.10.2 [intro.multithread]).
So, how does 32.5.4 [atomics.order] p3 apply to this example such that the load operation precedes the store operation in the single total order S?
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll. Send to SG1.
LWG found the issue unclear and felt it was missing context that would help understand it properly.
In cplusplus/CWG/issues/641 the following example was given:
std::atomic<bool> a = false; std::atomic<bool> b = false; int v = 0; // thread 1: a.store(true, seq_cst); if(b.load(seq_cst)== false){ v = 1; // #1 } //thread 2: b.store(true, seq_cst); if(a.load(seq_cst)== false){ v = 2; // #2 }To prove whether #1 and #2 can have data race, we should prove whether it's possible thataandbsimultaneously readfalse. This proof equals whether there can be a valid single total order in this case. To determine the order ofb.loadandb.storewhenb.loadreads the initialization valuefalse, 32.5.4 [atomics.order] p3.3 should apply here. However, the initialization is not an atomic modification such thatXcannot be that value.
A possible fix is to amend 32.5.4 [atomics.order]/3.3 to say something like this:
(3.3) A and B are not the same atomic read-modify-write operation, and either
- (3.3.1) there exists an atomic modification X of M such that A reads the value stored by X and X precedes B in the modification order of M, or
- (3.3.2) A reads the initial value of X and B modifies M, or
Proposed resolution:
Section: 23 [containers] Status: New Submitter: Jonathan Wakely Opened: 2024-11-22 Last modified: 2025-02-07
Priority: 4
View other active issues in [containers].
View all other issues in [containers].
View all issues with New status.
Discussion:
We sometimes give detailed specifications of container members which add
additional specification to the common requirements in
23.2.2 [container.requirements.general], for example
23.3.13.5 [vector.modifiers] defines vector::erase without actually
saying it erases any elements. The actual effects of erase are given in
23.2.4 [sequence.reqmts].
Authors of library wording often struggle with this non-local form of
specification, where we sometimes do spell out container member functions
in full, and sometimes rely on distant wording that applies to all containers.
It would be easier if vector::erase referred back to
23.2.4 [sequence.reqmts].
[2025-02-07; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
Section: 32.5.4 [atomics.order] Status: SG1 Submitter: jim x Opened: 2024-11-29 Last modified: 2025-02-07
Priority: 4
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with SG1 status.
Discussion:
32.5.4 [atomics.order] p8 and p9 gave two paradigmatic examples of how "circularly depend on their own computation" means. However, consider this example:
std::atomic<int> x = 0, y = 2;
// thread 1:
if (y.load(relaxed) == 1) { // #1
x.store(1, relaxed); // #2
}
//thread 2:
int pre = x.load(relaxed); // #3
while (pre != 0) {
if (x.compare_exchange_strong(pre, pre + 1, acquire, relaxed)) { // #4
break;
}
}
y.store(1, relaxed); // #5
when both #1 and #3 read 1, is this a kind of OOTA? #3 depends on #2, #2 depends on #1,
#1 depends on #5, and the execution of #5 depends on the exiting of the loop, which in turn initially
depends on pre.
cmpxchg keeps failing). So, it is unclear whether the execution of #5 depends on the loop.
However, it resembles the spin-loop (a failed cmpxchg is a pure load with a relaxed load), and the
subsequent codes won't execute until the loop exits. So, the scenario of spin-lock seems to agree that
the code after a loop depends on the loop(regardless of whether the loop can quickly exit or be a
long-time-run loop).
From this perspective, the while case is something like the if, for if, the condition is not
true, and the code thereof cannot be executed. Similarly, a code after a while cannot be executed
if the loop doesn't exit.
Suggested resolution:
Either accurately specify what "circularly depend on their own computation" means, or add a paradigmatic
example regarding loop to indicate what it means.
[Additional discussion from submitter:]
I meant, that for a classic spin-lock, the code after the loop won't be executed until the loop exits, and the operation is just a pure load with relaxed memory order during the busy wait. The loop for a spin-lock can have three possible cases:
The compiler cannot assume which case the loop for the spin-lock is. The code after the loop won't be reordered(during the busy waiting with a relaxed memory order); otherwise, implementing the spin-lock would be an issue based on the assumption that the codes after the loop wouldn't be reached when the loop was busy waiting.
In this example, the loop has the same possible cases as the loop
in the spin-lock. So, the execution of #5 seems to depend on the loop
and the loop depends on the pre. why I mention the spin-lock just is to
justify that the code after the loop won't be reordered in both compile-time
and runtime even though the condition of the loop is a pure load with a relaxed
memory order(otherwise, the foundation for which the busy-waiting of a
spink-lock is based on won't be true.). So, the loop has a similar effect
to the if statement on the control flow: the code in the block of
the if statement won't be executed if the condition is false, as well,
the code after the loop won't be executed if the loop hasn't yet exited.
[2025-02-07; Reflector poll]
Set priority to 4 after reflector poll. Send to SG1.
"Should the example use unsigned so there's no chance of UB due to int
overflowing?"
"Contrived example showing well-known fact that OOTA prohibition is just hand-waving. N3786 introduced the current wording and made no secret of that. P1217 explored the issue further, as have other SG1 papers. Hard problems. Solutions welcome. Don't think this is the place to focus however."
Proposed resolution:
flat_foo::emplaceSection: 23.6.12.5 [flat.multiset.modifiers], 23.6.8.7 [flat.map.modifiers] Status: New Submitter: Arthur O'Dwyer Opened: 2024-12-09 Last modified: 2025-02-07
Priority: 3
View all issues with New status.
Discussion:
The usual pattern in 23 [containers] is that x.emplace(args...) has a precondition
(23.2.4 [sequence.reqmts] p20, 23.2.7.1 [associative.reqmts.general] p48) but no
Constraints element. That is, emplace is not SFINAE-friendly. And it has only the one overload,
so it doesn't need a constraint for purposes of overload resolution.
emplace: deque (23.3.5.4 [deque.modifiers]), list (23.3.11.4 [list.modifiers]),
vector (23.3.13.5 [vector.modifiers]), containers (23.2.4 [sequence.reqmts] p19),
associative containers (23.2.7.1 [associative.reqmts.general] p47),
unordered containers (23.2.8.1 [unord.req.general] p78), priority_queue (23.6.4.4 [priqueue.members] p5),
optional (22.5.3.4 [optional.assign] p29).
Constraints on emplace: flat_map (23.6.8.7 [flat.map.modifiers] p1),
flat_multiset (23.6.12.5 [flat.multiset.modifiers] p1), any (22.7.4.4 [any.modifiers] p1),
expected (22.8.6.4 [expected.object.assign] p16), variant (22.6.3.5 [variant.mod] p1).
I believe a Constraints element was accidentally copy-pasted from the spec of flat_map::insert(P&&)
— which does need the constraint because it's part of insert's large overload set — to
flat_map::emplace, and then from there to flat_multiset::emplace. The constraint is already (correctly) absent
from flat_set::emplace.
While we're touching this paragraph, also resolve the vague word "initializes" to "direct-non-list-initializes."
Editorially, pair<…> is a verbose way to spell the value_type of a flat_map; we should
be consistent and just say value_type.
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll. See reflector discussion for comments about proposed resolution.
Proposed resolution:
This wording is relative to N4993.
[Drafting note: 23.6.11.5 [flat.set.modifiers] is already OK as far as this issue is concerned: it has no wording for
[flat.multimap.modifiers] is already OK ditto: it does not exist. ]emplace.
Modify 23.6.12.5 [flat.multiset.modifiers] as indicated:
template<class... Args> iterator emplace(Args&&... args);-1- Mandates
-2- Effects: First, direct-non-list-initializes an objectConstraints:is_constructible_v<value_type, Args...>istrue.tof typevalue_typewithstd::forward<Args>(args)..., then insertstas if by:auto it = ranges::upper_bound(c, t, compare); c.insert(it, std::move(t));-3- Returns: An iterator that points to the inserted element.
Modify 23.6.8.7 [flat.map.modifiers] as indicated:
template<class... Args> pair<iterator, bool> emplace(Args&&... args);-1- Mandates
-2- Effects: First, direct-non-list-iConstraints:is_constructible_v<value_typeispair<key_type, mapped_type>, Args...>true.Initializes an objecttof typevalue_typewithpair<key_type, mapped_type>std::forward<Args>(args)...; if the map already contains an element whose key is equivalent tot.first,*thisis unchanged. Otherwise, equivalent to:auto key_it = ranges::upper_bound(c.keys, t.first, compare); auto value_it = c.values.begin() + distance(c.keys.begin(), key_it); c.keys.insert(key_it, std::move(t.first)); c.values.insert(value_it, std::move(t.second));-3- Returns: The
boolcomponent of the returned pair istrueif and only if the insertion took place, and the iterator component of the pair points to the element with key equivalent tot.first.
ssizeSection: 24.7 [iterator.range], 25.3.11 [range.prim.ssize] Status: New Submitter: Casey Carter Opened: 2024-12-13 Last modified: 2025-02-07
Priority: 3
View other active issues in [iterator.range].
View all other issues in [iterator.range].
View all issues with New status.
Discussion:
There exist range types R whose size is representable by neither ptrdiff_t nor
make-signed-like-t<ranges::range_size_t<R>>, specifically views::iota(size_t(0), size_t(-1)).
It's unfortunate that std::ssize and ranges::ssize produce a negative size for such ranges even when their
difference type is capable of representing their size (see demo).
Perhaps the ssize overloads should return
static_cast<common_type_t<ptrdiff_t, iter_difference_t<decltype(meow.begin())>>>(meow.size())
instead when the argument meow models range?
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
"The behaviour for Ranges regressed due to LWG 3403(i).
It might have been a mistake to broaden the domain of ranges::ssize to match
that of ranges::size. The latter includes some non-Range arguments to ease
the transition from std::size, but thjere was no existing body of pre-C++20
code calling std::ssize that needed a transition path."
Proposed resolution:
NULL is too broadSection: 17.2.3 [support.types.nullptr] Status: New Submitter: Janet Cobb Opened: 2024-12-09 Last modified: 2025-02-07
Priority: 3
View all issues with New status.
Discussion:
7.3.12 [conv.ptr]/1 reads in part: "A null pointer constant is an integer literal (5.13.2 [lex.icon])
with value zero or a prvalue of type std::nullptr_t.".
NULL is an implementation-defined null pointer constant.".
Together, these imply that #define NULL (::std::unreachable(), nullptr) is a conforming definition. The expression is
a prvalue of type std::nullptr_t, so it is a null pointer constant. This makes it implementation-defined whether
any program that evaluates NULL has undefined behavior.
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
"I'd very much like to see nullptr added to the footnote."
Previous resolution [SUPERSEDED]:
This wording is relative to N5001.
Modify 17.2.3 [support.types.nullptr] as indicated:
-2- The macro
161) Possible definitions includeNULLis an implementation-defined null pointer constant that is a literal (5.13.2 [lex.icon], 5.13.8 [lex.nullptr]).footnote 1610and0L, but not(void*)0.
[2025-02-07; Jonathan provides improved wording]
Proposed resolution:
This wording is relative to N5001.
Modify 17.2.3 [support.types.nullptr] as indicated:
-2- The macro
161) Possible definitions includeNULLis an implementation-defined null pointer constant that is a literal (5.13.2 [lex.icon], 5.13.8 [lex.nullptr]).footnote 161nullptr,0and0L, but not(void*)0.
subrange should provide data()Section: 25.5.4.1 [range.subrange.general], 25.5.4.1 [range.subrange.general] Status: New Submitter: Hewill Kang Opened: 2024-12-16 Last modified: 2025-02-07
Priority: 4
View all issues with New status.
Discussion:
Currently, only four view classes in <ranges> explicitly provide data() members.
Two of them are empty_view and single_view, because their data() is always valid
and can be marked noexcept.
The remaining two are ref_view and owning_view with constrained data(),
which is redundant since data() can always obtained from view_interface
when the underlying range is contiguous. I suspect this is because ranges::data
is more efficient.
However, subrange does not have a data() member, which seems worth considering
because this function can always be noexcept given that to_address is always noexcept.
[2025-02-07; Reflector poll]
Set priority to 4 after reflector poll.
Lots or NAD votes. "If we care about data being noexcept, we should add
conditional noexcept to view_interface overloads. Seems like design."
"I don't care about noexcept (impls can strengthen it if it matters), but it's a good idea to avoid the extra iterator copy."
Proposed resolution:
This wording is relative to N5001.
Modify 25.5.4.1 [range.subrange.general] as indicated:
namespace std::ranges {
[…]
template<input_or_output_iterator I, sentinel_for<I> S = I, subrange_kind K =
sized_sentinel_for<S, I> ? subrange_kind::sized : subrange_kind::unsized>
requires (K == subrange_kind::sized || !sized_sentinel_for<S, I>)
class subrange : public view_interface<subrange<I, S, K>> {
[…]
constexpr bool empty() const;
constexpr make-unsigned-like-t<iter_difference_t<I>> size() const
requires (K == subrange_kind::sized);
constexpr auto data() const noexcept requires contiguous_iterator<I>;
[…]
};
[…]
}
Modify 25.5.4.3 [range.subrange.access] as indicated:
constexpr make-unsigned-like-t<iter_difference_t<I>> size() const requires (K == subrange_kind::sized);-5- Effects:
[…]constexpr auto data() const noexcept requires contiguous_iterator<I>;-?- Effects: Equivalent to:
return to_address(begin_);
Section: 29.9.7 [linalg.helpers], 18.4.9 [concept.swappable], 24.3.3.2 [iterator.cust.swap] Status: New Submitter: Jiang An Opened: 2024-12-18 Last modified: 2025-02-07
Priority: 3
View all issues with New status.
Discussion:
Several exposition-only function objects in 29.9.7 [linalg.helpers] are specified with strange wording — "If the function selected by overload resolution does not return […], the program is ill-formed, no diagnostic required;". These conditions don't seem to be static properties because what the selected function returns can be completely dependent on the runtime environment and input.
Likewise, 18.4.9 [concept.swappable] and 24.3.3.2 [iterator.cust.swap] also contain such weird usage. Presumably we should say the behavior is undefined in these cases.[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
bitset::reference should be const-assignableSection: 22.9.2 [template.bitset] Status: New Submitter: Arthur O'Dwyer Opened: 2024-12-21 Last modified: 2025-02-07
Priority: 3
View other active issues in [template.bitset].
View all other issues in [template.bitset].
View all issues with New status.
Discussion:
LWG 3638(i), which proposes changes to vector<bool>::reference, is related.
Should vector<bool>::reference and bitset<N>::reference behave differently
in any respect? I think there's no good reason for them to behave differently, and good technical
incentives to permit them to behave the same. We already have implementation divergence: libc++ makes
bitset::reference const-assignable, whereas libstdc++ and MS STL do not. This means that libc++'s
bitset::reference successfully avoids false positives from Arthur's proposed -Wassign-to-class-rvalue
diagnostic, while libstdc++'s does not (See Godbolt).
const into the existing spec, because ABI. But also, since our goal is consistency
with the post-P2321 vector<bool>::reference, we should do the same thing here as P2321, not invent anything novel.
Open questions related to the current P/R:
LWG 3638 proposes to add these three swap overloads to vector<bool>::reference.
Should we also, consistently, add them to bitset::reference? I think we should.
friend constexpr void swap(reference x, reference y) noexcept; friend constexpr void swap(reference x, bool& y) noexcept; friend constexpr void swap(bool& x, reference y) noexcept;
Both vector<bool>::reference and bitset::reference right now are specified with
constexpr reference(const reference&) = default;
which is meaningless because we don't know the data members of reference. So this isn't actually
specifying that the constructor is trivial, let alone that it's noexcept. I think we should re-specify
both types' copy constructors as simply constexpr and noexcept; and then if we want them to be trivial,
we should say so in English prose.
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
"Just const-quality the existing assignment operators."
"Would need to change the return type (breaking) or use const_cast (weird)."
"And it would be needlessly inconsistent with vector<bool>::reference."
"The swap part belongs in LWG 3638(i)."
Previous resolution [SUPERSEDED]:
This wording is relative to N5001.
Modify 22.9.2.1 [template.bitset.general] as indicated:
namespace std { template<size_t N> class bitset { public: // bit reference class reference { public: constexpr reference(const reference&) = default; constexpr ~reference(); constexpr reference& operator=(bool x) noexcept; // for b[i] = x; constexpr reference& operator=(const reference&) noexcept; // for b[i] = b[j]; constexpr const reference& operator=(bool x) const noexcept; constexpr bool operator~() const noexcept; // flips the bit constexpr operator bool() const noexcept; // for x = b[i]; constexpr reference& flip() noexcept; // for b[i].flip(); friend constexpr void swap(reference x, reference y) noexcept; friend constexpr void swap(reference x, bool& y) noexcept; friend constexpr void swap(bool& x, reference y) noexcept; }; […] }; […] }
[2025-02-07; Jonathan provides improved wording]
Moved swap changes to LWG 3638(i).
Proposed resolution:
This wording is relative to N5001.
Modify 22.9.2.1 [template.bitset.general] as indicated:
namespace std {
template<size_t N> class bitset {
public:
// bit reference
class reference {
public:
constexpr reference(const reference&) = default;
constexpr ~reference();
constexpr reference& operator=(bool x) noexcept; // for b[i] = x;
constexpr reference& operator=(const reference&) noexcept; // for b[i] = b[j];
constexpr const reference& operator=(bool x) const noexcept;
constexpr bool operator~() const noexcept; // flips the bit
constexpr operator bool() const noexcept; // for x = b[i];
constexpr reference& flip() noexcept; // for b[i].flip();
};
[…]
};
[…]
}
Section: 33.9.2 [exec.snd.expos] Status: New Submitter: Eric Niebler Opened: 2025-01-02 Last modified: 2025-02-07
Priority: 2
View other active issues in [exec.snd.expos].
View all other issues in [exec.snd.expos].
View all issues with New status.
Discussion:
33.9.2 [exec.snd.expos]/p39 reads:
For a subexpression
sndrletSndrbedecltype((sndr)). Letrcvrbe a receiver with an associated environment of typeEnvsuch thatsender_in<Sndr, Env>istrue.completion-signatures-for<Sndr, Env>denotes a specialization ofcompletion_signatures, the set of whose template arguments correspond to the set of completion operations that are potentially evaluated as a result of starting (33.3 [exec.async.ops]) the operation state that results from connectingsndrandrcvr. Whensender_in<Sndr, Env>isfalse, the type denoted bycompletion-signatures-for<Sndr, Env>, if any, is not a specialization ofcompletion_signatures.
This paragraph is trying to specify the return type of basic-sender::get_completion_signatures,
but it immediately goes off the rails when it tests for the satisfaction of sender_in<Sndr, Env>.
The sender_in<Sndr, Env> concept requires that get_completion_signatures(sndr, env) is
well-formed and that its type is a specialization of completion_signatures. But the return type of
get_completion_signatures(sndr, env) is exactly the thing this para is trying to define!
[2025-02-07; Reflector poll]
Set priority to 2 after reflector poll.
First sentence of p39 should be either "Let the type Sndr be ..." or
"Let Sndr be ...", but not "Let type Sndr be ...".
Proposed resolution:
This wording is relative to N5001.
Modify 33.9.2 [exec.snd.expos] as indicated:
-39- Let type
Recommended practice: When the typeSndrbe a (possiblyconst-qualified) specialization ofbasic-senderor an lvalue reference of such, and letRcvrbe the type of a receiver with an associated environment of typeEnv. If the typebasic-operation<Sndr, Rcvr>is well-formed, letopbe an lvalue subexpression of that type.For a subexpressionsndrletSndrbedecltype((sndr)). Letrcvrbe a receiver with an associated environment of typeEnvsuch thatsender_in<Sndr, Env>istrue.completion-signatures-for<Sndr, Env>denotes a specialization ofcompletion_signatures, the set of whose template arguments correspond to the set of completion operations that are potentially evaluated (6.3 [basic.def.odr]) as a result of evaluatingop.start().starting (33.3 [exec.async.ops]) the operation state that results from connectingOtherwise, the type denoted bysndrandrcvr. Whensender_in<Sndr, Env>isfalsecompletion-signatures-for<Sndr, Env>, if any, is not a specialization ofcompletion_signatures.basic-operation<Sndr, Rcvr>is ill-formed, implementations are encouraged to use the type denoted bysender_in<Sndr, Env>isfalsecompletion-signatures-for<Sndr, Env>to communicate to users why.
ios_base members may not have indeterminate values after constructionSection: 31.5.2.8 [ios.base.cons], 31.5.2.4 [ios.base.locales] Status: New Submitter: S. B. Tam Opened: 2025-01-12 Last modified: 2025-02-07
Priority: 3
View all other issues in [ios.base.cons].
View all issues with New status.
Discussion:
31.5.2.8 [ios.base.cons] specifies that
Each
ios_basemember has an indeterminate value after construction.
However
If the ios_base object has static storage duration, the members would have been zero-initialized.
If the ios_base object has automatic storage duration, the members would have erroneous values.
In either case, the constructor cannot cause the members to have indeterminate values after construction.
A related problem is that 31.5.2.4 [ios.base.locales] requiresios_base::getloc to return:
If no locale has been imbued, a copy of the global C++ locale,
locale(), in effect at the time of construction.
However, according to 31.5.2.8 [ios.base.cons], the corresponding member is not initialized
until basic_ios::init is called.
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
"First part is NAD, can use e.g. placement new to force indeterminate values, and it's not observable by well-defined progams anyway."
Proposed resolution:
This wording is relative to N5001.
Modify 31.5.2.4 [ios.base.locales] as indicated:
ios_base();-1- Effects: Each
ios_basemember has an unspecified (possibly indeterminate or erroneous) value after construction. The object's members shall be initialized by callingbasic_ios::initbefore the object's first use or before it is destroyed, whichever comes first; otherwise the behavior is undefined.
Modify 31.5.2.8 [ios.base.cons] as indicated:
locale getloc() const;-4- Returns: If no locale has been imbued, a copy of the global C++ locale,
locale(), in effect at the time ofconstructionthe lastbasic_ios::initcall on the current object. Otherwise, returns the imbued locale, to be used to perform locale-dependent input and output operations.
Section: 30.11.2 [time.zone.db] Status: New Submitter: S. B. Tam Opened: 2025-01-16 Last modified: 2025-02-07
Priority: 3
View all issues with New status.
Discussion:
N5001 30.11.2.2 [time.zone.db.list] p3:
Synchronization: This operation is thread-safe with respect to
reload_tzdb().
N5001 30.11.2.3 [time.zone.db.access] p2:
Synchronization: It is safe to call this function from multiple threads at one time.
N5001 30.11.2.4 [time.zone.db.remote] p3:
Synchronization: This function is thread-safe with respect to
get_tzdb_list().front()andget_tzdb_list().erase_after().
The standard does not define what "thread-safe" means, which makes the meaning of these sentences unclear. Does it mean that "concurrent calls do not introduce data races", or does it additionally require a single total order on these operations? In either case, it should be specified clearly.
[2025-02-07; Reflector poll]
Set priority to 3 after reflector poll.
"We're missing a guarantee that reload_tzdb() strongly happens before
a call to get_tzdb_list().front() that retrieves a newly added element.
Otherwise accessing any time_zone obtained from get_tzdb_list().front()
could race with the call to reload_tzdb() that writes the front of the list."
Proposed resolution:
expected<int, int> isn't specified to be trivially assignableSection: 22.8.6.4 [expected.object.assign], 22.8.7.4 [expected.void.assign] Status: New Submitter: Barry Revzin Opened: 2025-01-21 Last modified: 2025-02-07
Priority: 2
View other active issues in [expected.object.assign].
View all other issues in [expected.object.assign].
View all issues with New status.
Discussion:
Currently, we specify that the copy constructor, move constructor, and destructor expected<int, int>
are trivial operations. But we do not specify that the copy assignment operator or move assignment operator are.
There is implementation divergence — MSVC's implementation is trivially copyable, but libstdc++'s and libc++'s
are not (although, they are trivial for the purposes of calls, which is important for being able to return such a
type in a register).
T and E are trivially
copy assignable and the move assignment operator is trivial if T and E are trivially move assignable.
[2025-02-07; Reflector poll]
Set priority to 2 after reflector poll.
"Needs to consider trivial constructors as well as assignment."
"This is an ABI break for something that shipped in C++23, NAD."
Proposed resolution:
This wording is relative to N5001.
Modify 22.8.6.4 [expected.object.assign] as indicated:
constexpr expected& operator=(const expected& rhs);-2- Effects: […]
-3- Returns:*this. -4- Remarks: This operator is defined as deleted unless:
(4.1) — […]
(4.2) — […]
(4.3) — […]
(4.4) — […]
-?- This operator is trivial if
(?.1) —
is_trivially_copy_assignable_v<T>istrueand(?.2) —
is_trivially_copy_assignable_v<E>istrue.constexpr expected& operator=(expected&& rhs) noexcept(see below);-5- Constraints: […]
-6- Effects: […] -7- Returns:*this. -8- Remarks: The exception specification is equivalent to:is_nothrow_move_assignable_v<T> && is_nothrow_move_constructible_v<T> && is_nothrow_move_assignable_v<E> && is_nothrow_move_constructible_v<E>-?- This operator is trivial if
(?.1) —
is_trivially_move_assignable_v<T>istrueand(?.2) —
is_trivially_move_assignable_v<E>istrue.
Modify 22.8.7.4 [expected.void.assign] as indicated:
constexpr expected& operator=(const expected& rhs);-1- Effects: […]
-2- Returns:*this. -3- Remarks: This operator is defined as deleted unlessis_copy_assignable_v<E>istrueandis_copy_constructible_v<E>istrue. -?- This operator is trivial ifis_trivially_copy_assignable_v<E>istrue.constexpr expected& operator=(expected&& rhs) noexcept(see below);-4- Constraints: […]
-5- Effects: […] -6- Returns:*this. -7- Remarks: The exception specification is equivalent tois_nothrow_move_constructible_v<E> && is_nothrow_move_assignable_v<E>. -?- This operator is trivial ifis_trivially_move_assignable_v<E>istrue.
std::visit with immediate functionsSection: 22.6.7 [variant.visit] Status: New Submitter: Jiang An Opened: 2025-01-26 Last modified: 2025-01-30
Priority: 2
View all other issues in [variant.visit].
View all issues with New status.
Discussion:
std::visit generally needs to be implemented with "vtables" that contain function pointers.
When std::visit needs to call an immediate function (e.g. when passing a lambda whose
operator() is consteval), the vtable will contain a pointer to an immediate-escalated
function, which forbids the vtable from being a constexpr variable.
std::visit, it seems necessary to form
the vtable, or do some non-constant-time lookup each time when calling std::visit. In other words,
22.6.7 [variant.visit]/8 seems to be unimplementable when an immediate function is involved.
[2025-01-01; P3603R0 addresses this]
[2025-01-30; Reflector poll]
Set priority to 2 after reflector poll.
Proposed resolution:
This wording is relative to N5001.
Modify 22.6.7 [variant.visit] as indicated:
template<class Visitor, class... Variants> constexpr see below visit(Visitor&& vis, Variants&&... vars); template<class R, class Visitor, class... Variants> constexpr R visit(Visitor&& vis, Variants&&... vars);-1- […] Let
[…] -8- Complexity: Ifnbesizeof...(Variants). […]n > 1or any of the aforementionedINVOKEoperations calls an immediate function, the invocation of the callable object has no complexity requirements. OtherwiseFor, the invocation of the callable object is implemented in constant time, i.e., forn ≤ 1n = 1, it does not depend on the number of alternative types ofV0.Forn > 1, the invocation of the callable object has no complexity requirements.
Section: 33.9.1 [exec.snd.general] Status: New Submitter: Eric Niebler Opened: 2025-02-03 Last modified: 2025-02-07
Priority: 1
View all issues with New status.
Discussion:
Imported from cplusplus/sender-receiver #308.
33.9.1 [exec.snd.general]/p1 reads:
Subclauses 33.9.11 [exec.factories] and 33.9.12 [exec.adapt] define customizable algorithms that return senders. Each algorithm has a default implementation. LetThe emphasized sentence is the problem. Since P2300 got lazy customization, the expressionsndrbe the result of an invocation of such an algorithm or an object equal to the result (18.2 [concepts.equality]), and letSndrbedecltype((sndr)). Letrcvrbe a receiver of typeRcvrwith associated environmentenvof typeEnvsuch thatsender_to<Sndr, Rcvr>istrue. For the default implementation of the algorithm that producedsndr, connectingsndrtorcvrand starting the resulting operation state (33.3 [exec.async.ops]) necessarily results in the potential evaluation (6.3 [basic.def.odr]) of a set of completion operations whose first argument is a subexpression equal torcvr. LetSigsbe a pack of completion signatures corresponding to this set of completion operations. Then the type of the expressionget_completion_signatures(sndr, env)is a specialization of the class templatecompletion_signatures( [exec.util.cmplsig]), the set of whose template arguments isSigs. If a user-provided implementation of the algorithm that producedsndris selected instead of the default, any completion signature that is in the set of types denoted bycompletion_signatures_of_t<Sndr, Env>and that is not part ofSigsshall correspond to error or stopped completion operations, unless otherwise specified.
get_completion_signatures(snd, env)
could dispatch to a customization.
We should define a low-level exposition-only
get-completion-signatures
function that does the same as get_completion_signatures
except without the sender transformation.
Then we can express get_completion_signatures in terms of that.
[2025-02-07; Reflector poll]
Set priority to 1 after reflector poll.
Proposed resolution:
connect_result_t should be constrained with sender_toSection: 99 [exec.syn] Status: New Submitter: Eric Niebler Opened: 2025-02-04 Last modified: 2025-02-07
Priority: 1
View all issues with New status.
Discussion:
Imported from cplusplus/sender-receiver #320.
Since turning execution::connect(sndr, rcvr)'s constraints
to a mandates, the connect customization point is now unconstrained.
But there is at least one place in the algorithms (33.9.12.10 [exec.let])
that is using connect_result_t in an immediate context of a function template
with the expectation that connect_result_t<Sndr, Rcvr>
will be well-formed only when Sndr and Rcvr can actually be connected.
with the current definition, connect_result_t<Sndr, Rcvr>
could very well cause a hard error; it will never cuase a substitution failure.
The solution is to constrain the connect_result_t alias template.
Just as completion_signatures_of_t<Sndr, Env>
is constrained with sender_in<Sndr, Env>,
so too should connect_result_t<Sndr, Rcvr>
be constrained with sender_to<Sndr, Rcvr>.
For reference, the sender_to concept is defined as follows:
template<class Sndr, class Rcvr>
concept sender_to =
sender_in<Sndr, env_of_t<Rcvr>> &&
receiver_of<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>> &&
requires (Sndr&& sndr, Rcvr&& rcvr) {
connect(std::forward<Sndr>(sndr), std::forward<Rcvr>(rcvr));
};
[2025-02-07; Reflector poll]
Set priority to 1 after reflector poll.
Proposed resolution:
This wording is relative to N5001.
template<class Sndr, class Rcvr> requires sender_to<Sndr, Rcvr> using connect_result_t = decltype(connect(declval<Sndr>(), declval<Rcvr>()));
source_location is not specified when used in an default template argumentSection: 17.8.2.2 [support.srcloc.cons] Status: New Submitter: Cassio Neri Opened: 2025-02-07 Last modified: 2025-10-23
Priority: 3
View all other issues in [support.srcloc.cons].
View all issues with New status.
Discussion:
17.8.2.2 [support.srcloc.cons]/2 in N5001 says nothing
about using source_location::current() in an initializer of a template
parameter. The example below suggests that gcc, clang and msvc seem to agree
on this case. It would be nice if the Standard legitimized current practice.
#include <source_location>
template <int i = std::source_location::current().line()> // line 3
struct A {
static constexpr int value = i;
};
template <int i = std::source_location::current().line()> // line 8
constexpr int f() {
return i;
}
static_assert(A<>::value == 3); // passes
static_assert(f() == 8); // passes
[2025-02-07; Jonathan provides wording]
For a default argument of a function parameter, current() is recommended
to return the location of the caller that makes use of that default argument.
For a default template argument, the location would be determined by the
template's point of instantiation (13.8.4.1 [temp.point]) which would
not always do what users expect. Using the location of the default template
argument in the template declaration seems sensible and predictable,
and matches existing practice. Arguably, this doesn't need to be stated
because it's just "exactly where the current() call appear in the source".
The other cases in the Remarks paragraph are situations where the preferred
location is different, because we want to know where it's used, not defined.
[2025-10-23; Reflector poll.]
Set priority to 3 after reflector poll.
CWG 2631 clarified the rules for immediate invocation for default function arguments and member initializers. We should consider rewriting Remarks to rely on that, and change most of the existing paragraph into normative note. Also use Recommended practice element, as this is normative recommendation.
The cross reference to 11.4 [class.mem] should be changed to 11.4.1 [class.mem.general].
The treatment of default template arguments is the subject of CWG
1635.
An alternative design could be that source_location inside default template argument
refers to point of use. It was noted that this does not reflect current behavior
of compilers.
Proposed resolution:
This wording is relative to N5001.
-2- Remarks: Any call tocurrentthat appears as a default member initializer (11.4 [class.mem]), or as a subexpression thereof, should correspond to the location of the constructor definition or aggregate initialization that uses the default member initializer. Any call tocurrentthat appears as a default argument (9.3.4.7 [dcl.fct.default]), or as a subexpression thereof, should correspond to the location of the invocation of the function that uses the default argument (7.6.1.3 [expr.call]). Any call tocurrentthat appears as a default template argument (13.2 [temp.param]), or as a subexpression thereof, should correspond to the location where the default template argument is specified.
cache_latest_view::iterator's reference type Section: 25.7.34.3 [range.cache.latest.iterator] Status: New Submitter: Hewill Kang Opened: 2025-02-09 Last modified: 2025-06-13
Priority: 3
View all issues with New status.
Discussion:
cache_latest_view::iterator can be roughly regarded as an iterator that
transforms the original iterator's reference, because its reference is always an lvalue
which is range_reference_t<V>&, even if the original iterator returns
an rvalue.
iter_move and return the rvalue
reference type of the original iterator, because the original reference has changed, so the former
may not form a valid common reference with the original rvalue reference.
That is, in some rare cases even if the cache_latest_view can be legally constructed,
it may not be an input_range as indirectly_readable is not satisfied.
A contrived example could be:
struct I {
using value_type = int;
using difference_type = int;
struct move_only_ref {
move_only_ref(const int&);
move_only_ref(move_only_ref&&) = default;
};
move_only_ref operator*() const;
I& operator++();
void operator++(int);
};
using R = ranges::cache_latest_view<ranges::subrange<I, unreachable_sentinel_t>>; // ok
static_assert(input_iterator<I>);
static_assert(ranges::input_range<R>); // failed
It's unclear whether we should change the reference type to range_reference_t<V>&&
to preserve the original ref-qualifiers as much as possible (I don't see any discussion of this option
in the original paper), or not provide a specialized iter_move, since it's intuitive to have
the default iter_move return an rvalue when it always returns an lvalue.
[2025-06-13; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 30.11.2.1 [time.zone.db.tzdb] Status: New Submitter: Jonathan Wakely Opened: 2025-02-22 Last modified: 2025-10-21
Priority: 2
View all issues with New status.
Discussion:
Since
October 2022
the IANA Time Zone Database's tzfile data format allows a Link to refer
to another Link, instead of referring directly to a Zone.
The zic(8)
man page says:
The TARGET field should appear as the NAME field in some zone line or as the LINK-NAME field in some link line. The LINK-NAME field is used as an alternative name for that zone; it has the same syntax as a zone line's NAME field. Links can chain together, although the behavior is unspecified if a chain of one or more links does not terminate in a Zone name.Because chains of links were introduced to
tzfile after chrono::tzdb
was standardized, C++ does not properly support chains of links.
The time_zone_link::target() member function is required to return
"The name of the time_zone for which this time_zone_link provides
an alternative name."
This doesn't allow returning the name of another time_zone_link,
which implies that the implementation should flatten a series of links so
that the target is a time_zone. However, this discards information which
is present in the tzdb, so that the information exposed to a C++ program
does not preserve the original structure of a chain of links.
The standard could be adjusted to support chains of links by allowing
time_zone_link::target() to refer to another link, and then altering the
algorithm used by tzdb::locate_zone(string_view) to find a time_zone from
a name that might be a zone or a link.
[2025-10-21; Reflector poll.]
Set priority to 2 after reflector poll. Add Jonathan's comments on the resolution:
The goal of the resolution is to permit time_zone_link::target to name
another link, and not overspecify tzdb::locate_zone to require that
tz_l.target() names a valid zone tz. Considered normatively saying what
happens if a link chain doesn't terminate in a zone (like the zic(8) man page
does), but that seemed like overspecification. We don't currently say what
happens if a non-chained link has an invalid target, it's just covered by
locate_zone failing to find anything.
So just added a note saying bad chains are bad.
Not urgent, as Libstdc++ and libc++ already handle this in some form. Links that use the same name as zones are unlikely to be a problem with the official IANA database, probably only relevant for manually edited data. Likewise for cycles in link chains. Maybe just leave to QoI.
Proposed resolution:
This wording is relative to N5001.
const time_zone* locate_zone(string_view tz_name) const;-2- Returns:
- (2.1) — If
zonescontains an elementtzfor whichtz.name() == tz_name, a pointer totz;- (2.2) — otherwise, if
linkscontains an elementtz_lfor whichtz_l.name() == tz_name, thena pointer to the elementthe result oftzof zones for whichtz.name() == tz_l.target()locate_zone(tz_l.target()). .[Note 1: A
time_zone_linkspecifies an alternative name for atime_zone. — end note]-3- Throws: If a
const time_zone*cannot be found as described in the Returns: element, throws aruntime_error.[Note 2: On non-exceptional return, the return value is always a pointer to a valid
time_zone. — end note]-?- Remarks: If both
zonesandlinkscontain elements that matchtz_namethen it is unspecified whether&tzorlocate_zone(tz_l.target())is returned.[Drafting note: This gives flexibility how to implement the lookup inlocate_zone.]
-1- A
time_zone_linkspecifies an alternative name for atime_zone.time_zone_linksare constructed when the time zone database is initialized. The name of atime_zone_linkcan be used as an argument totzdb::locate_zone(30.11.2.1 [time.zone.db.tzdb]). Atime_zone_linkcan refer directly to atime_zone, or to anothertime_zone_link, forming a chain of links.
-1- Returns: The alternative name for the time zone.string_view name() const noexcept;string_view target() const noexcept;-2- Returns: The name of the
time_zonefor which thistime_zone_linkprovides an alternative name or the name of anothertime_zone_link.[Note 1:
tzdb::locate_zonefollows a chain of links formed when a link's target is the name of atime_zone_link, throwing an exception if the chain of links does not terminate in atime_zone. — end note]
Section: 29.5.4.5 [rand.eng.philox] Status: New Submitter: Thomas Köppe Opened: 2025-02-12 Last modified: 2025-08-19
Priority: 3
View all other issues in [rand.eng.philox].
View all issues with New status.
Discussion:
The current wording that specifies the operation of the Philox random bit generator seems needlessly vague. We can add precision by defining a few more terms, instead of requiring the reader to fill in the blanks.
Concretely, the variable is only vaguely defined at the moment, and the definition of the "r-round network", "rounds", and how they fit together, is somewhat informal and imprecise. The statement thatPhilox "returns the sequence = " is needlessly
ambiguous (what is here?).
I propose the change that I drafted at draft/pull/7152:
Namely, spell out the meaning of the "rounds" and create a distinct name for every value in every round.
This allows us to state the result precisely, and makes it clear how each round computes a new value from the
values of the previous rounds.
It seems convenient to change the round counter to be 1-based (and
is an alias for the initial value, ), so that the final result is
.
[2025-06-13; Reflector poll]
Set priority to 3 after reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N5001.
Modify 29.5.4.5 [rand.eng.philox] as indicated:
-2- The generation algorithm returns , the value stored in the element of after applying the transition algorithm.
-3- The state transition is performed as if by the following algorithm:if ( == ) {Philox(, ) // see below // this updates }-4- The
Philoxfunction maps the length-/2 sequence and the length- sequence into a length- output sequence. Philox applies an -round substitution-permutation network to the values in .A single round of the generation algorithm performs the following steps:That is, there are intermediate values , , …, , where , and for each round (with ), is computed from as follows. The output sequence is .
(4.1) —
The output sequence of the previous round ( in case of the first round) is permuted to obtain the intermediate state :An intermediate state is obtained by permuting the previous output, , where , and is defined in Table 124.
(4.2) —
The following computations are applied to the elements of the sequence:The next output is computed from the elements of the as follows. For
(4.2.?) — = mulhi(,,w) xor xor , and
= mulhi(,,w) xor xor(4.2.?) — = mullo(,,w),
= mullo(,,w)where
:
(4.2.1) — mullo() is the low half of the modular multiplication of and : ,
(4.2.2) — mulhi() is the high half of the modular multiplication of and : ,
(4.2.3) —
is the index in the sequences,is the round key for round , ,(4.2.4) —
is the index of the round,is the element of the key sequence ,
(4.2.5) — is the round key for round , ,
(4.2.6) — are the elements of the key sequence ,(4.2.7) — is
multipliers[], and(4.2.8) — is
round_consts[].
[2025-08-08; Matt Stephanson comments and makes wording improvements with Thomas Köppe]
For what it's worth, I believe the new wording correctly describes the algorithm and does not make any substantive changes.
As for the wording itself:
Paragraphs 1, 2, and 3 still refer to the output sequence as Y, so I don't think
it should be removed from the end of the first sentence in p4. On the contrary, to maintain the connection
and parallel the "$X^{(0)} := X$" wording, I think the final sentence should also be
"The output sequence is $X^{(r)} := Y$".
I can't explain why, but my intuition is that, in bullet (4.2), "the elements of $V^{(q)}$"
sounds better than "the elements of the $V^{(q)}$". The second "the" worked with the original wording
"the V sequence", but "sequence" is omitted in the proposed resolution.
The use of K for both the fixed key sequence and the round keys seems potentially
confusing. Maybe R for "round key" is better?
After discussion with Thomas Köppe there was agreement that (a) should be applied with the modification
that we should write it as definition of Y and not the other way around, that (b) should
be applied as suggested, and that there was no real consensus for proposal (c).
Proposed resolution:
This wording is relative to N5014.
Modify 29.5.4.5 [rand.eng.philox] as indicated:
-2- The generation algorithm returns , the value stored in the element of after applying the transition algorithm.
-3- The state transition is performed as if by the following algorithm:if ( == ) {Philox(, ) // see below // this updates }-4- The
Philoxfunction maps the length-/2 sequence and the length- sequence into a length- output sequence. Philox applies an -round substitution-permutation network to the values in .A single round of the generation algorithm performs the following steps:That is, there are intermediate values , , …, , where , and for each round (with ), is computed from as follows. The output sequence is .
(4.1) —
The output sequence of the previous round ( in case of the first round) is permuted to obtain the intermediate state :An intermediate state is obtained by permuting the previous output, , where , and is defined in Table 129.
(4.2) —
The following computations are applied to the elements of the sequence:The next output is computed from the elements of as follows. For
(4.2.?) — = mulhi(,,w) xor xor , and
= mulhi(,,w) xor xor(4.2.?) — = mullo(,,w),
= mullo(,,w)where
:
(4.2.1) — mullo() is the low half of the modular multiplication of and : ,
(4.2.2) — mulhi() is the high half of the modular multiplication of and : ,
(4.2.3) —
is the index in the sequences,is the round key for round , ,(4.2.4) —
is the index of the round,is the element of the key sequence ,
(4.2.5) — is the round key for round , ,
(4.2.6) — are the elements of the key sequence ,(4.2.7) — is
multipliers[], and(4.2.8) — is
round_consts[].
Section: 33.9 [exec.snd] Status: New Submitter: Frank Birbacher Opened: 2025-02-14 Last modified: 2025-10-23
Priority: 2
View all issues with New status.
Discussion:
In certain clauses for defining senders the unspecified order of evaluation of function arguments can lead to retrieving values from a move-from state of a sender. An example is 33.9.12.6 [exec.continues.on] where paragraph 3 states:
transform_sender(get-domain-early(sndr), make-sender(continues_on, sch, sndr))
In this expression the evaluation of get-domain-early(sndr) can happen before or after
the make-sender. The latter can steal the value from sndr by moving from it. So
get-domain-early may see the moved-from state of sndr and fail to obtain anything.
33.9.12.9 [exec.then] p3
33.9.12.10 [exec.let] p4
33.9.12.11 [exec.bulk] p2
[exec.split] p4
33.9.12.12 [exec.when.all] p3
33.9.12.13 [exec.into.variant] p3
33.9.12.14 [exec.stopped.opt] p2
33.9.12.15 [exec.stopped.err] p2
33.9.13.2 [exec.sync.wait.var] p1
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
"Don't want to have to consider whether you can call get_env on a moved-from sender.
If the domain is a stateless tag type, then get-domain-early's return value
can't depend on the actual value of the sender, just its type.
If that's correct, we can just spell this decltype(get-domain-early(sndr)){}.
We already do this for get-domain-late."
"The point of all these exposition-only things was to make the wording more compact and readable.
Adding decltype and parens everywhere wouldn't help that.
Maybe change get-domain-early and get-domain-late from function templates
to all-caps wording macros, e.g. GET-DOMAIN-EARLY(sndr)?"
"NAD, I think the issue is covered by “except sndr is evaluated just once.”"
Proposed resolution:
take/drop adaptorSection: 25.7.10.1 [range.take.overview], 25.7.12.1 [range.drop.overview] Status: New Submitter: Hewill Kang Opened: 2025-02-15 Last modified: 2025-10-21
Priority: 3
View other active issues in [range.take.overview].
View all other issues in [range.take.overview].
View all issues with New status.
Discussion:
The take/drop adaptor does not explicitly require N to be non-negative (although
the view class does), which makes it possible for some specialized cases to be well-defined when N is negative,
since no Preconditions are violated:
auto e = std::views::empty<int>
| std::views::take(-1); // []
auto i = std::views::iota(1, 5)
| std::views::drop(-1); // [0, 1, 2, 3, 4]
auto r = std::views::repeat('a', 2)
| std::views::drop(-1); // ['a', 'a', 'a']
This is not the intention, we should ban these cases.
Previous resolution [SUPERSEDED]:
This wording is relative to N5001.
Modify 25.7.10.1 [range.take.overview] as indicated:
-2- The name
views::takedenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::take(E, F)is ill-formed. Otherwise, the expressionviews::take(E, F)is expression-equivalent to:
(2.?) — Preconditions:
static_cast<D>(F) >= 0istrue.(2.1) — if
Tis a specialization ofempty_view(25.6.2.2 [range.empty.view]), then((void)F, decay-copy(E)), except that the evaluations ofEandFare indeterminately sequenced.Modify 25.7.12.1 [range.drop.overview] as indicated:
-2- The name
views::dropdenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::drop(E, F)is ill-formed. Otherwise, the expressionviews::drop(E, F)is expression-equivalent to:
(2.?) — Preconditions:
static_cast<D>(F) >= 0istrue.(2.1) — if
Tis a specialization ofempty_view(25.6.2.2 [range.empty.view]), then((void)F, decay-copy(E)), except that the evaluations ofEandFare indeterminately sequenced.
[2025-02-24]
Upon reflector discussion many preferred to use instead the new Hardened preconditions: element which have been introduced by the recently voted in P3471R4.
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
"Disagreement on whether to introduce Hardened preconditions in this Clause. Some discomfort with adding the element as a bullet point inside a list, no better suggestions though.
Proposed resolution:
This wording is relative to N5001 plus additions from P3471R4.
Modify 25.7.10.1 [range.take.overview] as indicated:
-2- The name
views::takedenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::take(E, F)is ill-formed. Otherwise, the expressionviews::take(E, F)is expression-equivalent to:
(2.?) — Hardened preconditions:
static_cast<D>(F) >= 0istrue.(2.1) — if
Tis a specialization ofempty_view(25.6.2.2 [range.empty.view]), then((void)F, decay-copy(E)), except that the evaluations ofEandFare indeterminately sequenced.
Modify 25.7.12.1 [range.drop.overview] as indicated:
-2- The name
views::dropdenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::drop(E, F)is ill-formed. Otherwise, the expressionviews::drop(E, F)is expression-equivalent to:
(2.?) — Hardened preconditions:
static_cast<D>(F) >= 0istrue.(2.1) — if
Tis a specialization ofempty_view(25.6.2.2 [range.empty.view]), then((void)F, decay-copy(E)), except that the evaluations ofEandFare indeterminately sequenced.
run_loop::finish should be noexceptSection: 33.12.1 [exec.run.loop] Status: New Submitter: Eric Niebler Opened: 2025-02-13 Last modified: 2025-06-13
Priority: 2
View all issues with New status.
Discussion:
Imported from cplusplus/sender-receiver #329.
run_loop::finish puts the run_loop into the finishing state so that the next
time the work queue is empty, run_loop::run will return instead of waiting for more work.
.finish() on a run_loop instance can potentially throw (finish() is not marked noexcept),
that is because one valid implementation involves acquiring a lock on a std::mutex — a potentially throwing operation.
But failing to put the run_loop into the finishing state is problematic in the same way
that a failing destructor is problematic: shutdown and clean-up code depends on it succeeding.
Consider sync_wait's use of run_loop:
sync-wait-state<Sndr> state;
auto op = connect(sndr, sync-wait-receiver<Sndr>{&state});
start(op);
state.loop.run();
if (state.error) {
rethrow_exception(std::move(state.error));
}
return std::move(state.result);
It is the job of sync-wait-receiver to put the run_loop into the finishing state
so that the invocation of state.loop.run() will return. It does that in its completion functions, like so:
void set_stopped() && noexcept;Effects: Equivalent to
state->loop.finish().
Here we are not handling the fact that state->loop.finish() is potentially throwing. Given that this
function is noexcept, this will lead to the application getting terminated. Not good.
state.result to be rethrown later, we still have a problem.
Since run_loop::finish() threw, the run_loop has not been placed into the finishing state.
That means that state.loop.run() will never return, and sync_wait will hang forever.
Simply put, run_loop::finish() has to be noexcept. The implementation must find a way to put the run_loop
into the finishing state. If it cannot, it should terminate. Throwing an exception and foisting the
problem on the caller — who has no recourse — is simply wrong.
[2025-06-13; Reflector poll]
Set priority to 2 after reflector poll.
"If this can call terminate(), we should explicitly say so
(c.f. 32.7.4 [thread.condition.condvar] p11)"
Proposed resolution:
This wording is relative to N5001.
Modify 33.12.1.1 [exec.run.loop.general] as indicated:
namespace std::execution {
class run_loop {
// 33.12.1.2 [exec.run.loop.types], associated types
class run-loop-scheduler; // exposition only
class run-loop-sender; // exposition only
struct run-loop-opstate-base { // exposition only
virtual void execute() = 0; // exposition only
run_loop* loop; // exposition only
run-loop-opstate-base* next; // exposition only
};
template<class Rcvr>
using run-loop-opstate = unspecified; // exposition only
// 33.12.1.4 [exec.run.loop.members], member functions
run-loop-opstate-base* pop-front(); // exposition only
void push-back(run-loop-opstate-base*); // exposition only
public:
// 33.12.1.3 [exec.run.loop.ctor], constructor and destructor
run_loop() noexcept;
run_loop(run_loop&&) = delete;
~run_loop();
// 33.12.1.4 [exec.run.loop.members], member functions
run-loop-scheduler get_scheduler();
void run();
void finish() noexcept;
};
}
Modify 33.12.1.4 [exec.run.loop.members] as indicated:
void finish() noexcept;-8- Preconditions:
-9- Effects: Changesstateis eitherstartingorrunning.statetofinishing. -10- Synchronization:finishsynchronizes with thepop-frontoperation that returnsnullptr.
num_put::do_put and void pointersSection: 28.3.4.3.3.3 [facet.num.put.virtuals] Status: New Submitter: Nikolas Klauser Opened: 2025-02-26 Last modified: 2025-06-13
Priority: 3
View other active issues in [facet.num.put.virtuals].
View all other issues in [facet.num.put.virtuals].
View all issues with New status.
Discussion:
The num_put::do_put overloads are defined in terms of printf.
However, it is not clear what the intended behaviour of pointers is.
num_put wording makes a quite clear statement that it should
be whatever printf("%p", ptr) would be, num_get is entirely silent
on which function should be used. This makes it entirely unclear whether
round-tripping is supposed to work. It's also not clear whether
num_put was just simple to specify via printf or whether the intent
was that the output matches in all cases. Round-tripping between
num_put and num_get was broken in libc++ until recently. However, to
fix that, the output of num_put::do_put no longer matches the libc's
printf in all cases. libstdc++ had this behaviour since at least two
decades, indicating that nobody seems to have a problem with
num_put::do_put and printf having different results in some rare cases.
[2025-06-13; Reflector poll]
Set priority to 3 after reflector poll.
We could emphasize that calling printf isn't actually intended:
The representations at the end of stage 1 consists of thechar's that would be printed by a call ofprintf(s, val)in a hypothetical implementation, wheresis the conversion specifier determined above.
Why is there an apostrophe in char's?
Proposed resolution:
This wording is relative to N5001.
Modify 28.3.4.3.3.3 [facet.num.put.virtuals] as indicated:
iter_type do_put(iter_type out, ios_base& str, char_type fill, long val) const; iter_type do_put(iter_type out, ios_base& str, char_type fill, long long val) const; iter_type do_put(iter_type out, ios_base& str, char_type fill, unsigned long val) const; iter_type do_put(iter_type out, ios_base& str, char_type fill, unsigned long long val) const; iter_type do_put(iter_type out, ios_base& str, char_type fill, double val) const; iter_type do_put(iter_type out, ios_base& str, char_type fill, long double val) const; iter_type do_put(iter_type out, ios_base& str, char_type fill, const void* val) const;-1- Effects: […]
-2- The details of this operation occur in several stages: […] -3- Detailed descriptions of each stage follow. -4- Returns:out. Stage 1: The first action of stage 1 is to determine a conversion specifier. […] For conversion fromvoid*the specifier is%p. The representations at the end of stage 1 consists of thechar's that would be printed by a call ofprintf(s, val)wheresis the conversion specifier determined above, except that any implementation-defined behavior ofprintfmay be different from a call toprintf.
basic_const_iterator's relational operators due to ADL + CWG 2369Section: 24.5.3.5 [const.iterators.ops] Status: New Submitter: Patrick Palka Opened: 2025-03-03 Last modified: 2025-06-13
Priority: 2
View all issues with New status.
Discussion:
Consider the example (devised by Hewill Kang)
using RCI = reverse_iterator<basic_const_iterator<vector<int>::iterator>>; static_assert(std::totally_ordered<RCI>);
Checking RCI is totally_ordered entails checking
requires (RCI x) { x RELOP x; } for each RELOP in {<, >, <=, >=}
which we expect to be straightforwardly satisfied by reverse_iterator's
namespace-scope operators (24.5.1.8 [reverse.iter.cmp]):
template<class Iterator1, class Iterator2>
constexpr bool operator<(
const reverse_iterator<Iterator1>& x,
const reverse_iterator<Iterator2>& y);
// etc
But due to ADL we find ourselves also considering the basic_const_iterator
relop friends (24.5.3.5 [const.iterators.ops]/24).
template<input_iterator Iterator>
class basic_const_iterator {
template<not-a-const-iterator I>
friend constexpr bool operator<(const I& x, const basic_const_iterator& y)
requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>
// etc
};
Before CWG 2369 these candidates would quickly get
discarded since for the second operand RCI clearly isn't convertible to basic_const_iterator.
But after CWG 2369 implementations must first check these operators' constraints
(with Iterator = vector<int>::iterator and I = RCI), which entails
checking totally_ordered<RCI> recursively, causing the example to be ill-formed.
basic_const_iterator<J> where J is
constrained to match Iterator:
template<not-a-const-iterator I, same_as<Iterator> J>
friend constexpr bool operator<(const I& x, const basic_const_iterator<J>& y)
requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>
// etc
So that deduction fails earlier, before constraints get checked, for a second
operand that isn't a specialization of basic_const_iterator (or derived from one).
basic_const_iterator's
operators, but there the recursion was independent of CWG 2369.
[2025-06-13; Reflector poll]
Set priority to 2 after reflector poll.
Proposed resolution:
This wording is relative to N5001.
Modify 24.5.3.3 [const.iterators.iterator], class template basic_const_iterator synopsis, as indicated:
namespace std {
[…]
template<input_iterator Iterator>
class basic_const_iterator {
[…]
template<not-a-const-iterator I, same_as<Iterator> J>
friend constexpr bool operator<(const I& x, const basic_const_iterator<J>& y)
requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
template<not-a-const-iterator I, same_as<Iterator> J>
friend constexpr bool operator>(const I& x, const basic_const_iterator<J>& y)
requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
template<not-a-const-iterator I, same_as<Iterator> J>
friend constexpr bool operator<=(const I& x, const basic_const_iterator<J>& y)
requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
template<not-a-const-iterator I, same_as<Iterator> J>
friend constexpr bool operator>=(const I& x, const basic_const_iterator<J>& y)
requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;
[…]
};
}
Modify 24.5.3.5 [const.iterators.ops] as indicated:
template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator<(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator>(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator<=(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>; template<not-a-const-iterator I, same_as<Iterator> J> friend constexpr bool operator>=(const I& x, const basic_const_iterator<J>& y) requires random_access_iterator<Iterator> && totally_ordered_with<Iterator, I>;-23- Let
-24- Effects: Equivalent to:opbe the operator.return x op y.current_;
join_view incorrectly stores inner rangeSection: 25.7.14.2 [range.join.view], 25.7.15.2 [range.join.with.view] Status: New Submitter: Hewill Kang Opened: 2025-03-06 Last modified: 2025-10-23
Priority: 3
View other active issues in [range.join.view].
View all other issues in [range.join.view].
View all issues with New status.
Discussion:
When the inner range is a prvalue, join_view removes its cv-qualifiers
and stores it in the propagating-cache, which is not quite right as the inner range may
only be const-iterable (demo):
#include <ranges>
struct R {
int* begin() = delete;
int* end() = delete;
const int* begin() const;
const int* end() const;
};
int main() {
auto r = std::views::iota(0, 5)
| std::views::transform([](int) -> const R { return {}; })
| std::views::join;
auto b = r.begin(); // hard error
}
The proposed resolution preserves the inner range's original qualifiers, which is consistent with how
cache_latest_view stores the reference when it is a prvalue.
The same goes for join_with_view.
[2025-10-23; Reflector poll.]
Set priority to 3 after reflector poll.
Lot of NAD votes. This type satisfies, but does not model range.
Proposed resolution:
This wording is relative to N5001.
Modify 25.7.14.2 [range.join.view] as indicated:
namespace std::ranges {
template<input_range V>
requires view<V> && input_range<range_reference_t<V>>>
class join_view : public view_interface<join_view<V>> {
private:
using InnerRng = range_reference_t<V>; // exposition only
[…]
non-propagating-cache<remove_cv_t<InnerRng>> inner_; // exposition only, present only
// if is_reference_v<InnerRng> is false
public:
[…]
};
[…]
}
Modify 25.7.15.2 [range.join.with.view] as indicated:
namespace std::ranges {
[…]
template<input_range V, forward_range Pattern>
requires view<V> && input_range<range_reference_t<V>>
&& view<Pattern>
&& concatable<range_reference_t<V>, Pattern>
class join_with_view : public view_interface<join_with_view<V, Pattern>> {
using InnerRng = range_reference_t<V>; // exposition only
[…]
non-propagating-cache<remove_cv_t<InnerRng>> inner_; // exposition only, present only
// if is_reference_v<InnerRng> is false
[…]
public:
[…]
};
[…]
}
Section: 28.5.7 [format.range] Status: New Submitter: Hewill Kang Opened: 2025-03-06 Last modified: 2025-10-21
Priority: 2
View all issues with New status.
Discussion:
The standard does not explicitly prohibit ranges that are only const-iterable, i.e. a range with
const begin() and deleted or invalid non-const begin().
Unfortunately, those ranges cannot be formatted because the R in
formatter<R> is always without the const-qualifier,
which makes it never satisfy the range concept
(demo):
#include <print>
#include <ranges>
struct R {
int* begin() = delete;
int* end() = delete;
const int* begin() const;
const int* end() const;
};
int main() {
const R r;
static_assert(std::ranges::contiguous_range<decltype(r)>);
for (auto&& elem : r)
std::print("{} ", elem); // ok
std::ranges::for_each(
r, [](auto&& elem) { std::print("{} ", elem); }
); // ok
std::print("{}", r); // not ok
}
Although such type might be relatively rare, it does reflect an inconsistency in the general usage of formatting ranges, which do not support all valid ranges.
[2025-10-21; Reflector poll.]
Set priority to 2 after reflector poll.
The majority voted NAD, because the range shown in the issue is nonsensical. But enough people voted P2 that we should not close it without more discussion.
Proposed resolution:
Section: 23.4.1 [associative.general] Status: New Submitter: Tomasz Kaminski Opened: 2025-03-14 Last modified: 2025-08-29
Priority: 2
View all issues with New status.
Discussion:
The from_range deduction guide for maps currently do not handle ranges of tuple of two elements:
std::vector<int> v;
auto zv = std::views::zip(v, v);
std::map m4(std::from_range, zv); // Ill-formed, no-deduction guide
This seems to be result of merge conflict between P2165 (Compatibility between tuple, pair and tuple-like objects)
and P1206R4 (Conversions from ranges to containers): The helper range-key-type and
range-mapped-type aliases introduced by the later use the old formulation of ::first_type,
::second::type instead of tuple_element.
std::flat_map<int, float> fm; // iterator value_type is pair<int, float>
std::map m1(fm.begin(), fm.end()); // OK, deduces std::map<int, float>
auto tv = fm | std::views::transform(std::identity{}); // iterator value value_type is pair<int const&, float const&>
std::map m3(tv.begin(), tv.end()); // Ill-formed, deduces std::map<int const&, float&>
[2025-08-29; Reflector poll]
Set priority to 2 after reflector poll.
Proposed resolution:
This wording is relative to N5001.
[Drafting note: The proposed change also strips
constfrom the value type of themap, changing the behavior of previously working code:]std::pair<int const, float const> tp[2]; std::map m(std::begin(tp), std::end(tp)); // Was std::map<int, float const>, now std::map<int, float>
Modify 23.4.1 [associative.general] as indicated:
template<class InputIterator>
using iter-value-type =
typename iterator_traits<InputIterator>::value_type; // exposition only
template<class InputIterator>
using iter-key-type = remove_const_tremove_cvref_t<
tuple_element_t<0, iter-value-type<InputIterator>>>; // exposition only
template<class InputIterator>
using iter-mapped-type = remove_cvref_t<
tuple_element_t<1, iter-value-type<InputIterator>>>; // exposition only
template<class InputIterator>
using iter-to-alloc-type = pair<
add_const_t<
tuple_element_t<0, iter-value-type<InputIterator>>
iter-key-type<InputIterator>
>,
tuple_element_t<1, iter-value-type<InputIterator>>
iter-mapped-type<InputIterator>
>; // exposition only
template<ranges::input_range Range>
using range-key-type =
remove_const_t<typename ranges::range_value_t<Range>::first_type>
remove_cvref_t<tuple_element_t<0, ranges::range_value_t<Range>>>; // exposition only
template<ranges::input_range Range>
using range-mapped-type =
typename ranges::range_value_t<Range>::second_type
remove_cvref_t<tuple_element_t<1, ranges::range_value_t<Range>>>; // exposition only
template<ranges::input_range Range>
using range-to-alloc-type =
pair<add_const_t<
typename ranges::range_value_t<Range>::first_type
range-key-type<Range>
>,
typename ranges::range_value_t<Range>::second_type
range-mapped-type<Range>
>; // exposition only
std::deque, std::forward_list, or std::list?Section: 23.3.5.3 [deque.capacity], 23.3.7.5 [forward.list.modifiers], 23.3.11.3 [list.capacity] Status: New Submitter: Jiang An Opened: 2025-03-15 Last modified: 2025-10-17
Priority: 3
View all other issues in [deque.capacity].
View all issues with New status.
Discussion:
Currently, std::vector and std::inplace_vector's resize functions are specified to have no effects
on the container when an exception is throwing on appending. However, such specification seem to be missing
for std::deque, std::forward_list, and std::list.
resize exception guarantee for std::vector came from resolving LWG 2033(i) and were
later effectively copied to std::inplace_vector because that container's specification should resemble
as much as possible that of std::vector.
[2025-10-16; Reflector poll]
Set priority to 3 after reflector poll.
[2025-10-16; Jonathan provides wording]
LWG 4106(i) already fixed this for std::forward_list.
For std::list the "If an exception is thrown, there are no effects" wording
in 23.3.11.4 [list.modifiers] p2 doesn't apply to std::list::resize
because it's in a different subclause (23.3.11.3 [list.capacity]).
We can fix that though.
[2025-10-17; Comment from Nevin.]
deque::resize invalidates all iterators if it causes a map reallocation.
Proposed resolution:
This wording is relative to N5014.
Modify 23.3.5.3 [deque.capacity] as indicated:
constexpr void resize(size_type sz);-1- Preconditions:
Tis Cpp17MoveInsertable and Cpp17DefaultInsertable intodeque.-2- Effects: If
sz < size()istrue, erases the lastsize() - szelements from the sequence. Otherwise, appendssz - size()default-inserted elements to the sequence. If an exception is thrown, there is no effect on the container.constexpr void resize(size_type sz, const T& c);-3- Preconditions:
Tis Cpp17CopyInsertable intodeque.-4- Effects: If
sz < size()istrue, erases the lastsize() - szelements from the sequence. Otherwise, appendssz - size()copies ofcto the sequence. If an exception is thrown, there is no effect on the container.
Modify 23.3.11.3 [list.capacity] as indicated:
constexpr void resize(size_type sz);-1- Preconditions:
Tis Cpp17DefaultInsertable intolist.-2- Effects: If
size() < szistrue, appendssz - size()default-inserted elements to the sequence. Ifsz <= size()istrue, equivalent to:If an exception is thrown, there is no effect on the container.list<T>::iterator it = begin(); advance(it, sz); erase(it, end());constexpr void resize(size_type sz, const T& c);-3- Preconditions:
Tis Cpp17CopyInsertable intolist.-4- Effects:
As if byEquivalent to:if (sz < size() insert(end(), sz-size(), c); else if (sz < size()) { iterator i = begin(); advance(it, sz); erase(it, end()); } else ; // do nothing
to_input_view::iterator cannot be compared to its const sentinelSection: 25.7.35 [range.to.input] Status: New Submitter: Hewill Kang Opened: 2025-03-15 Last modified: 2025-06-13
Priority: 2
View all issues with New status.
Discussion:
to_input_view was recently added to the working draft by P3137R3.
#include <ranges>
int main() {
auto r = std::views::single(0)
| std::views::chunk(1)
| std::views::to_input;
r.begin() == std::as_const(r).end(); // #1, error
r.begin() == r.cend(); // #2, error
}
In #1, r.begin() returns to_input_view<chunk_view>::iterator<false>,
while the latter returns chunk_view::iterator<true>.
Since the former can only be compared with chunk_view::iterator<false> that cannot
be converted from chunk_view::iterator<true>, the two are incomparable.
to_input_view::iterator:
template<bool OtherConst = !Const>
requires sentinel_for<sentinel_t<maybe-const<OtherConst, V>>, iterator_t<Base>>
friend constexpr bool operator==(const iterator& x, const sentinel_t<maybe-const<OtherConst, V>>& y)
{ return x.current_ == y; }
Unfortunately, it still doesn't resolve #2, because r.cend() returns
basic_const_iterator<chunk_view::iterator<true>>, which cannot be compared to any
non-copyable iterators as its operator==(const S& s) requires S to be a
sentinel type, which rules out to_input_view::iterator, so the constraint is not satisfied.
to_input_view.
[2025-06-13; Reflector poll]
Set priority to 2 after reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 25.7.35.2 [range.to.input.view] as indicated:
[…]namespace std::ranges { template<input_range V> requires view<V> class to_input_view : public view_interface<to_input_view<V>> { V base_ = V(); // exposition only template<bool Const> class iterator; // exposition only template<bool Const> class sentinel; // exposition only […] }; […] }constexpr auto end() requires (!simple-view<V>);-?- Effects: Equivalent to:
return sentinel<false>(ranges::end(base_));constexpr auto end() const requires range<const V>;-4- Effects: Equivalent to:
return sentinel<true>(ranges::end(base_));
Modify 25.7.35.3 [range.to.input.iterator] as indicated:
[…]namespace std::ranges { template<input_range V> requires view<V> template<bool Const> class to_input_view<V>::iterator { […]friend constexpr bool operator==(const iterator& x, const sentinel_t<Base>& y); friend constexpr difference_type operator-(const sentinel_t<Base>& y, const iterator& x) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>; friend constexpr difference_type operator-(const iterator& x, const sentinel_t<Base>& y) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;[…] }; }friend constexpr bool operator==(const iterator& x, const sentinel_t<Base>& y);
-7- Returns:x.current_ == y.friend constexpr difference_type operator-(const sentinel_t<Base>& y, const iterator& x) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
-8- Returns:y - x.current_.friend constexpr difference_type operator-(const iterator& x, const sentinel_t<Base>& y) requires sized_sentinel_for<sentinel_t<Base>, iterator_t<Base>>;
-9- Returns:x.current_ - y.
Add [range.to.input.sentinel] after [range.to.input.iterator] as indicated:
namespace std::ranges { template<input_range V> requires view<V> template<bool Const> class to_input_view<V>::sentinel { using Base = maybe-const<Const, V>; // exposition only sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition only constexpr explicit sentinel(sentinel_t<Base> end); // exposition only public: sentinel() = default; constexpr sentinel(sentinel<!Const> other) requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>; constexpr sentinel_t<Base> base() const; template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y); template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const sentinel& x, const iterator<OtherConst>& y); }; }constexpr explicit sentinel(sentinel_t<Base> end);-?- Effects: Initializes
end_withstd::move(end).constexpr sentinel(sentinel<!Const> other) requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;-?- Effects: Initializes
end_withstd::move(other.end_).constexpr sentinel_t<Base> base() const;-?- Effects: Equivalent to:
return end_;template<bool OtherConst> requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);-?- Effects: Equivalent to:
return x.current_ == y.end_;template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const iterator<OtherConst>& x, const sentinel& y);-?- Effects: Equivalent to:
return x.current_ - y.end_;template<bool OtherConst> requires sized_sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>> friend constexpr range_difference_t<maybe-const<OtherConst, V>> operator-(const sentinel& x, const iterator<OtherConst>& y);-?- Effects: Equivalent to:
return x.end_ - y.current_;
simd<complex>::real/imag is overconstrainedSection: 29.10.8.4 [simd.complex.access] Status: New Submitter: Matthias Kretz Opened: 2025-03-18 Last modified: 2025-08-16
Priority: 2
View all issues with New status.
Discussion:
29.10.8.4 [simd.complex.access] overconstrains the arguments to real and imag.
complex<T>::real/imag allows conversions to T whereas simd<complex<T>>
requires basically an exact match (same_as<simd<T>> modulo ABI tag differences).
complex<double> c = {};
c.real(1.f); // OK
simd<complex<double>> sc = {};
sc.real(simd<float>(1.f)); // ill-formed, should be allowed
The design intent was to match the std::complex<T> interface. In which case
the current wording doesn't match that intent. complex doesn't say real(same_as<T> auto)
but 'real(T)', which allows conversions.
basic_simd(real, imag) constructor. It deduces the type for the
real/imag arguments instead of using a dependent type derived from value_type and ABI tag.
// OK:
complex<double> c{1., 1.f};
// Ill-formed, should be allowed:
simd<complex<double>> sc0(1., 1.);
simd<complex<double>, 4> sc1(simd<double, 4>(1.), simd<float, 4>(1.f));
[2025-06-13; Reflector poll]
Set priority to 2 after reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 29.10.7.1 [simd.overview], class template
basic_simdsynopsis, as indicated:namespace std::datapar { template<class T, class Abi> class basic_simd { public: using value_type = T; using mask_type = basic_simd_mask<sizeof(T), Abi>; using abi_type = Abi; using real-type = rebind_t<typename T::value_type, basic_simd> // exposition-only // 29.10.7.2 [simd.ctor], basic_simd constructors […]template<simd-floating-point V>constexpr explicit(see below) basic_simd(const real-typeV& reals, const real-typeV& imags = {}) noexcept; […] // 29.10.8.4 [simd.complex.access], basic_simd complex-value accessors constexpr real-typeautoreal() const noexcept; constexpr real-typeautoimag() const noexcept;template<simd-floating-point V>constexpr void real(const real-typeV& v) noexcept;template<simd-floating-point V>constexpr void imag(const real-typeV& v) noexcept; […] }; […] }Modify 29.10.7.2 [simd.ctor] as indicated:
template<simd-floating-point V>constexpr explicit(see below) basic_simd(const real-typeV& reals, const real-typeV& imags = {}) noexcept;-19- Constraints:
(19.1) —simd-complex<basic_simd>is modeled., and
(19.2) —V::size() == size()istrue.[…]
-21- Remarks: The expression insideexplicitevaluates tofalseif and only if the floating-point conversion rank ofT::value_typeis greater than or equal to the floating-point conversion rank ofreal-type.V::value_typeModify 29.10.8.4 [simd.complex.access] as indicated:
constexpr real-typeautoreal() const noexcept; constexpr real-typeautoimag() const noexcept;-1- Constraints:
-2- Returns: An object of typesimd-complex<basic_simd>is modeled.real-typewhere therebind_t<typename T::value_type, basic_simd>ith element is initialized to the result ofcmplx-func(operator[](i))for alliin the range[0, size()), wherecmplx-funcis the corresponding function from<complex>.template<simd-floating-point V>constexpr void real(const real-typeV& v) noexcept;template<simd-floating-point V>constexpr void imag(const real-typeV& v) noexcept;-3- Constraints:
(3.1) —simd-complex<basic_simd>is modeled.,
(3.2) —same_as<typename V::value_type, typename T::value_type>is modeled, and
(3.3) —V::size() == size()istrue.[…]
[2025-07-21; Matthias Kretz comments]
The currently shown P/R says:
Remarks: The expression inside
explicitevaluates tofalseif and only if the floating-point conversion rank ofT::value_typeis greater than or equal to the floating-point conversion rank ofreal-type::value_type.
But, by construction, real-type::value_type is the same as T::value_type.
So we get an elaborately worded explicit(false) here (which is correct).
Consequently, the proposed resolution needs to strike explicit(<i>see below</i>)
from 29.10.7.1 [simd.overview] and 29.10.7.2 [simd.ctor] and drop the Remarks paragraph (21).
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.7.1 [simd.overview], class template basic_vec synopsis, as indicated:
namespace std::simd {
template<class T, class Abi> class basic_vec {
public:
using value_type = T;
using mask_type = basic_mask<sizeof(T), Abi>;
using abi_type = Abi;
using iterator = simd-iterator<basic_vec>;
using const_iterator = simd-iterator<const basic_vec>;
using real-type = rebind_t<typename T::value_type, basic_vec> // exposition-only
// 29.10.7.2 [simd.ctor], basic_vec constructors
[…]
template<simd-floating-point V>
constexpr explicit(see below) basic_vec(const real-typeV& reals, const real-typeV& imags = {}) noexcept;
[…]
// 29.10.8.4 [simd.complex.access], basic_vec complex-value accessors
constexpr real-typeauto real() const noexcept;
constexpr real-typeauto imag() const noexcept;
template<simd-floating-point V>
constexpr void real(const real-typeV& v) noexcept;
template<simd-floating-point V>
constexpr void imag(const real-typeV& v) noexcept;
[…]
};
[…]
}
Modify 29.10.7.2 [simd.ctor] as indicated:
template<simd-floating-point V>constexprexplicit(see below)basic_vec(const real-typeV& reals, const real-typeV& imags = {}) noexcept;-19- Constraints:
(19.1) —simd-complex<basic_vec>is modeled., and
(19.2) —V::size() == size()istrue.[…]
-21- Remarks: The expression insideexplicitevaluates tofalseif and only if the floating-point conversion rank ofT::value_typeis greater than or equal to the floating-point conversion rank ofV::value_type.
Modify 29.10.8.4 [simd.complex.access] as indicated:
constexpr real-typeautoreal() const noexcept; constexpr real-typeautoimag() const noexcept;-1- Constraints:
-2- Returns: An object of typesimd-complex<basic_vec>is modeled.real-typewhere therebind_t<typename T::value_type, basic_vec>ith element is initialized to the result ofcmplx-func(operator[](i))for alliin the range[0, size()), wherecmplx-funcis the corresponding function from<complex>.template<simd-floating-point V>constexpr void real(const real-typeV& v) noexcept;template<simd-floating-point V>constexpr void imag(const real-typeV& v) noexcept;-3- Constraints:
(3.1) —simd-complex<basic_vec>is modeled.,
(3.2) —same_as<typename V::value_type, typename T::value_type>is modeled, and
(3.3) —V::size() == size()istrue.[…]
iterator_category correctlySection: 24.5.3.3 [const.iterators.iterator], 25.7 [range.adaptors] Status: New Submitter: Hewill Kang Opened: 2025-03-27 Last modified: 2025-06-12
Priority: 3
View other active issues in [const.iterators.iterator].
View all other issues in [const.iterators.iterator].
View all issues with New status.
Discussion:
Currently, basic_const_iterator, and several range adaptors such as
filter_view's iterators provide iterator_category only when the
underlying iterator models forward_iterator, implying that they expect those
iterators should have a valid iterator_category.
However, this is incorrect because being a forward_iterator does not
necessarily mean it is a Cpp17InputIterator, it just means that it probably
meets the syntactic requirements of Cpp17InputIterator.
Any iterator that specializes iterator_traits and provides only
iterator_concept without iterator_category is not a
Cpp17InputIterator, for example, common_iterator with a
difference_type of integer-class type.
In this case, instantiating these iterator adaptors will result in a hard error because the
iterator_category they expect does not exist. The following illustrates the
problem (demo):
#include <iterator>
#include <ranges>
int main() {
auto r = std::views::iota(0ULL)
| std::views::take(5)
| std::views::common;
static_assert(std::ranges::forward_range<decltype(r)>);
std::basic_const_iterator ci(r.begin()); // 'iterator_category': is not a member of 'std::iterator_traits'
auto f = r | std::views::filter([](auto) { return true; });
auto b = f.begin(); // 'iterator_category': is not a member of 'std::iterator_traits'
}
I believe that checking if the underlying iterator is a forward_iterator is not an appropriate
mechanism to provide iterator_category, but rather checking if its iterator_traits
specialization provides iterator_category.
This issue is somewhat related to LWG 3763(i), which is a further consideration after LWG 3749(i) has been resolved.
[2025-06-12; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
simd_mask<complex<double>>::operator+/-/~ return a disabled simd specializationSection: 29.10.9.4 [simd.mask.unary] Status: New Submitter: Matthias Kretz Opened: 2025-03-27 Last modified: 2025-10-17
Priority: 1
View other active issues in [simd.mask.unary].
View all other issues in [simd.mask.unary].
View all issues with New status.
Discussion:
Consider:
simd<complex<double>> c = {};
simd_mask<complex<double>> k = (c == c); // sizeof(complex<double>) == 16
auto i = -k; // simd<__int128> !
basic_simd_mask unary +, -, and ~ return basic_simd<integer-from<Bytes, Abi>>.
29.10.2.1 [simd.expos.defn]/2 says:
integer-from<Bytes>is an alias for a signed integer typeTsuch thatsizeof(T)equalsBytes.
But __int128 isn't a vectorizable type. Consequently, simd<__int128> currently
is a disabled specialization. So it seems simd<complex<double>> wants __int128
to be added to the list of vectorizable types.
"Specialize" basic_simd_mask<16, Abi> to return
rebind_t<integer-from<8>, basic_simd_mask<integer-from<16>, Abi>>
(reduces a 128-bit value of a vector-mask to a 64-bit value in the simd)
"Specialize" basic_simd_mask<16, Abi> to return
resize_t<size() * 2, rebind_t<integer-from<8>,
basic_simd_mask<integer-from<16>, Abi>>>
(duplicates a 128-bit value of a vector-mask to two 64-bit values in the simd)
delete unary +, -, and ~ for basic_simd_mask<16, Abi>
(closest to the status quo)
[2025-06-12; Reflector poll]
Set priority to 1 after reflector poll.
[2025-10-17; related to 4376(i).]
Proposed resolution:
This wording is relative to N5008.
Modify 29.10.9.4 [simd.mask.unary] as indicated:
constexpr basic_simd_mask operator!() const noexcept; constexpr basic_simd<integer-from<Bytes>, Abi> operator+() const noexcept; constexpr basic_simd<integer-from<Bytes>, Abi> operator-() const noexcept; constexpr basic_simd<integer-from<Bytes>, Abi> operator~() const noexcept;-1- Let
-2- Returns: A data-parallel object where theopbe the operator.ith element is initialized to the results of applyingoptooperator[](i)for alliin the range of[0, size()). -?- Remarks: IfBytesis greater than8,operator+(),operator-(), andoperator~()are deleted.
formattable typeSection: 28.5.6.3 [format.formattable] Status: New Submitter: Hewill Kang Opened: 2025-04-06 Last modified: 2025-06-12
Priority: 2
View other active issues in [format.formattable].
View all other issues in [format.formattable].
View all issues with New status.
Discussion:
User-specific formatters usually have the following form:
template <> struct std::formatter<T> {
constexpr auto parse(format_parse_context& ctx)
-> format_parse_context::iterator;
auto format(const T& value, format_context& ctx) const
-> format_context::iterator;
};
This is reflected in wording examples such as 28.5.6.4 [format.formatter.spec] bullet 8 or 28.5.6.7 [format.context] bullet 9:
#include <format>
#include <string>
enum color { red, green, blue };
const char* color_names[] = { "red", "green", "blue" };
template<> struct std::formatter<color> : std::formatter<const char*> {
auto format(color c, format_context& ctx) const {
return formatter<const char*>::format(color_names[c], ctx);
}
};
which allows us to format color with std::format("{}", red).
Unfortunately, even so, the color still does not satisfy std::formattable.
formattable is currently defined as follows:
template<class T, class Context,
class Formatter = typename Context::template formatter_type<remove_const_t<T>>>
concept formattable-with = // exposition only
semiregular<Formatter> &&
requires(Formatter& f, const Formatter& cf, T&& t, Context fc,
basic_format_parse_context<typename Context::char_type> pc)
{
{ f.parse(pc) } -> same_as<typename decltype(pc)::iterator>;
{ cf.format(t, fc) } -> same_as<typename Context::iterator>;
};
template<class T, class charT>
concept formattable =
formattable-with<remove_reference_t<T>, basic_format_context<fmt-iter-for<charT>, charT>>;
where fmt-iter-for<charT> is an unspecified type that can write
charT, which for char is back_insert_iterator<string>
and char* in libstdc++ and libc++, respectively.
That is, for color to satisfy formattable, it is
necessary to ensure that cf.format(t, fc) is well-formed.
However, the format() function in the above example takes a format_context
whose Out parameter is internal iterator type, namely
__format::_Sink_iter<char> and
back_insert_iterator<__format::__output_buffer<char>> in
libstdc++ and libc++, respectively.
Since basic_format_context with different Out parameters cannot be converted to
each other, the constraint is not satisfied.
The reason color can still be formatted is that basic_format_arg
checks for formattable-with<Context> where Context
has been correctly specified as format_context.
And since color is formattable but not formattable, this further
prevents formatting a range with elements of color, because the formatter
specialization for ranges requires that the element type must be formattable.
This leads to some inconsistencies (demo):
std::println("{}", red); // ok
static_assert(std::formattable<color, char>); // fires
std::vector<color> v;
std::println("{}", v); // not ok
The workaround is to turn the custom format() into a template function
such as format(color c, auto& ctx) or
format(color c, basic_format_context<Out, charT>& ctx),
However, this seems mandate users to always declare format() as the template
function for the best practice, which in my opinion defeats the purpose of introducing
format_context in the first place.
Also, since fmt-iter-for<charT> is unspecified, if it is specified
in some library implementation as the same type as format_context's Out
parameters, then color will suddenly become formattable. This lack
of guarantee about formattable can bring unnecessary confusion.
I think we should ensure that color is formattable, because it is formattable.
[2025-06-12; Reflector poll]
Set priority to 2 after reflector poll.
"This change would prevent future evolution of the API."
"Maybe formatter is not useful and formattable_with should always be used."
Proposed resolution:
This wording is relative to N5008.
Modify 28.5.6.3 [format.formattable] as indicated:
-1-
Let.fmt-iter-for<charT>be an unspecified type that modelsoutput_iterator<const charT&>(24.3.4.10 [iterator.concept.output])[…] template<class T, class charT> concept formattable = formattable-with<remove_reference_t<T>, conditional_t<same_as<charT, char>, format_context, wformat_context>basic_format_context<fmt-iter-for<charT>, charT>>;
ranges::for_each(_n) should be less constrainedSection: 26.6.5 [alg.foreach] Status: LEWG Submitter: Jiang An Opened: 2025-04-08 Last modified: 2025-10-23
Priority: 3
View other active issues in [alg.foreach].
View all other issues in [alg.foreach].
View all issues with LEWG status.
Discussion:
Currently, ranges::for_each(_n) are constrained with indirectly_unary_invocable,
which doesn't meet the actual use of the range elements. These algorithms are only
expected to invoke the callable object with the possibly projected elements, and not
to use any element as the value type. Moreover, indirectly_unary_invocable requires
the callable object to be copy constructible, which might be undesired because the
corresponding std::for_each(_n) only require move constructibilty.
ranges::for_each introduced
by P2609R3. P2609R3 looks like a reasonable fix as long as the affected
algorithms potentially use the intermediate element values copied as
std::iter_value_t<I>. However, when the algorithm is not expected to or
even required not to do this, P2609R3 can bring unexpected impacts. It seems that
constraints around iter_value_t should be avoided for such an algorithm.
[2025-10-23; Reflector poll; Status changed: New → LEWG and P3.]
This is design change.
More algorithms (like count_if, any_of, etc.) that never produce
iter_value_t are affected.
Example of affected code:
std::vector<std::string> v;
// the following not working, constraints not satisfied
std::ranges::for_each(
v | std::views::as_rvalue,
[](std::string&& s) { }
);
Proposed resolution:
This wording is relative to N5008.
Modify 26.4 [algorithm.syn], header <algorithm> synopsis, as indicated:
[…]
namespace ranges {
template<class I, class F>
using for_each_result = in_fun_result<I, F>;
template<input_iterator I, sentinel_for<I> S, class Proj = identity,
indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
constexpr for_each_result<I, Fun>
for_each(I first, S last, Fun f, Proj proj = {});
template<input_range R, class Proj = identity,
indirectly_unary_invocable<projected<iterator_t<R>, Proj>>move_constructible Fun>
requires invocable<Fun&, iter_reference_t<projected<iterator_t<R>, Proj>>>
constexpr for_each_result<borrowed_iterator_t<R>, Fun>
for_each(R&& r, Fun f, Proj proj = {});
}
[…]
namespace ranges {
template<class I, class F>
using for_each_n_result = in_fun_result<I, F>;
template<input_iterator I, class Proj = identity,
indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun>
requires invocable<Fun&, iter_reference_t<projected<I, Proj>>>
constexpr for_each_n_result<I, Fun>
for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});
}
[…]
Modify 26.6.5 [alg.foreach] as indicated:
template<input_iterator I, sentinel_for<I> S, class Proj = identity,indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun> requires invocable<Fun&, iter_reference_t<projected<I, Proj>>> constexpr ranges::for_each_result<I, Fun> ranges::for_each(I first, S last, Fun f, Proj proj = {}); template<input_range R, class Proj = identity,indirectly_unary_invocable<projected<iterator_t<R>, Proj>>move_constructible Fun> requires invocable<Fun&, iter_reference_t<projected<iterator_t<R>, Proj>>> constexpr ranges::for_each_result<borrowed_iterator_t<R>, Fun> ranges::for_each(R&& r, Fun f, Proj proj = {});[…]
-15- [Note 6: The overloads in namespacerangesrequireFunto modelcopy_constructible. — end note][…]
template<input_iterator I, class Proj = identity,indirectly_unary_invocable<projected<I, Proj>>move_constructible Fun> requires invocable<Fun&, iter_reference_t<projected<I, Proj>>> constexpr ranges::for_each_n_result<I, Fun> ranges::for_each_n(I first, iter_difference_t<I> n, Fun f, Proj proj = {});[…]
-30- [Note 11: The overload in namespacerangesrequiresFunto modelcopy_constructible. — end note]
schedule_from and continues_on are flippedSection: 33.9.2 [exec.snd.expos] Status: New Submitter: Eric Niebler Opened: 2025-04-26 Last modified: 2025-06-12
Priority: 1
View other active issues in [exec.snd.expos].
View all other issues in [exec.snd.expos].
View all issues with New status.
Discussion:
The reason for having two different customization points for transitioning between two execution contexts is described in 33.9.2 [exec.snd.expos] bullet (14.1) Note 1, to wit:
[Note 1: The
continues_onalgorithm works in tandem withschedule_from(33.9.12.7 [exec.schedule.from]) to give scheduler authors a way to customize both how to transition onto (continues_on) and off of (schedule_from) a given execution context. Thus,continues_onignores the domain of the predecessor and uses the domain of the destination scheduler to select a customization, a property that is unique tocontinues_on. That is why it is given special treatment here. — end note]
The exposition-only get-domain-late function treats continues_on
senders specially to make sure the correct domain (that of the destination scheduler)
is used to find customizations at connect time.
continues_on and schedule_from are reversed.
continues_on(sndr, sch) is defined as (33.9.12.6 [exec.continues.on]):
transform_sender(get-domain-early(sndr), make-sender(continues_on, sch, sndr))
which is using the domain of the predecessor rather than ignoring it as 33.9.2 [exec.snd.expos] p14.1
says it does. And schedule_from(sch, sndr) is currently defined as (33.9.12.7 [exec.schedule.from]):
transform_sender( query-or-default(get_domain, sch, default_domain()), make-sender(schedule_from, sch, sndr))
which is using the domain of the destination scheduler to find customizations. The logic for determining the domain to use for early customization of these two algorithms are opposite what they are for late customization. This is a bug. They should be consistent.
"Lazy" customization (at connect time) was added to P2300 later in the process, and this inconsistency was a mistake on my part. The correct thing to do is to changeget-domain-late to treat schedule_from as special, not continues_on.
[2025-06-12; Reflector poll]
Set priority to 1 after reflector poll.
"Names are a bit misleading but the change looks right."
Proposed resolution:
This wording is relative to N5008.
Modify 33.9.2 [exec.snd.expos] as indicated:
template<class Sndr, class Env> constexpr auto get-domain-late(const Sndr& sndr, const Env& env) noexcept;-14- Effects: Equivalent to:
(14.1) — If
sender-for<Sndr,iscontinues_on_tschedule_from_t>true, thenreturn Domain();where
Domainis the type of the following expression:[] { auto [_, sch, _] = sndr; return query-or-default(get_domain, sch, default_domain()); }();[Note 1: The
algorithm works in tandem withcontinues_onschedule_from(schedule_fromcontinues_on33.9.12.7 [exec.schedule.from]33.9.12.6 [exec.continues.on]) to give scheduler authors a way to customize both how to transition onto () and off of (continues_onschedule_from) a given execution context. Thus,schedule_fromcontinues_onignores the domain of the predecessor and uses the domain of the destination scheduler to select a customization, a property that is unique tocontinues_onschedule_from. That is why it is given special treatment here. — end note]continues_onschedule_from[…]
lazy_split_viewSection: 25.7.16.3 [range.lazy.split.outer] Status: New Submitter: Hewill Kang Opened: 2025-04-26 Last modified: 2025-10-21
Priority: 2
View other active issues in [range.lazy.split.outer].
View all other issues in [range.lazy.split.outer].
View all issues with New status.
Discussion:
Consider (demo):
#include <print>
#include <ranges>
#include <sstream>
int main() {
std::istringstream is{"1 0 2 0 3"};
auto r = std::views::istream<int>(is)
| std::views::lazy_split(0)
| std::views::stride(2);
std::println("{}", r); // should print [[1], [3]]
}
The above leads to SIGSEGV in libstdc++, the reason is that we are iterating over the nested range as:
for (auto&& inner : r) {
for (auto&& elem : inner) {
// […]
}
}
which is disassembled as:
auto outer_it = r.begin();
std::default_sentinel_t out_end = r.end();
for(; outer_it != out_end; ++outer_it) {
auto&& inner_r = *outer_it;
auto inner_it = inner_r.begin();
std::default_sentinel_t inner_end = inner_r.end();
for(; inner_it != inner_end; ++inner_it) {
auto&& elem = *inner_it;
// […]
}
}
Since inner_it and output_it actually update the same iterator,
when we back to the outer loop, lazy_split_view::outer-iterator
is now equal to default_sentinel, which makes output_it reach the end,
so ++outer_it will increment the iterator past end, triggering the assertion.
Note that this also happens in MSVC-STL
when _ITERATOR_DEBUG_LEVEL is turned on.
It seems that extra flags are needed to fix this issue because output_it should not
be considered to reach the end when we back to the outer loop.
[2025-10-21; Reflector poll.]
Set priority to 2 after reflector poll.
"This is unfortunate. lazy_split is probably not very commonly used,
but handling input ranges was half the reason why we kept it around.
Can we reuse trailing_empty_ for this instead of adding a new flag?
Both flags have the same meaning, and the two cases where they are true
are disjoint: we need has_next_ when iterating through the inner range
exhausted the source range; we need trailing_empty_ when we find a delimiter
at the end of the source range when incrementing the outer iterator,
which by definition means that iterating through the inner range
didn't exhaust it."
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 25.7.16.3 [range.lazy.split.outer] as indicated:
[…]namespace std::ranges { template<input_range V, forward_range Pattern> requires view<V> && view<Pattern> && indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && (forward_range<V> || tiny-range<Pattern>) template<bool Const> struct lazy_split_view<V, Pattern>::outer-iterator { private: using Parent = maybe-const<Const, lazy_split_view>; // exposition only using Base = maybe-const<Const, V>; // exposition only Parent* parent_ = nullptr; // exposition only iterator_t<Base> current_ = iterator_t<Base>(); // exposition only, present only // if V models forward_range bool trailing_empty_ = false; // exposition only bool has_next_ = false; // exposition only, present only // if forward_range<V> is false public: […] }; }constexpr explicit outer-iterator(Parent& parent) requires (!forward_range<Base>);[…]-2- Effects: Initializes
parent_withaddressof(parent)andhas_next_withcurrent != ranges::end(parent_->base_).constexpr outer-iterator& operator++();[…]-6- Effects: Equivalent to:
const auto end = ranges::end(parent_->base_); if (current == end) { trailing_empty_ = false; if constexpr (!forward_range<V>) has_next_ = false; return*this; } const auto [pbegin, pend] = subrange{parent_->pattern_}; if (pbegin == pend) ++current; else if constexpr (tiny-range<Pattern>) { current = ranges::find(std::move(current), end, *pbegin); if (current != end) { ++current; if (current == end) trailing_empty_ = true; } } else { do { auto [b, p] = ranges::mismatch(current, end, pbegin, pend); if (p == pend) { current = b; if (current == end) trailing_empty_ = true; break; // The pattern matched; skip it } } while (++current != end); } if constexpr (!forward_range<V>) if (current == end) has_next_ = false; return *this;friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t);-8- Effects: Equivalent to:
if constexpr (!forward_range<V>) return !x.has_next_ && !x.trailing_empty_; else return x.current == ranges::end(x.parent_->base_) && !x.trailing_empty_;
[2025-10-21; Hewill Kang provides simpler wording]
Proposed resolution:
This wording is relative to N5014.
Modify 25.7.16.3 [range.lazy.split.outer] as indicated:
constexpr outer-iterator& operator++();-6- Effects: Equivalent to:
const auto end = ranges::end(parent_->base_); if (current == end) { trailing_empty_ = false; return*this; } const auto [pbegin, pend] = subrange{parent_->pattern_}; if (pbegin == pend) ++current; else if constexpr (tiny-range<Pattern>) { current = ranges::find(std::move(current), end, *pbegin); if (current != end) { ++current; if (current == end) trailing_empty_ = true; else if constexpr (!forward_range<V>) trailing_empty_ = true; } } else { do { auto [b, p] = ranges::mismatch(current, end, pbegin, pend); if (p == pend) { current = b; if (current == end) trailing_empty_ = true; break; // The pattern matched; skip it } } while (++current != end); } return *this;
swap overloads for indirect and polymorphic only found by ADLSection: 20.4.1.7 [indirect.swap], 20.4.2.7 [polymorphic.swap] Status: LEWG Submitter: Jonathan Wakely Opened: 2025-05-01 Last modified: 2025-10-14
Priority: 2
View all issues with LEWG status.
Discussion:
The non-member swap overloads for std::indirect and std::polymorphic
are defined as hidden friends, so are only available via ADL.
This means that calling std::swap(i1, i2) will always use the generic
std::swap instead of the custom overload for std::indirect.
[2025-10-14; Reflector poll]
Set priority to 2 after reflector poll. Status New → LEWG.
This is a broader issue than just std::indirect and std::polymorphic,
it also affects std::jthread, std::mdspan, move_only_function, copyable_function, node-handle, inplace_vector,
flat_map and co., and maybe more.
Are we comfortable that defining swap as a hidden friend means that
the generic std::swap might be suboptimal,
and for allocator-aware types might even do something with different effects?
e.g. if propagate_on_container_swap is true but
propagate_on_container_move_assignment is false.
Proposed resolution:
This wording is relative to N5008.
Modify 20.2.2 [memory.syn] as indicated:
// 20.4.1, class template indirect template<class T, class Allocator = allocator<T>> class indirect; template<class T, class Allocator> constexpr void swap(indirect<T, Allocator>& lhs, indirect<T, Allocator>& rhs) noexcept(see below); // 20.4.1.10, hash support template<class T, class Alloc> struct hash<indirect<T, Alloc>>; // 20.4.2, class template polymorphic template<class T, class Allocator = allocator<T>> class polymorphic; template<class T, class Allocator> constexpr void swap(polymorphic<T, Allocator>& lhs, polymorphic<T, Allocator>& rhs) noexcept(see below);
Modify 20.4.1.2 [indirect.syn] as indicated:
// 20.4.1.7, swap constexpr void swap(indirect& other) noexcept(see below);friend constexpr void swap(indirect& lhs, indirect& rhs) noexcept(see below);
Modify 20.4.1.7 [indirect.swap] as indicated:
template<class T, class Allocator> constexpr void swap(indirect<T, Allocator>& lhs, indirect<T, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs)));-3- Effects: Equivalent tolhs.swap(rhs).
Modify 20.4.2.2 [polymorphic.syn] as indicated:
// 20.4.2.7, swap constexpr void swap(polymorphic& other) noexcept(see below);friend constexpr void swap(polymorphic& lhs, polymorphic& rhs) noexcept(see below);
Modify 20.4.2.7 [polymorphic.swap] as indicated:
template<class T, class Allocator> constexpr void swap(polymorphic<T, Allocator>& lhs, polymorphic<T, Allocator>& rhs) noexcept(noexcept(lhs.swap(rhs)));-3- Effects: Equivalent tolhs.swap(rhs).
indirect unnecessarily requires copy constructionSection: 99 [indirect.asgn] Status: New Submitter: Jonathan Wakely Opened: 2025-05-01 Last modified: 2025-06-12
Priority: 1
View all issues with New status.
Discussion:
The move assignment operator for indirect says:
Mandates:However, the only way it ever construct an object is:is_copy_constructible_t<T>istrue.
constructs a new owned object with the owned object of other as the argument
as an rvalue
and that only ever happens when alloc == other.alloc
is false.
It seems like we should require is_move_constructible_v instead,
and only if the allocator traits mean we need to construct an object.
(Technically move-constructible might not be correct, because the allocator's
construct member might use a different constructor).
Additionally, the noexcept-specifier for the move assignment doesn't match the effects. The noexcept-specifier says it can't throw if POCMA is true, but nothing in the effects says that ownership can be transferred in that case; we only do a non-throwing transfer when the allocators are equal. I think we should transfer ownership when POCMA is true, which would make the noexcept-specifier correct.
[2025-06-12; Reflector poll]
Set priority to 1 after reflector poll.
Similar change needed for std::polymorphic.
Proposed resolution:
This wording is relative to N5008.
Modify 99 [indirect.asgn] as indicated:
constexpr indirect& operator=(indirect&& other) noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value);-5- Mandates: If
allocator_traits<Allocator>::propagate_on_container_move_assignment::valueisfalseandallocator_traits<Allocator>::is_always_equal::valueisfalse,is_iscopymove_constructible_t<T>true.-6- Effects: If
addressof(other) == thisistrue, there are no effects. Otherwise:
- (6.1) — The allocator needs updating if
allocator_traits<Allocator>::propagate_on_container_move_assignment::valueistrue.- (6.2) — If
otheris valueless,*thisbecomes valuelessand the owned object in.*this, if any, is destroyed usingallocator_traits<Allocator>::destroyand then the storage is deallocated- (6.3) — Otherwise, if the allocator needs updating or if
alloc == other.allocistrue,swaps the owned objects in*thisandother; the owned object inother, if any, is then destroyed usingallocator_traits<Allocator>::destroyand then the storage is deallocated*thistakes ownership of the owned object ofother.- (6.4) — Otherwise, constructs a new owned object with the owned object of
otheras the argument as an rvalue, using either the allocator in*thisor the allocator inotherif the allocator needs updating.- (6.5) — The previously owned object in
*this, if any, is destroyed usingallocator_traits<Allocator>::destroyand then the storage is deallocated.- (6.6) — If the allocator needs updating, the allocator in
*thisis replaced with a copy of the allocator inother.-7- Postcondition:
otheris valueless.
final?Section: 16.4.6.13 [derivation] Status: New Submitter: Jiang An Opened: 2025-04-28 Last modified: 2025-08-29
Priority: 3
View all other issues in [derivation].
View all issues with New status.
Discussion:
Currently, iterator and sentinel types of several views are exposition-only in the standard wording,
and none of them is specified to be final. These types are arguably required to be non-final due to
16.4.6.13 [derivation] p4 because it's possible to say they are specified except for names.
join_view's iterator final for some reasons
(https://reviews.llvm.org/D142811#inline-1383022).
Perhaps we should clarify that the final-ity of exposition-only class is unspecified.
[2025-08-29; Reflector poll]
Set priority to 3 after reflector poll. Votes for P2 (due to implementation divergence), NAD, and LEWG.
Proposed resolution:
This wording is relative to N5008.
Modify 16.4.6.13 [derivation] as indicated:
-4- All types specified in the C++ standard library shall be non-
finaltypes unless otherwise specified. Exposition-only classes (16.3.3.2 [expos.only.entity]) are not considered specified for the purpose offinal.
stride_view::iterator should provide operator->Section: 25.7.32.3 [range.stride.iterator] Status: New Submitter: Hewill Kang Opened: 2025-05-01 Last modified: 2025-10-21
Priority: 3
View all issues with New status.
Discussion:
Currently, only filter_view::iterator and join_view::InnerIter in
<ranges> provide operator->, which makes sense since their operator*
simply dereferences the underlying iterator.
stride_view::iterator, suggesting that providing
operator-> does is the intuitive thing to do, e.g. when wrapping pointers, keeping
operator-> valid so that users can continue to use it->foo().
There is no reason to give up this convenience because stride_view::iterator is intended
to preserve the nature of the underlying iterator.
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 25.7.32.3 [range.stride.iterator] as indicated:
namespace std::ranges {
template<input_range V>
requires view<V>
template<bool Const>
class stride_view<V>::iterator {
using Parent = maybe-const<Const, stride_view>; // exposition only
using Base = maybe-const<Const, V>; // exposition only
iterator_t<Base> current_ = iterator_t<Base>(); // exposition only
sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition only
range_difference_t<Base> stride_ = 0; // exposition only
range_difference_t<Base> missing_ = 0; // exposition only
constexpr iterator(Parent* parent, iterator_t<Base> current, // exposition only
range_difference_t<Base> missing = 0);
public:
[…]
constexpr decltype(auto) operator*() const { return *current_; }
constexpr auto operator->() const
requires has-arrow<iterator_t<Base>> && copyable<iterator_t<Base>>
{ return current_; }
[…]
};
}
Section: 16.4.4.6.1 [allocator.requirements.general] Status: New Submitter: Jiang An Opened: 2025-05-05 Last modified: 2025-10-14
Priority: 4
View other active issues in [allocator.requirements.general].
View all other issues in [allocator.requirements.general].
View all issues with New status.
Discussion:
It seems assumed that a size_t value can be passed to an allocator's allocate member
function per the simple-allocator exposition-only concept in
16.4.4.6.1 [allocator.requirements.general] and the minimal constraints for
allocator types in 23.2.2.2 [container.reqmts].
size_t values, because only allocator_traits<A>::size_type,
which is possibly not size_t, is required to be usable with the allocator.
Do we want to change these constraints, or change the Cpp17Allocator requirements to
require accepting size_t values?
[2025-10-14; Reflector poll]
Set priority to 4 after reflector poll.
"The wording technically allows you to write
allocate(same_as<size_type> auto)
and reject any other type."
"I imagine lots of code relies on std::size_t being accepted.
That's surely a general design issue that's much broader than just the
Cpp17Allocator requirement,
and making the requirement stricter seems evolutionary."
Proposed resolution:
std::basic_string on some platformsSection: 27.4.3.8.2 [string.find] Status: New Submitter: Jiang An Opened: 2025-05-05 Last modified: 2025-10-21
Priority: 3
View other active issues in [string.find].
View all other issues in [string.find].
View all issues with New status.
Discussion:
P1148R0 respecified the searching functions of std::basic_string to return
corresponding string view type's npos member constant (equal to std::size_t(-1)), converted
to the string type S's member S::size_type, when the search fails. Before the change,
S::npos (equal to S::size_type(-1)) was returned on failure.
std::size_t isn't the widest unsigned integer type (e.g. on usual 32-bit
platforms), the return value can change. Because there can be an allocator with a wider size_type,
and when the basic_string type S uses such an allocator, S::size_type is specified to be that
type, which in turn makes S::size_type(std::size_t(-1)) not equal to S::size_type(-1).
Do we want to restore the old return values?
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 27.4.3.8.2 [string.find] as indicated:
template<class T> constexpr size_type find(const T& t, size_type pos = 0) const noexcept(see below); […] template<class T> constexpr size_type find_last_not_of(const T& t, size_type pos = npos) const noexcept(see below);-2- Constraints: […]
-3- Effects: LetGbe the name of the function. Equivalent to:basic_string_view<charT, traits> s = *this, sv = t;return s.G(sv, pos);if (auto result = s.G(sv, pos); result == size_t(-1)) return npos; else return result;
[2025-06-10, reflector discussion]
During reflector discussion of this issue there was a preference to adjust the
proposed wording to use s.npos instead of size_t(-1).
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 27.4.3.8.2 [string.find] as indicated:
template<class T> constexpr size_type find(const T& t, size_type pos = 0) const noexcept(see below); […] template<class T> constexpr size_type find_last_not_of(const T& t, size_type pos = npos) const noexcept(see below);-2- Constraints: […]
-3- Effects: LetGbe the name of the function. Equivalent to:basic_string_view<charT, traits> s = *this, sv = t;return s.G(sv, pos);if (auto result = s.G(sv, pos); result == s.npos) return npos; else return result;
Section: 33.2.1 [exec.queryable.general] Status: New Submitter: Eric Niebler Opened: 2025-05-07 Last modified: 2025-10-23
Priority: 2
View all issues with New status.
Discussion:
Imported from cplusplus/sender-receiver #333.
We require the types of query objects such asget_scheduler to be customization point objects.
16.3.3.3.5 [customization.point.object] requires them to be semiregular but that concept
does not require default constructability. Much of std::execution assumes query object types
to be default constructible.
I propose adding a (nothrow) default-constructibility requirement.
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
"The discussion is wrong, semiregular requires default_initializable.
If we want to mandate nothrow construction
(a.k.a the implementation isn't out to get you),
I'd rather we do it for all CPOs."
Proposed resolution:
This wording is relative to N5008.
Modify 33.2.1 [exec.queryable.general] as indicated:
-1- A queryable object is a read-only collection of key/value pair where each key is a customization point object known as a query object. The type of a query object satisfies
default_initializable, and its default constructor is not potentially throwing. A query is an invocation of a query object with a queryable object as its first argument and a (possibly empty) set of additional arguments. A query imposes syntactic and semantic requirements on its invocations.
Section: 27.4.3.8.2 [string.find] Status: New Submitter: Jiang An Opened: 2025-05-07 Last modified: 2025-10-17
Priority: 3
View other active issues in [string.find].
View all other issues in [string.find].
View all issues with New status.
Discussion:
P1206R7 added some requirements for sequence containers, including program-defined ones.
These requirements mean that if a custom C++20 sequence container type has no newly required member functions added in C++23 mode, it is no longer a sequence container, and uses of it withstd::stack, std::queue, or std::priority_queue possibly have undefined behavior or
make the program ill-formed. Additionally, some users want to use C++23 flat container adaptors
with old sequence containers (llvm/llvm-project#136656),
it is unclear whether such uses are intentionally rejected.
Perhaps the requirements on from_range_t constructor, insert_range and assign_range should be made optional,
although these operations should have specified semantics whenever being well-formed in immediate contexts.
[2025-10-17; Reflector poll.]
Set priority to 3 after reflector poll.
"We keep adding container requirements, cleaning up the adaptors might need a paper."
Proposed resolution:
copy_if, remove_copy, remove_copy_if, unique_copy have too strong preconditionsSection: 26.7.1 [alg.copy], 26.7.8 [alg.remove], 26.7.9 [alg.unique] Status: New Submitter: Alex Guteniev Opened: 2025-05-12 Last modified: 2025-10-21
Priority: 3
View other active issues in [alg.copy].
View all other issues in [alg.copy].
View all issues with New status.
Discussion:
26.7.1 [alg.copy]/16 , 26.7.8 [alg.remove]/11, 26.7.9 [alg.unique]/8.1 all say:
Preconditions: The ranges
[first, last)and[result, result + (last - first))do not overlap.
These algorithms may produce fewer elements than (last - first). If this is known in advance,
a smaller output range may be used, so that the precondition will not be satisfied.
std::array<S, 4> in = {{{4, 2}, {1, 3}, {3, 4}, {3, 5}}};
std::array<S, 2> out;
auto ret = std::ranges::copy_if(in.begin(), in.end(), out.begin(), [](int i) { return i == 3; }, &S::val);
I think there should be a weaker precondition, like 26.7.1 [alg.copy]/2:
Preconditions:
resultis not in the range[first, last).
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
"The proposed resolution is incorrect, because the output iterator could ‘enter’ the range [first, last) via being incremented. If the output iterator is a reverse iterator, it starts writing to the end of [first,last) before we need to read from there."
"NAD - nothing is broken with the existing constraint. Relaxing it is a design change."
"NAD. The right behavior, if the algorithm runs out of output space, is to return the last processed input iterator and the "next spot" output iterator. This is what P3179 (parallel ranges algorithms) does with its range-as-output algorithms. I would not want the proposed resolution without this change. Otherwise, we would be introducing new UB unnecessarily."
Proposed resolution:
This wording is relative to N5008.
Modify 26.7.1 [alg.copy] as indicated:
template<class InputIterator, class OutputIterator, class Predicate> constexpr OutputIterator copy_if(InputIterator first, InputIterator last, OutputIterator result, Predicate pred); […] template<input_range R, weakly_incrementable O, class Proj = identity, indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred> requires indirectly_copyable<iterator_t<R>, O> constexpr ranges::copy_if_result<borrowed_iterator_t<R>, O> ranges::copy_if(R&& r, O result, Pred pred, Proj proj = {});-15- Let
-16 Preconditions:Ebe: […]resultis not in the range[first, last)The ranges.[first, last)and[result, result + (last - first))do not overlap
Modify 26.7.8 [alg.remove] as indicated:
template<class InputIterator, class OutputIterator, class T = iterator_traits<InputIterator>::value_type> constexpr OutputIterator remove_copy(InputIterator first, InputIterator last, OutputIterator result, const T& value); […] template<input_range R, weakly_incrementable O, class Proj = identity, indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred> requires indirectly_copyable<iterator_t<R>, O> constexpr ranges::remove_copy_if_result<borrowed_iterator_t<R>, O> ranges::remove_copy_if(R&& r, O result, Pred pred, Proj proj = {});-8- Let
[…] -11 Preconditions:Ebe […]resultis not in the range[first, last)The ranges.[first, last)and[result, result + (last - first))do not overlap
Modify 26.7.9 [alg.unique] as indicated:
template<class InputIterator, class OutputIterator> constexpr OutputIterator unique_copy(InputIterator first, InputIterator last, OutputIterator result); […] template<input_range R, weakly_incrementable O, class Proj = identity, indirect_equivalence_relation<projected<iterator_t<R>, Proj>> C = ranges::equal_to> requires indirectly_copyable<iterator_t<R>, O> && (forward_iterator<iterator_t<R>> || (input_iterator<O> && same_as<range_value_t<R>, iter_value_t<O>>) || indirectly_copyable_storable<iterator_t<R>, O>) constexpr ranges::unique_copy_result<borrowed_iterator_t<R>, O> ranges::unique_copy(R&& r, O result, C comp = {}, Proj proj = {});-6- Let
-7- Mandates: […] -8 Preconditions:predbe […]
(8.1) —
resultis not in the range[first, last)The ranges.[first, last)and[result, result + (last - first))do not overlap(8.2) — […]
std::format_to etc. behave when the output is overlong?Section: 28.5 [format] Status: New Submitter: Jiang An Opened: 2025-05-14 Last modified: 2025-10-17
Priority: 3
View other active issues in [format].
View all other issues in [format].
View all issues with New status.
Discussion:
It seems permitted to write overly long contents via an (C++20) output iterator with
std::format_to(_n), where the length isn't representable in size_t or ptrdiff_t,
especially when the implementation provides integer-class types. However, currently
some implementation can't properly handle content whose length is greater than
PTRDIFF_MAX. Presumably we don't want UB in such cases. Should we explicitly allow
throwing exception and/or silent truncation?
std::formatted_size is std::size_t,
even if the implementation supports formatted contents whose lengths are greater than
SIZE_MAX, the length can't be correctly returned. Perhaps we need to either precisely
specify the return value as modulo arithmetic seems undesired, or specify that an
exception is thrown.
[2025-10-17; Reflector poll.]
Set priority to 3 after reflector poll.
Victor thinks it should be an exception.
Proposed resolution:
function_refSection: 22.10.17.1 [func.wrap.general] Status: New Submitter: Tomasz Kamiński Opened: 2025-05-15 Last modified: 2025-10-21
Priority: 2
View all issues with New status.
Discussion:
Currently the wording in 22.10.17.1 [func.wrap.general] allows implementation to avoid double indirection when constructing owning functions wrappers from another one:
-2- Let
tbe an object of a type that is a specialization offunction,copyable_function, ormove_only_function, such that the target objectxofthas a type that is a specialization offunction,copyable_function, ormove_only_function. Each argument of the invocation ofxevaluated as part of the invocation oftmay alias an argument in the same position in the invocation oftthat has the same type, even if the corresponding parameter is not of reference type.
However, the wording does not cover a function_ref, disallowing implementation to perform
this optimization when signatures are compatible, for example:
std::function_ref<void() noexcept> f1(ptr); std::function_ref<void()> f1(f2);
We should include function_ref in the list. Note that this allows, but does not require,
an implementation to perform such an optimization. As a consequence, it is acceptable
to specify the allowance for all combinations of polymorphic wrappers, even for creating an
owning wrapper from a non-owning one, where implementing such an optimization may not be possible.
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 22.10.17.1 [func.wrap.general] as indicated:
-2- Let
tbe an object of a type that is a specialization offunction,copyable_function,ormove_only_function, orfunction_ref, such that the target objectxofthas a type that is a specialization offunction,copyable_function,ormove_only_function, orfunction_ref. Each argument of the invocation ofxevaluated as part of the invocation oftmay alias an argument in the same position in the invocation oftthat has the same type, even if the corresponding parameter is not of reference type.
[2024-05-21; Tomasz's comment and upates proposed resolution]
After implementing double indirection avoidance in the libstdc++, I have realized that above wording change is insufficient to cover all user observable effects of the change. Revelant quote from the Avoid double indirection in function_ref from libstdc++ mailing lists:
To avoidance of double indirection requires that constructed
function_ref, refers directly to the target function of the source, instead of source, and this is visible after the assigment:void foo() noexcept; void bar() noexcept; std::function_ref<void() noexcept> sr(&foo); std::function_ref<void()> dr(sr); dr(); // callsfooregardless of implementation sr = &bar; sr(); // callsbardr(); // still callsfooif we avoid indirection, // callsbarif we do notSimilary for
move_only_function/copyable_functionsource:std::move_only_function<void()> sm; std::function_ref<void()> dm(sm); dm(); // UB becausesmis empty sm = &foo; dm(); // remains UB if we avoid indirection, // callsbarif we do not.While we may want to allow skipping indirection for function_ref, as this produces same behavior as in case for copy constructor (matching signatures):
void foo() noexcept; void bar() noexcept; std::function_ref<void() noexcept> sr(&foo); std::function_ref<void() noexcept> dr(sr); // copy-cosntructor dr(); // callsfooregardless of implementation sr = &bar; sr(); // callsbardr(); // still callsfooif we avoid indirectionI do not think this is acceptable for
move_only_function. …Note that for the same reason, implementations are not free to avoid dangling when constructing
function_reffromreference_wrapper:auto srw = std::ref(&foo); std::function_ref<void()> drw(srw); drw(); // callsfoosrw = std::ref(&bar); drw(); // callsfooif we unwrap referenc wrapper, // callsbarotherwise.Note that this is limited to
function_refdue reference nature of this wrapper.
The updated resolution allows indirection but making it unspecified if
function_ref constructed from other function_ref specialization,
will refer to source object or its target.
[2025-10-21; Reflector poll.]
Set priority to 2 after reflector poll.
Strong oposition to making it unspecified whether function_ref constructed from
other function_ref will reference source object or its target directly.
Proposed resolution:
This wording is relative to N5008.
Modify 22.10.17.1 [func.wrap.general] as indicated:
-2- Let
tbe an object of a type that is a specialization offunction,copyable_function,ormove_only_function, orfunction_ref, such that the target objectxofthas a type that is a specialization offunction,copyable_function,ormove_only_function, orfunction_ref. Each argument of the invocation ofxevaluated as part of the invocation oftmay alias an argument in the same position in the invocation oftthat has the same type, even if the corresponding parameter is not of reference type.
Modify 22.10.17.6.3 [func.wrap.ref.ctor] as indicated:
template<class F> constexpr function_ref(F&&) noexcept;[…]-7- Effects: Initializes
bound-entitywithaddressof(f)andthunk-ptrwith the address of a functionthunksuch thatthunk(bound-entity, call-args...)is expression-equivalent (3.22 [defns.expression.equivalent]) toinvoke_r<R>(static_cast<cv T&>(f), call-args...).-?- Remarks: If
remove_cveref_t<F>is a specialization offunction_refan implementation may initializebound-entitywithbound-entityoff. [Example::void f1() noexcept; void f2() noexcept; function_ref<void() noexcept> r1(&r1); function_ref<void()> r2(r1); r1 = &f2; f2(); // it is unspecified iff1orf2is invoked— end example]
Section: 22.4.4.2 [tuple.cnstr] Status: New Submitter: Jiang An Opened: 2025-05-24 Last modified: 2025-10-17
Priority: 3
View other active issues in [tuple.cnstr].
View all other issues in [tuple.cnstr].
View all issues with New status.
Discussion:
Per 20.2.8.2 [allocator.uses.construction]/1, uses-allocator construction is only defined for objects.
And presumably, an attempt to construct std::tuple of reference from an allocator_arg_t constructor
causes a hard error.
allocator_arg_t constructors are conditionally
deleted according to 22.4.4.2 [tuple.cnstr]/33. However, it's confusing that these constructors are
sometimes non-deleted when the tuple contains a reference, while there are hard errors in an instantiation
instead.
[2025-10-14; Reflector poll]
Set priority to 3 after reflector poll.
"There is a defect, but the right fix is to just define uses-allocator construction of references as equivalent to normal construction."
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 22.4.4.2 [tuple.cnstr] as indicated:
template<class Alloc> constexpr explicit(see below) tuple(allocator_arg_t, const Alloc& a); […] template<class Alloc, tuple-like UTuple> constexpr explicit(see below) tuple(allocator_arg_t, const Alloc& a, UTuple&&);-32- Preconditions:
-33- Effects: Equivalent to the preceding constructors except that each element is constructed with uses-allocator construction (20.2.8.2 [allocator.uses.construction]). -?- Remarks: These constructors are defined as deleted ifAllocmeets the Cpp17Allocator requirements (16.4.4.6.1 [allocator.requirements.general]).is_reference_v<Ti>istruefor at least oneTi.
[2025-10-15; Jonathan provides new wording]
I don't think we care about uses-allocator construction of references in general.
You can't construct a reference using allocator_traits::construct nor put references in containers.
For a std::pair containing a reference, I think make_obj_using_allocator and uses_allocator_construction_args work fine.
The problem only exists in the std::tuple wording where we say that the elements are constructed using uses-allocator construction.
So let's just fix that.
Proposed resolution:
This wording is relative to N5014.
Modify 22.4.4.2 [tuple.cnstr] as indicated:
template<class Alloc> constexpr explicit(see below) tuple(allocator_arg_t, const Alloc& a); […] template<class Alloc, tuple-like UTuple> constexpr explicit(see below) tuple(allocator_arg_t, const Alloc& a, UTuple&&);-32- Preconditions:
-33- Effects: Equivalent to the preceding constructors except that each element of non-reference type is constructed with uses-allocator construction (20.2.8.2 [allocator.uses.construction]).Allocmeets the Cpp17Allocator requirements (16.4.4.6.1 [allocator.requirements.general]).
function<void()> suppresses nodiscard warningsSection: 22.10.4 [func.require] Status: New Submitter: Jonathan Wakely Opened: 2025-05-29 Last modified: 2025-10-21
Priority: 3
View other active issues in [func.require].
View all other issues in [func.require].
View all issues with New status.
Discussion:
struct [[nodiscard]] A { };
A f();
std::function<void()> func = f;
Invoking func() will discard the return value of f(), but there will be
no warning. This is because INVOKE<void>(...)
is defined in terms of static_cast<void>(...) and the
explicit cast to void suppresses nodiscard warnings.
This is in contast to INVOKE<R>(...) where the
conversion to non-void R is implicit.
It seems right that std::invoke_r<void>(f) should not give
nodiscard warnings, because that's quite explicit about converting to void,
and similarly for std::bind<void>(f)().
However, I think it's debatable whether all uses of INVOKE<void> (and std::function<void()> in particular)
intend an explicit cast to void that ignores nodiscard types.
It's very easy to set f as the target of func and then lose its warning,
and there's no explicit use of void when you write func = f; func();.
We could consider defining INVOKE<void>(...) to be
an expression of type void, without explicitly saying there's a cast to void.
For example, (INVOKE(...), void()) would invoke the invocable and have type
void, but would not require any nodiscard warnings to be suppressed.
If we did that, some uses of INVOKE<R> such as
std::invoke_r and std::bind<R> might need to
be adjusted to preserve the explicit conversion to void.
That would allow us to be selective about which uses of
INVOKE<void> we consider to be explicit about
discarding results, and which we don't.
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
Split opinions between considering this NAD due warnings being QoI matter,
and explicit use static_cast<void> indicating that any
nodiscard warning should be silenced.
Concerns where expressed that removing static_assert is insufficient,
as warnings may be still suppressed by heuristics that remove warnings
originating from system headers.
Proposed resolution:
std::projected::operator*Section: 24.3.6.4 [projected] Status: New Submitter: Jiang An Opened: 2025-05-30 Last modified: 2025-10-20
Priority: 3
View all other issues in [projected].
View all issues with New status.
Discussion:
std::projected::operator* is only intentionally used for type calculation like std::declval.
Currently, if one attempts to call it in a potentially evaluated expression, the program is ill-formed,
no diagnostic required because the operator* is not defined.
std::declval
(22.2.6 [declval]/2). Implementation used to perform such misuse via std::ranges::iter_move,
but libc++ and libstdc++ have already switched not to do this (see
microsoft/STL#5555).
[2025-10-20; Reflector poll.]
Set priority to 3 after reflector poll.
"NAD, the standard is clear that it can't be used."
Proposed resolution:
This wording is relative to N5008.
Modify 24.3.6.4 [projected] as indicated:
namespace std { template<class I, class Proj> struct projected-impl { // exposition only struct type { // exposition only […] // models weakly_incrementable indirect_result_t<Proj&, I> operator*() const;// not defined}; }; […] }indirect_result_t<Proj&, I> operator*() const;-?- Mandates: This function is not odr-used (6.3 [basic.def.odr]).
rank == 0, layout_stride is atypically convertibleSection: 23.7.3.4 [mdspan.layout] Status: New Submitter: Luc Grosheintz Opened: 2025-06-02 Last modified: 2025-08-29
Priority: 2
View other active issues in [mdspan.layout].
View all other issues in [mdspan.layout].
View all issues with New status.
Discussion:
Commonly, two layouts are considered convertible, if the underlying
extent_types are convertible.
layout_left::mapping(layout_stride::mapping) and
layout_right::mapping(layout_stride::mapping), the condition is rank > 0.
Therefore,
using E1 = std::extents<int>;
using E2 = std::extents<unsigned int>;
static_assert(std::is_convertible_v<
std::layout_stride::mapping<E2>,
std::layout_right::mapping<E1>
>);
even though:
static_assert(!std::is_convertible_v<E2, E1>);
Moreover, for rank 0 layout_stride can be converted to any
specialization of layout_left or layout_right; but not to every
specialization of layout_stride.
[2025-06-12; Reflector poll]
Set priority to 2 after reflector poll.
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
[Drafting note: As drive-by fixes the edits for
layout_left_padded<>::mappingandlayout_right_padded<>::mappingalso correct an editorial asymmetry between class header synopsis declaration form and prototype specification form of the corresponding constructors and adjust to the correct formatting of the exposition-only data memberrank_.]
Modify 23.7.3.4.5.1 [mdspan.layout.left.overview] as indicated:
namespace std { template<class Extents> class layout_left::mapping { […] // 23.7.3.4.5.2 [mdspan.layout.left.cons], constructors […] template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>&); constexpr mapping& operator=(const mapping&) noexcept = default; […] }; }Modify 23.7.3.4.5.2 [mdspan.layout.left.cons] as indicated:
template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-13- Constraints: […]
-14- Preconditions: […] -15- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(extents_type::rank() == 0 && is_convertible_v<OtherExtents, extents_type>)Modify 23.7.3.4.6.1 [mdspan.layout.right.overview] as indicated:
namespace std { template<class Extents> class layout_right::mapping { […] // 23.7.3.4.6.2 [mdspan.layout.right.cons], constructors […] template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>&); constexpr mapping& operator=(const mapping&) noexcept = default; […] }; }Modify 23.7.3.4.6.2 [mdspan.layout.right.cons] as indicated:
template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-13- Constraints: […]
-14- Preconditions: […] -15- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(extents_type::rank() == 0 && is_convertible_v<OtherExtents, extents_type>)Modify 23.7.3.4.8.1 [mdspan.layout.leftpad.overview] as indicated:
namespace std { template<size_t PaddingValue> template<class Extents> class layout_left_padded<PaddingValue>::mapping { […] // 23.7.3.4.8.3 [mdspan.layout.leftpad.cons], constructors […] template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>&); […] }; }Modify 23.7.3.4.8.3 [mdspan.layout.leftpad.cons] as indicated:
template<class OtherExtents> constexpr explicit(rank_ > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-10- Constraints: […]
-11- Preconditions: […] -12- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(rank_ == 0 && is_convertible_v<OtherExtents, extents_type>)Modify 23.7.3.4.9.1 [mdspan.layout.rightpad.overview] as indicated:
namespace std { template<size_t PaddingValue> template<class Extents> class layout_right_padded<PaddingValue>::mapping { […] // 23.7.3.4.9.3 [mdspan.layout.rightpad.cons], constructors […] template<class OtherExtents> constexpr explicit(rank_ > 0see below) mapping(const layout_stride::mapping<OtherExtents>&); […] }; }Modify 23.7.3.4.9.3 [mdspan.layout.rightpad.cons] as indicated:
template<class OtherExtents> constexpr explicit(rank_ > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-10- Constraints: […]
-11- Preconditions: […] -12- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(rank_ == 0 && is_convertible_v<OtherExtents, extents_type>)
[2025-06-20, Luc Grosheintz provides further wording improvements]
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
[Drafting note: As drive-by fixes the edits for
layout_left_padded<>::mappingandlayout_right_padded<>::mappingalso correct an editorial asymmetry between class header synopsis declaration form and prototype specification form of the corresponding constructors and adjust to the correct formatting of the exposition-only data memberrank_.]
Modify 23.7.3.4.5.1 [mdspan.layout.left.overview] as indicated:
namespace std { template<class Extents> class layout_left::mapping { […] // 23.7.3.4.5.2 [mdspan.layout.left.cons], constructors […] template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>&); constexpr mapping& operator=(const mapping&) noexcept = default; […] }; }Modify 23.7.3.4.5.2 [mdspan.layout.left.cons] as indicated:
template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-13- Constraints: […]
-14- Preconditions: […] -15- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(extents_type::rank() == 0 && is_convertible_v<OtherExtents, extents_type>)Modify 23.7.3.4.6.1 [mdspan.layout.right.overview] as indicated:
namespace std { template<class Extents> class layout_right::mapping { […] // 23.7.3.4.6.2 [mdspan.layout.right.cons], constructors […] template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>&); constexpr mapping& operator=(const mapping&) noexcept = default; […] }; }Modify 23.7.3.4.6.2 [mdspan.layout.right.cons] as indicated:
template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-13- Constraints: […]
-14- Preconditions: […] -15- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(extents_type::rank() == 0 && is_convertible_v<OtherExtents, extents_type>)Modify 23.7.3.4.8.1 [mdspan.layout.leftpad.overview] as indicated:
namespace std { template<size_t PaddingValue> template<class Extents> class layout_left_padded<PaddingValue>::mapping { […] // 23.7.3.4.8.3 [mdspan.layout.leftpad.cons], constructors […] template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>&); […] }; }Modify 23.7.3.4.8.3 [mdspan.layout.leftpad.cons] as indicated:
template<class OtherExtents> constexpr explicit(rank_ > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-10- Constraints: […]
-11- Preconditions: […] -12- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(rank_ == 0 && is_convertible_v<OtherExtents, extents_type>)template<class LayoutLeftPaddedMapping> constexpr explicit(see below) mapping(const LayoutLeftPaddedMapping& other);-13- Constraints: […]
[…] -16- Remarks: The expression insideexplicitis equivalent to:!is_convertible_v<typename LayoutLeftPaddedMapping::extents_type, extents_type> && rank_> 1 && (padding_value != dynamic_extent || LayoutLeftPaddedMapping::padding_value == dynamic_extent)Modify 23.7.3.4.9.1 [mdspan.layout.rightpad.overview] as indicated:
namespace std { template<size_t PaddingValue> template<class Extents> class layout_right_padded<PaddingValue>::mapping { […] // 23.7.3.4.9.3 [mdspan.layout.rightpad.cons], constructors […] template<class OtherExtents> constexpr explicit(rank_ > 0see below) mapping(const layout_stride::mapping<OtherExtents>&); […] }; }Modify 23.7.3.4.9.3 [mdspan.layout.rightpad.cons] as indicated:
template<class OtherExtents> constexpr explicit(rank_ > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-10- Constraints: […]
-11- Preconditions: […] -12- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(rank_ == 0 && is_convertible_v<OtherExtents, extents_type>)template<class LayoutRightPaddedMapping> constexpr explicit(see below) mapping(const LayoutRightPaddedMapping& other);-13- Constraints: […]
[…] -17- Remarks: The expression insideexplicitis equivalent to:!is_convertible_v<typename LayoutRightPaddedMapping::extents_type, extents_type> && rank_ > 1 && (padding_value != dynamic_extent || LayoutRightPaddedMapping::padding_value == dynamic_extent)
[2025-09-27, Tomasz Kamiński fixes constraints in constructors from padded layouts]
Proposed resolution:
This wording is relative to N5008.
[Drafting note: As drive-by fixes the edits for
layout_left_padded<>::mappingandlayout_right_padded<>::mappingalso correct an editorial asymmetry between class header synopsis declaration form and prototype specification form of the corresponding constructors and adjust to the correct formatting of the exposition-only data memberrank_.]
Modify 23.7.3.4.5.1 [mdspan.layout.left.overview] as indicated:
namespace std {
template<class Extents>
class layout_left::mapping {
[…]
// 23.7.3.4.5.2 [mdspan.layout.left.cons], constructors
[…]
template<class OtherExtents>
constexpr explicit(extents_type::rank() > 0see below)
mapping(const layout_stride::mapping<OtherExtents>&);
constexpr mapping& operator=(const mapping&) noexcept = default;
[…]
};
}
Modify 23.7.3.4.5.2 [mdspan.layout.left.cons] as indicated:
template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-13- Constraints: […]
-14- Preconditions: […] -15- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(extents_type::rank() == 0 && is_convertible_v<OtherExtents, extents_type>)
Modify 23.7.3.4.6.1 [mdspan.layout.right.overview] as indicated:
namespace std {
template<class Extents>
class layout_right::mapping {
[…]
// 23.7.3.4.6.2 [mdspan.layout.right.cons], constructors
[…]
template<class OtherExtents>
constexpr explicit(extents_type::rank() > 0see below)
mapping(const layout_stride::mapping<OtherExtents>&);
constexpr mapping& operator=(const mapping&) noexcept = default;
[…]
};
}
Modify 23.7.3.4.6.2 [mdspan.layout.right.cons] as indicated:
template<class OtherExtents> constexpr explicit(extents_type::rank() > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-13- Constraints: […]
-14- Preconditions: […] -15- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(extents_type::rank() == 0 && is_convertible_v<OtherExtents, extents_type>)
Modify 23.7.3.4.8.1 [mdspan.layout.leftpad.overview] as indicated:
namespace std {
template<size_t PaddingValue>
template<class Extents>
class layout_left_padded<PaddingValue>::mapping {
[…]
// 23.7.3.4.8.3 [mdspan.layout.leftpad.cons], constructors
[…]
template<class OtherExtents>
constexpr explicit(extents_type::rank() > 0see below)
mapping(const layout_stride::mapping<OtherExtents>&);
[…]
};
}
Modify 23.7.3.4.8.3 [mdspan.layout.leftpad.cons] as indicated:
template<class OtherExtents> constexpr explicit(rank_ > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-10- Constraints: […]
-11- Preconditions: […] -12- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(rank_ == 0 && is_convertible_v<OtherExtents, extents_type>)template<class LayoutLeftPaddedMapping> constexpr explicit(see below) mapping(const LayoutLeftPaddedMapping& other);-13- Constraints: […]
[…] -16- Remarks: The expression insideexplicitis equivalent to:!is_convertible_v<typename LayoutLeftPaddedMapping::extents_type, extents_type> || rank_> 1 && (padding_value != dynamic_extent || LayoutLeftPaddedMapping::padding_value == dynamic_extent)
Modify 23.7.3.4.9.1 [mdspan.layout.rightpad.overview] as indicated:
namespace std {
template<size_t PaddingValue>
template<class Extents>
class layout_right_padded<PaddingValue>::mapping {
[…]
// 23.7.3.4.9.3 [mdspan.layout.rightpad.cons], constructors
[…]
template<class OtherExtents>
constexpr explicit(rank_ > 0see below)
mapping(const layout_stride::mapping<OtherExtents>&);
[…]
};
}
Modify 23.7.3.4.9.3 [mdspan.layout.rightpad.cons] as indicated:
template<class OtherExtents> constexpr explicit(rank_ > 0see below) mapping(const layout_stride::mapping<OtherExtents>& other);-10- Constraints: […]
-11- Preconditions: […] -12- Effects: […] -?- Remarks: The expression insideexplicitis equivalent to:!(rank_ == 0 && is_convertible_v<OtherExtents, extents_type>)template<class LayoutRightPaddedMapping> constexpr explicit(see below) mapping(const LayoutRightPaddedMapping& other);-13- Constraints: […]
[…] -17- Remarks: The expression insideexplicitis equivalent to:!is_convertible_v<typename LayoutRightPaddedMapping::extents_type, extents_type> || rank_ > 1 && (padding_value != dynamic_extent || LayoutRightPaddedMapping::padding_value == dynamic_extent)
Section: 26.3.6 [execpol] Status: New Submitter: Jiang An Opened: 2025-06-03 Last modified: 2025-10-21
Priority: 3
View all issues with New status.
Discussion:
Existing standard execution policy types don't hold any state, and mostly act as disambiguating tags in parallel algorithms. Perhaps it will be better to ensure them to be similar to construction tag types, which possibly enables more desired usages.
Currently, libc++ makes these types non-movable, which is arguable undesired per the decision made in P3136R1.[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
"Not seeing the point of this change. For code dealing with generic policies, it shouldn't be constructing new objects of the policy type (there could be implementation-defined policies, or ones added in future standards, which are not default-constructible). For code using a specific policy, just using the named variable is strictly less typing than constructing the type. Maybe NAD or LEWG." "Making them tag types helps e.g. P3481 where we have to store execution policies, and in P2500." "Don't you want copy constructible, not default constructible?"
Proposed resolution:
This wording is relative to N5008.
Modify 26.3.6.3 [execpol.seq] as indicated:
class execution::sequenced_policy { unspecified
public:
explicit sequenced_policy() = default;
};
Modify 26.3.6.4 [execpol.par] as indicated:
class execution::parallel_policy { unspecified
public:
explicit parallel_policy() = default;
};
Modify 26.3.6.5 [execpol.parunseq] as indicated:
class execution::parallel_unsequenced_policy { unspecified
public:
explicit parallel_unsequenced_policy() = default;
};
Modify 26.3.6.6 [execpol.unseq] as indicated:
class execution::unsequenced_policy { unspecified
public:
explicit unsequenced_policy() = default;
};
Modify 26.3.6.7 [execpol.objects] as indicated:
inline constexpr execution::sequenced_policy execution::seq{ unspecified };
inline constexpr execution::parallel_policy execution::par{ unspecified };
inline constexpr execution::parallel_unsequenced_policy execution::par_unseq{ unspecified };
inline constexpr execution::unsequenced_policy execution::unseq{ unspecified };
Section: 26.2 [algorithms.requirements] Status: New Submitter: Jan Schultke Opened: 2025-06-08 Last modified: 2025-10-21
Priority: 4
View other active issues in [algorithms.requirements].
View all other issues in [algorithms.requirements].
View all issues with New status.
Discussion:
26.2 [algorithms.requirements] paragraph 15 states:
The well-formedness and behavior of a call to an algorithm with an explicitly-specified template argument list is unspecified, except where explicitly stated otherwise.
It is unclear what this applies to because "algorithm" is never defined. 26.2 [algorithms.requirements]
paragraph 1 uses the term while mentioning iterators and containers, but [algorithm] specifies
function templates such as std::gcd which do not accept iterators.
[2025-10-21; Reflector poll.]
Set priority to 4 after reflector poll.
"NAD, I don't think people are confused about what an algorithm is."
"I'd rather the fix would define 'algorithm', as even the Note below the change uses that term."
Proposed resolution:
This wording is relative to N5008.
Modify 26.2 [algorithms.requirements] as indicated:
-15- The well-formedness and behavior of a call to
[Note 3: Consequently, an implementation can declare an algorithm with different template parameters than those presented. — end note]an algorithma function template specified in Clause 26 [algorithms] (including call operators of algorithm function objects denoted as algorithms throughout Clause 26.2 [algorithms.requirements]) with an explicitly-specified template argument list is unspecified, except where explicitly stated otherwise.
get-domain-early(sndr) worksSection: 33.9.2 [exec.snd.expos] Status: New Submitter: Jonathan Wakely Opened: 2025-06-16 Last modified: 2025-06-21
Priority: Not Prioritized
View other active issues in [exec.snd.expos].
View all other issues in [exec.snd.expos].
View all issues with New status.
Discussion:
During LWG review of P3481R3 it was noted that we need to say "except that sndr
is only evaluated once" because it's used in get-domain-early(sndr), but that
exposition-only-function doesn't actually use its argument, it only cares about the type, and
it default constructs a Domain of a type determined by using the type of sndr.
template<class Sender> using get-domain-early-t = decltype(get-domain-early(declval<const Sender&>()));
then we wouldn't need to use sndr twice in expressions that currently use get-domain-early(sndr).
Proposed resolution:
Section: 31.12.13.2 [fs.op.absolute] Status: New Submitter: Jonathan Wakely Opened: 2025-06-16 Last modified: 2025-10-14
Priority: 4
View all issues with New status.
Discussion:
[fs.op.absolute] has a note giving strong encouragement, which should be normative:
[Note 3: Implementations are strongly encouraged to not query secondary storage, and not consider
!exists(p)an error. — end note]
The part about !exists(p) not being an error could definitely be a Recommended practice paragraph.
Referring to "secondary storage" might need to be phrased differently to be normative (maybe with a note
clarifying that the normative wording is referring to "secondary storage").
[2025-10-14; Reflector poll]
Set priority to 4 after reflector poll.
Could the !exists(p) part be implementation-defined?
Proposed resolution:
value_or() and error_or() in std::expectedSection: 22.5.3.7 [optional.observe], 22.8.6.6 [expected.object.obs] Status: New Submitter: Hiroaki Ando Opened: 2025-06-27 Last modified: 2025-10-16
Priority: 3
View other active issues in [optional.observe].
View all other issues in [optional.observe].
View all issues with New status.
Discussion:
In 22.8.6.6 [expected.object.obs]/19, the return value of value_or()
is specified as follows:
Returns:
has_value() ? **this : static_cast<T>(std::forward<U>(v)).
Meanwhile, the return value of error_or() is specified as follows (22.8.6.6 [expected.object.obs]/23):
Returns:Since these functions appear to be dual in nature, it would be preferable to maintain consistent notation.std::forward<G>(e)ifhas_value()istrue,error()otherwise.
Jonathan adds:
The wording in expected::error_or is newer, having been added by
P2505R5, and intentionally avoided a conditional expression
(the problems with conditional expressions explained in P3177R0
don't actually affect these member functions, due to the non-const prvalue
return type, but determining that there are no pessimized copies in value_or
wouldn't be necessary if we didn't specify it with a conditional expression).
The error_or wording also avoids using an explicit conversion when the
Mandates: element requires implicit conversion to work anyway.
We might want to rephrase the value_or wording to match error_or,
or possibly make value_or and error_or even more explicit,
specifying them in terms of if-else:
:
Effects: Equivalent to:if (has_value()) return **this; else return std::forward<U>(v);
[2025-10-15; would be resolved by LWG 4406(i)]
[2025-10-16; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
std::relocateSection: 20.2.6 [obj.lifetime] Status: New Submitter: Giuseppe D'Angelo Opened: 2025-06-23 Last modified: 2025-10-22
Priority: 2
View other active issues in [obj.lifetime].
View all other issues in [obj.lifetime].
View all issues with New status.
Discussion:
The current specification of std::relocate in 20.2.6 [obj.lifetime] says
Throws: Nothing.
This is imprecise. A trivially relocatable type may feature a throwing
move constructor (or a throwing destructor); for instance, a linked-list
implementation with an externally allocated sentinel node may have a
throwing move constructor, but still be trivially relocatable. Such a
type is nothrow relocatable (as per is_nothrow_relocatable_v, cf.
21.3.6.4 [meta.unary.prop]) and therefore one can call std::relocate
on objects of that type.
std::relocate is specified to
relocate objects via move construction and destruction.
(std::trivially_relocate is unavailable during constant evaluation.)
Since P3068 we are allowed to throw during constant evaluation,
and therefore it's unclear whether std::relocate should propagate such an
exception to the caller or should instead make the evaluation not a
constant expression (and therefore make the program ill-formed, since by
construction we end up in this possibility during constant evaluation).
Given the rationale brought forward for std::relocate in P2786
is to be always a nofail operation, I'm proposing a resolution that goes in
that direction.
[2025-10-22; Reflector poll.]
Set priority to 2 after reflector poll.
"PR is incomplete, needs to be 'and either is_move_constructible_v<T> is false or an exception is thrown...'"
"I think we should use Constant when:."
Proposed resolution:
This wording is relative to N5008.
Modify 20.2.6 [obj.lifetime] as indicated:
template<class T> constexpr T* relocate(T* first, T* last, T* result);-16- Mandates: […]
-17- Preconditions: […] -18- Effects: […] -19- Returns:result + (last - first). -20- Throws: Nothing. -?- Remarks: Ifrelocateis called during constant evaluation, and an exception is thrown by a constructor or destructor ofT, the call torelocateis not a core constant expression (7.7 [expr.const]). [Note 3: Overlapping ranges are supported. — end note]
std::trivially_relocate needs stronger preconditions on "nested" objects with dynamic lifetimeSection: 20.2.6 [obj.lifetime] Status: New Submitter: Giuseppe D'Angelo Opened: 2025-06-23 Last modified: 2025-10-23
Priority: 2
View other active issues in [obj.lifetime].
View all other issues in [obj.lifetime].
View all issues with New status.
Discussion:
In 20.2.6 [obj.lifetime] the std::trivially_relocate function
is missing a precondition, that is, that any object alive in the range being
relocated is itself trivially relocatable.
wrapper type like:
// wraps a T
template<typename T>
struct wrapper {
alignas(T) std::byte data[sizeof(T)];
};
then one can build a non-trivially relocatable object into wrapper
objects:
struct NTR { ~NTR() {} };
static_assert(not std::is_trivially_relocatable_v<NTR>);
using WS = wrapper<NTR>;
static_assert(std::is_trivially_relocatable_v<WS>); // OK
And now one can do this:
WS* ws = /* … */; // create a wrapper new (&ws->data) NTR(); // create a NTR object into it std::trivially_relocate(ws, ws+1, dest); // should be UB
Attempting to trivially relocate *ws should result in undefined
behavior because NTR isn't trivially relocatable. I don't believe that
this fact is correctly captured by the preconditions of
std::trivially_relocate.
P,
then this code:
struct P { virtual void f(); };
static_assert(std::is_trivially_relocatable_v<P>);
using WP = wrapper<P>;
WP* wp = /* … */; // create a wrapper
new (&wp->data) P(); // create a P object into it
std::trivially_relocate(wp, wp+1, dest); // implementation defined
is well-defined or UB, depending on the implementation. This is because
on some implementations trivially relocating a polymorphic type requires
patching its virtual table pointer; cf. the discussion in chapter 15.1
of P2786R13. However the "type erasure" done by
wrapper<P> in the example (ultimately, it is just an array
of bytes) does not allow implementations to do such patching, and the code
is going to fail at runtime. Therefore this case also needs to be discussed by
std::trivially_relocate's specification.
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 20.2.6 [obj.lifetime] as indicated:
[Drafting note: For the general part of the issue (all objects in the range must be of trivially relocatable type), we append another point at the end of the existing Preconditions: element of
trivially_relocate.
For the specifics of polymorphic types, we amend at the end of the description the existing Remarks: element]template<class T> T* trivially_relocate(T* first, T* last, T* result);-9- Mandates: […]
-10- Preconditions:
(10.1) —
[first, last)is a valid range.(10.2) —
[result, result + (last - first))denotes a region of storage that is a subset of the region reachable throughresult(6.9.4 [basic.compound]) and suitably aligned for the typeT.(10.3) — No element in the range
[first, last)is a potentially-overlapping subobject.(10.?) — All objects whose storage is being provided for (6.8.2 [intro.object]) by objects in the
[first, last)range are of trivially relocatable type.-11- Postconditions: […]
-12- Returns:result + (last - first). -13- Throws: Nothing. -14- Complexity: Linear in the length of the source range. -15- Remarks: The destination region of storage is considered reused (6.8.4 [basic.life]). No constructors or destructors are invoked. If any polymorphic object (11.7.3 [class.virtual]) exists in storage provided for (6.8.2 [intro.object]) by objects in the[first, last)range, it is implementation-defined whether the behavior is undefined. [Note 2: Overlapping ranges are supported. — end note]
[2025-07-01; Pablo and Giuseppe improve wording]
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
It is unclear if the wording covers objects that are nested within (but not subobjects) of relocated objects. For trivially-copyable objectes nested within char array members, they are recreated by-implicit object creation.
Proposed resolution:
This wording is relative to N5008.
Modify 20.2.6 [obj.lifetime] as indicated:
[Drafting note: For the general part of the issue (all objects in the range must be of trivially relocatable type), we append another point at the end of the existing Preconditions: element of
trivially_relocate.
For the specifics of polymorphic types, we amend at the end of the description the existing Remarks: element]
template<class T> T* trivially_relocate(T* first, T* last, T* result);-9- Mandates: […]
-10- Preconditions:
(10.1) —
[first, last)is a valid range.(10.2) —
[result, result + (last - first))denotes a region of storage that is a subset of the region reachable throughresult(6.9.4 [basic.compound]) and suitably aligned for the typeT.(10.3) — No element in the range
[first, last)is a potentially-overlapping subobject.(10.?) — All objects whose storage is being provided for (6.8.2 [intro.object]) by objects in the
[first, last)range are of a type such that a union containing a non-static member of that type would be eligible for trivial relocation.-11- Postconditions: […]
-12- Returns:result + (last - first). -13- Throws: Nothing. -14- Complexity: Linear in the length of the source range. -15- Remarks: The destination region of storage is considered reused (6.8.4 [basic.life]). No constructors or destructors are invoked. [Note 2: Overlapping ranges are supported. — end note]
Section: 28.5.6.4 [format.formatter.spec] Status: LEWG Submitter: Jiang An Opened: 2025-06-27 Last modified: 2025-10-21
Priority: 3
View other active issues in [format.formatter.spec].
View all other issues in [format.formatter.spec].
View all issues with LEWG status.
Discussion:
The following program doesn't compile with MSVC STL (Godbolt link).
#include <format>
#include <ranges>
int main() {
auto iv1 = std::views::iota(42ull, 1729ull);
auto iv2 = std::views::iota(iv1.begin(), iv1.end());
std::format("{}", iv2.size()); // Error
}
In MSVC STL, the type of iv2.size() is an integer-class type, and the standard hasn't guaranteed there're
enabled formatter specializations for integer-class types. As a result, this program is not guaranteed to
be well-formed.
std::formatter specializations for integer-class types to get rid of
such uncertainty and inconsistency.
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 28.5.6.4 [format.formatter.spec] as indicated:
-2- […] Each header that declares the template
formatterprovides the following enabled specializations:
(2.1) — […]
(2.2) — […]
(2.3) — […]
(2.?) — For each
charT, for each cv-unqualified integer-class type (24.3.4.4 [iterator.concept.winc])IntegerClass, a specializationtemplate<> struct formatter<IntegerClass, charT>;(2.4) — […]
[2025-09-18; Jiang An improves wording]
[2025-10-21; Reflector poll. Status changed: NEW → LEWG.]
Set priority to 3 after reflector poll. Send to LEWG.
"Which additional operations integer-class types should be required to support
is a design question.
Why format but not iostreams? What about to_chars/from_chars?
All the stuff in <bit>? <utility>?
<cmath>?
You can show examples using any of these and claim 'uncertainty and inconsistency'."
Proposed resolution:
This wording is relative to N5014.
Modify 28.5.6.4 [format.formatter.spec] as indicated:
-2- […] Each header that declares the template
formatterprovides the following enabled specializations:
(2.1) — […]
(2.2) — […]
(2.3) — […]
(2.?) — For each
charT, for each cv-unqualified integer-class type (24.3.4.4 [iterator.concept.winc])IntegerClass, a specializationtemplate<> struct formatter<IntegerClass, charT>;(2.4) — […]
Modify 28.5.2.2 [format.string.std] as indicated:
-20- The meaning of some non-string presentation types is defined in terms of a call to
to_chars. In such cases, let[first, last)be a range large enough to hold theto_charsoutput andvaluebe the formatting argument value. If the formatting argument is of an integer-class type (24.3.4.4 [iterator.concept.winc]), the formatting argument value is as-if converted to a hypothetical extended integer type of the same signedness and width as the integer-class type first. Formatting is done as if by callingto_charsas specified and copying the output through the output iterator of the format context.
Modify [tab:format.type.int] (Table 107) as indicated:
Table 107 — Meaning of type options for integer and integer-class types [tab:format.type.int]
time_get::do_get_date is problematic even after LWG 461Section: 28.3.4.6.2.2 [locale.time.get.members] Status: New Submitter: S. B. Tam Opened: 2025-06-27 Last modified: 2025-10-21
Priority: 3
View all issues with New status.
Discussion:
Background: https://github.com/cplusplus/draft/pull/8009
LWG 461(i) changedtime_get::do_get_date to parse a set of fixed formats. For example, if
time_get::date_order() is no_order or mdy, the format parsed by do_get_date is %m/%d/%y.
This has several problems:
When the resolution of LWG 461(i) was applied to the draft standard, the slashes were lost.
This could mislead people into implementing do_get_date using the incorrect formats. Fortunately, none
of the standard library implementations are affected by this mistake.
Only 2-digit years are accepted due to the use of %y. This could lead to incorrect parse if
strftime uses %Y for the locale's date format.
date_order() might need additional locale facets to find out the date order, but it doesn't have an
ios_base& argument from which to get the locale.
Many locales do not use any of the specified formats. For example, de_DE uses %d.%m.%Y, zh_CN
uses %Y年%m月%d日. Although 28.3.4.6.2.3 [locale.time.get.virtuals]/5 gives an implementation the latitude
to accept additional formats, ambiguity could arise if the locale's format disagrees with date_order().
On POSIX systems, it is possible to query the locale's date format using nl_langinfo(D_FMT).
Maybe an implementation should be allowed to use that format instead of the one indicated by date_order().
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
"The current wording of do_date_order() suggests it should return one of the
dmy, mdy, ymd, ymd, or ydm enumerators as long as the date format
specified by %x has no components except day, month, and year.
But that fails to consider alternative separators between the components.
The kok_IN locale uses `"%d-%m-%y" which does not contain
"other variable components" but still doesn't match any of the fixed formats
in Table 102.
We need normative wording allowing no_order, instead of just a footnoote,
and we need to say do_get_date only has to use the formats in the table
when do_date_order() != no_order. It should not default to the mdy format
when do_date_order() returns no_order, because if the mdy format is
correct then it should have returned mdy!"
[2025-10-21; Jonathan adds wording]
This restores the slashes lost by LWG 461(i)
and attempts to make the use and effects of no_order more useful.
It does not attempt to address the problem that do_date_order()
has no access to an ios_base object.
Proposed resolution:
This wording is relative to N5014.
Modify 28.3.4.6.2.3 [locale.time.get.virtuals] as indicated:
dateorder do_date_order() const;-1- Returns: An enumeration value indicating the preferred order of components for those date formats that are composed of day, month, and year.
228Returnsno_orderif the date format specified by'x'contains other variable components (e.g., Julian day, week number, week day)does not match one of the formats in Table [tab:locale.time.get.dogetdate] .228) This function is intended as a convenience only, for common formats, and can returnno_orderin valid locales.iter_type do_get_time(iter_type s, iter_type end, ios_base& str, ios_base::iostate& err, tm* t) const;[...]iter_type do_get_date(iter_type s, iter_type end, ios_base& str, ios_base::iostate& err, tm* t) const;-4- Effects: Reads characters starting at s until it has extracted those
tmmembers and remaining format characters used bytime_put<>::putto produce one of the following formats, or until it encounters an error. The format depends on the value returned bydate_order()as shown in Table 102. Whendo_date_order()returnsno_order, it is unspecified whether the format shown in the table is used, or whether additional implementation-defined formats are accepted.[Note ?: For example, an implementation can accept dates in the format specified by
'x'as well as, or instead of, the format"%m/%d/%y". — end note
-5- An implementation may also accept additional implementation-defined formats.-6- Returns: An iterator pointing immediately beyond the last character recognized as possibly part of a valid date.
Table 102 —
do_get_dateeffects [tab:locale.time.get.dogetdate]
date_order()Format no_order"%m/%d/%y"dmy"%d/%m/%y"mdy"%m/%d/%y"ymd"%y/%m/%d"ydm"%y/%d/%m"
do_in and do_out could do with better specificationSection: 28.3.4.2.5.3 [locale.codecvt.virtuals] Status: New Submitter: S. B. Tam Opened: 2025-06-18 Last modified: 2025-10-17
Priority: 3
View other active issues in [locale.codecvt.virtuals].
View all other issues in [locale.codecvt.virtuals].
View all issues with New status.
Discussion:
Background: https://github.com/cplusplus/draft/pull/7347
The specification ofcodecvt::do_in and codecvt::do_out is unclear, and possibly incorrect:
the meaning of noconv is specified twice (once in paragraph 3, once in Table 91 [tab:locale.codecvt.inout]);
the effect on from_next is not specified;
the specification talks about "the input sequence [from, from_next)", but from_next is supposed to be an out parameter.
I think it should say "[from, from_end)" instead.
[2025-10-17; Reflector poll.]
Set priority to 3 after reflector poll.
"first element of the input that was not converted" doesn't make sense if there is no such element; similarly for the "first unchanged element".
Proposed resolution:
This wording is relative to N5008.
[Drafting note: This is modified from Jonathan Wakely's suggestion in https://github.com/cplusplus/draft/pull/7347#issuecomment]
In 28.3.4.2.5.3 [locale.codecvt.virtuals] remove Table 91 [tab:locale.codecvt.inout] in its entirety:
Table 91 —do_in/do_outresult values [tab:locale.codecvt.inout]ValueMeaningokcompleted the conversionpartialnot all source characters convertederrorencountered a character in[from, from_end)that cannot be convertednoconvinternTandexternTare the same type, and input sequence is identical to converted sequence
Modify 28.3.4.2.5.3 [locale.codecvt.virtuals] as indicated:
result do_out( stateT& state, const internT* from, const internT* from_end, const internT*& from_next, externT* to, externT* to_end, externT*& to_next) const; result do_in( stateT& state, const externT* from, const externT* from_end, const externT*& from_next, internT* to, internT* to_end, internT*& to_next) const;-1- Preconditions: […]
-2- Effects: Translates characters in the source range[from, from_end), placing the results in sequential positions starting at destination to. Converts no more than(from_end - from)source elements, and stores no more than(to_end - to)destination elements. -3-Stops if it encounters a character it cannot convert. It always leaves theIffrom_nextandto_nextpointers pointing one beyond the last element successfully converted. If it returnsnoconv,internTandexternTare the same type, and the converted sequence is identical to the input sequence[from, from_next),to_nextis set equal toto, the value ofstateis unchanged, and there are no changes to the values in[to, to_end).internTandexternTare the same type and the converted sequence would be identical to the input sequence [from,from_next), then no elements are converted, the value ofstateis unchanged, there are no changes to the values in [to,to_end), and the result isnoconv. Otherwise, if a character in [from,from_end) cannot be converted, conversion stops at that character and the result iserror. Otherwise, if all input characters are successfully converted and placed in the output range, the result isok. Otherwise, the result ispartial. In all cases,from_nextis set to point to the first element of the input that was not converted,to_nextis set to point to the first unchanged element in the output. [Note: When the result isnoconv,from_nextpoints tofromandto_nextpoints toto. — end note] -4- Acodecvtfacet that is used bybasic_filebuf[…] -5- Returns:An enumeration value, as summarized in Table 91The result as described above.
Section: 30.7.2.2 [time.clock.system.members] Status: New Submitter: Jiang An Opened: 2025-06-19 Last modified: 2025-08-26
Priority: 3
View all issues with New status.
Discussion:
Currently, the Constraints: element in 30.7.2.2 [time.clock.system.members] for the member typedef
system_clock::rep imposes a requirement for the implementation, without establishing any condition for user
code. Perhaps it's wrong to use a Constraints: element there.
Previous resolution [SUPERSEDED]:
This wording is relative to N5008.
Modify 30.7.2.2 [time.clock.system.members] as indicated:
using system_clock::rep = unspecified;-1-
Constraints:The implementation shall ensure thatsystem_clock::duration::min() < system_clock::duration::zero()istrue. [Note 1: This implies thatrepis a signed type. — end note]
[2025-07-15; Reflector discussion]
The discussion revealed a preference to not insert the additional "The implementation shall ensure that", because its not really needed, since this is just a normal implementation requirement that falls out of the specification.
[2025-08-21; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N5008.
Modify 30.7.2.2 [time.clock.system.members] as indicated:
using system_clock::rep = unspecified;-1-
Constraints:system_clock::duration::min() < system_clock::duration::zero()istrue. [Note 1: This implies thatrepis a signed type. — end note]
Section: 29.5.3.2 [rand.req.seedseq] Status: LEWG Submitter: Magnus Fromreide Opened: 2025-06-22 Last modified: 2025-08-29
Priority: 3
View all other issues in [rand.req.seedseq].
View all issues with LEWG status.
Discussion:
The requirements on a seed sequence are so strict that it is forbidden to initialize a random number generator directly from a hardware random number generator without lots of boiler plate and intermediary objects.
The main problem is that the seed sequence requirements state that a seed sequence must be "kind of copyable" and that calls to the copy must generate the same output as calls to the original. If one checks the uses of seed sequences then nothing makes use of this capability so I propose to just drop it. There has been a previous attempt to handle this issue using P0205 which preserved the old seed sequence and added a new concept that it used to seed generators. That effort stalled with the comment that it should be solved without the newstd::seed_adapter. This DR sidesteps that whole issue by simply removing
the unused requirements from the seed sequence concept.
I will admit that I am unsure about the deletion of Note 1 but since it only seems to attempt to motivate the
copyability I opted to let it go along with the requirements.
[2025-08-29; Reflector poll]
Set priority to 3 after reflector poll. This is a design change, send to LEWG.
Proposed resolution:
This wording is relative to N5008.
Modify 29.5.3.2 [rand.req.seedseq] as indicated:
-1- A seed sequence is an object that
consumes a sequence of integer-valued data andproduces a requested number of unsigned integer valuesi,0 = i < 232, based on the consumed data.[Note 1: Such an object provides a mechanism to avoid replication of streams of random variates. This can be useful, for example, in applications requiring large numbers of random number engines. — end note]-2- A classSmeets the requirements of a seed sequence if the expressions shown in Table 124 [tab:rand.req.seedseq] are valid and have the indicated semantics, and ifSalso meets all other requirements of 29.5.3.2 [rand.req.seedseq]. In Table 124 [tab:rand.req.seedseq] and throughout this subclause:
(2.1) —
Tis the type named byS' s associatedresult_type;(2.2) —
qis a value of typeSand; andris a value of typeSorconst S
(2.3) —ibandieare input iterators with an unsigned integervalue_typeof at least 32 bits;(2.4) —
rbandreare mutable random access iterators with an unsigned integervalue_typeof at least 32 bits;.
(2.5) —obis an output iterator; and
(2.6) —ilis a value of typeinitializer_list<T>.
Table 124 — Seed sequence requirements [tab:rand.req.seedseq] Expression Return type Pre/post-condition Complexity S::result_typeTTis an unsigned integer
type (6.9.2 [basic.fundamental]) of at least 32 bits.S()Creates a seed sequence with
the same initial state as all
other default-constructed seed
sequences of typeS.constantS(ib,ie)Creates a seed sequence having
internal state that depends on
some or all of the bits of the
supplied sequence[ib, ie).𝒪(ie - ib)S(il)Same asS(il.begin(), il.end()).same asS(il.begin(), il.end())q.generate(rb,re)voidDoes nothing if rb == re.
Otherwise, fills the supplied
sequence[rb, re)with 32-bit
quantitiesthat depend on the
sequence supplied to the
constructor and possibly also
depend on the history of
generate's previous
invocations.𝒪(re - rb) r.size()size_tThe number of 32-bit units that
would be copied by a call to
r.param.constantr.param(ob)voidCopies to the given destination
a sequence of 32-bit units that
can be provided to the
constructor of a second object of
typeS, and that would
reproduce in that second object
a state indistinguishable from
the state of the first object.𝒪(r.size())
is_sufficiently_alignedSection: 20.2.5 [ptr.align] Status: New Submitter: Damien Lebrun-Grandie Opened: 2025-07-03 Last modified: 2025-10-23
Priority: 2
View other active issues in [ptr.align].
View all other issues in [ptr.align].
View all issues with New status.
Discussion:
is_sufficiently_aligned should mandate that the alignment template argument is a power of two
and that it is greater equal to the byte alignment of its type template argument.
is_sufficiently_aligned has no Mandates element. It is an
oversight that we realized when implementing P2897R7 into libc++
(in https://github.com/llvm/llvm-project/pull/122603).
The function template was originally proposed as a static member function of the aligned_accessor
class template which has these two Mandates clauses and therefore applied
(see 23.7.3.5.4.1 [mdspan.accessor.aligned.overview] p1). It revision P2897R4,
is_sufficiently_aligned was moved out the class template definition to become the free function
memory helper that was voted into C++26 but the Mandates were lost in the process.
We propose to correct that oversight and reintroduce the following Mandates clauses right above
20.2.5 [ptr.align] p10.
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
The Mandates of Alignment >= alignof(T) was put into
question, as being too restrictive for general purpose free function.
Proposed resolution:
This wording is relative to N5008.
Modify 20.2.5 [ptr.align] as indicated:
template<size_t Alignment, class T> bool is_sufficiently_aligned(T* ptr);-?- Mandates:
(?.1) —
Alignmentis a power of two, and(?.2) —
Alignment >= alignof(T)istrue.-10- Preconditions:
-11- Returns:ppoints to an objectXof a type similar (7.3.6 [conv.qual]) toT.trueifXhas alignment at leastAlignment, otherwisefalse. -12- Throws: Nothing.
experimental::observer_ptr should have more constexprSection: 8.2.6 [fund.ts.v3::memory.observer.ptr.special] Status: New Submitter: Jonathan Wakely Opened: 2025-07-14 Last modified: 2025-08-26
Priority: 4
View all issues with New status.
Discussion:
In the Library Fundamentals TS, the swap overload, make_observer_ptr
function, and comparisons for observer_ptr could be constexpr, but are not.
The member swap is already constexpr in the TS, and the non-member swap
is constexpr in libc++ but not the comparisons. The proposed resolution has
been implemented and tested in libstdc++.
If we ever rebase the TS on a new C++ standard (or put observer_ptr into
the standard) the comparisons should all be updated like so:
template <class W1, class W2> constexpr bool operator==(observer_ptr<W1> p1, observer_ptr<W2> p2);Returns:p1.get() == p2.get().bool operator!=(...);template <class W> constexpr bool operator==(observer_ptr<W> p, nullptr_t) noexcept;bool operator==(...);Returns:not p.bool operator!=(...); bool operator!=(...);template <class W1, class W2> constexpr bool operator<=>(observer_ptr<W1> p1, observer_ptr<W2> p2);Returns:less<W3>compare_three_way()(p1.get(), p2.get()), where.W3is the composite pointer type (C++20 §7) ofW1*andW2*bool operator>(...); bool operator<=(...); bool operator>=(...);
[2025-08-21; Reflector poll]
Set priority to 4 after reflector poll. Seven votes for P0 but one vote for NAD, so not made Tentatively Ready.
Proposed resolution:
This wording is relative to N4939.
#include <memory> namespace std { namespace experimental::inline fundamentals_v3 { // 8.2, Non-owning (observer) pointers template <class W> class observer_ptr; // 8.2.6, observer_ptr specialized algorithms template <class W> constexpr void swap(observer_ptr<W>&, observer_ptr<W>&) noexcept; template <class W> constexpr observer_ptr<W> make_observer(W*) noexcept; // (in)equality operators template <class W1, class W2> constexpr bool operator==(observer_ptr<W1>, observer_ptr<W2>); template <class W1, class W2> constexpr bool operator!=(observer_ptr<W1>, observer_ptr<W2>); template <class W> constexpr bool operator==(observer_ptr<W>, nullptr_t) noexcept; template <class W> constexpr bool operator!=(observer_ptr<W>, nullptr_t) noexcept; template <class W> constexpr bool operator==(nullptr_t, observer_ptr<W>) noexcept; template <class W> constexpr bool operator!=(nullptr_t, observer_ptr<W>) noexcept; // ordering operators template <class W1, class W2> constexpr bool operator<(observer_ptr<W1>, observer_ptr<W2>); template <class W1, class W2> constexpr bool operator>(observer_ptr<W1>, observer_ptr<W2>); template <class W1, class W2> constexpr bool operator<=(observer_ptr<W1>, observer_ptr<W2>); template <class W1, class W2> constexpr bool operator>=(observer_ptr<W1>, observer_ptr<W2>); } // namespace experimental::inline fundamentals_v3 // 8.2.7, observer_ptr hash support template <class T> struct hash; template <class T> struct hash<experimental::observer_ptr<T>>; } // namespace std
template <class W> constexpr void swap(observer_ptr<W>& p1, observer_ptr<W>& p2) noexcept;-2- Effects:p1.swap(p2).template <class W> constexpr observer_ptr<W> make_observer(W* p) noexcept;-4- Returns:observer_ptr<W>{p}.template <class W1, class W2> constexpr bool operator==(observer_ptr<W1> p1, observer_ptr<W2> p2);-6- Returns:p1.get() == p2.get().template <class W1, class W2> constexpr bool operator!=(observer_ptr<W1> p1, observer_ptr<W2> p2);-8- Returns:not (p1 == p2).template <class W> constexpr bool operator==(observer_ptr<W> p, nullptr_t) noexcept; template <class W> constexpr bool operator==(nullptr_t, observer_ptr<W> p) noexcept;-10- Returns:not p.template <class W> constexpr bool operator!=(observer_ptr<W> p, nullptr_t) noexcept; template <class W> constexpr bool operator!=(nullptr_t, observer_ptr<W> p) noexcept;-12- Returns:(bool)p.template <class W1, class W2> constexpr bool operator<(observer_ptr<W1> p1, observer_ptr<W2> p2);-14- Returns:less<W3>()(p1.get(), p2.get()), whereW3is the composite pointer type (C++20 §7) ofW1*andW2*.template <class W1, class W2> constexpr bool operator>(observer_ptr<W1> p1, observer_ptr<W2> p2);-16- Returns:p2 < p1.template <class W1, class W2> constexpr bool operator<=(observer_ptr<W1> p1, observer_ptr<W2> p2);-16- Returns:not (p2 < p1).template <class W1, class W2> constexpr bool operator>=(observer_ptr<W1> p1, observer_ptr<W2> p2);-16- Returns:not (p1 < p2).
Section: 16.4.4.5 [hash.requirements] Status: New Submitter: Jonathan Wakely Opened: 2025-07-18 Last modified: 2025-10-14
Priority: 3
View all other issues in [hash.requirements].
View all issues with New status.
Discussion:
A colleague was mislead into thinking that the Cpp17Hash requirements
imply hash functions must be stateless, so that every h would produce the
same value for h(k). The normative wording and the note can be interpreted
as saying that the value of h is not relevant, only k matters.
The value returned shall depend only on the argumentkfor the duration of the program.[Note 1: Thus all evaluations of the expression
h(k)with the same value forkyield the same result for a given execution of the program. — end note]
I initially proposed changing it to "with the same values for h and k
but Jens observed that if h(k) is allowed to change h then a subsequent
call would not meet the condition "the same value of h".
We should also clarify that the evaluation of h(k) should not change h,
so that we have the same value of h each time.
Arguably, the wording in p2 "h is a value of type (possibly const) H"
already covers this. It suggests to me that the effects in the table are for
a particular value h, so for that value, the result of calling h(k)
depends only on the value of k
(and not on the current time, ambient temperature in Denmark,
or the output of a random number generator). And the fact that h can be
a value of type const H implies that calling h(k) doesn't change h.
Maybe we want to clarify it in terms of equality-preserving 18.2 [concepts.equality].
[2025-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Could say "Note: Thus, for a particular value h,
all evaluations of the expression h(k) with the same value
for k yield the same result for a given execution of the
program."
Proposed resolution:
F noexcept" typeSection: 21.4.12 [meta.reflection.extract] Status: New Submitter: Jan Schultke Opened: 2025-07-10 Last modified: 2025-08-28
Priority: 3
View all issues with New status.
Discussion:
This is a follow-up from https://github.com/cplusplus/#8008
In 21.4.12 [meta.reflection.extract] paragraph 7, the type "F noexcept" is used, presumably
with the intent that noexcept would be "injected" into the parameters-and-qualifiers of
the declarator associated with F.
However, the syntax F noexcept is ill-formed, and does not have that intended effect. There ought
to be a better way to phrase that.
[2025-08-27; Reflector poll]
Set priority to 3 after reflector poll.
"phrase this in terms of the quoted type description in 9.3.4.6 [dcl.fct]
(see before bullet 1.6, The type of the declarator-id in D is...
)."
Proposed resolution:
This wording is relative to N5014.
Modify 21.4.12 [meta.reflection.extract] as indicated:
[Drafting note: In bullet (7.1) we apply an additional drive-by fix for the broken "
Tis" construction before theis_convertible_vcondition]
template<class T> consteval T extract-member-or-function(info r); // exposition only-?- Given a function type
-6- Returns: […] -7- Throws:F, let the typeFwithnoexceptbe the same type, except that the parameters-and-qualifiers in the noptr-declarator describing the type containnoexcept(true).meta::exceptionunless
(7.1) —
rrepresents a non-static data member with typeX, that is not a bit-field, that is a direct member of classC,TandCare similar types (7.3.6 [conv.qual]), andTisis_convertible_v<X C::*, T>istrue;(7.2) —
rrepresents an implicit object member function with typeForFwithnoexceptthat is a direct member of a classC, andTisF C::*; or(7.3) —
rrepresents a non-member function, static member function, or explicit object member function of function typeForFwithnoexcept, andTisF*.
vector_sum_of_squares wordingSection: 29.9.13.8 [linalg.algs.blas1.ssq] Status: LEWG Submitter: Mark Hoemmen Opened: 2025-07-23 Last modified: 2025-10-17
Priority: 1
View all issues with LEWG status.
Discussion:
The current wording for vector_sum_of_squares
29.9.13.8 [linalg.algs.blas1.ssq] has three problems with its specification of the
value of result.scaling_factor.
The function permits InVec::value_type and Scalar to be any
linear algebra value types. However, computing
result.scaling_factor that satisfies both (3.1) and (3.2) requires
more operations, such as division. Even if those operations are
defined, they might not make result.scaling_factor satisfy the
required properties. For example, integers have division, but integer
division won't help here.
LAPACK's xLASSQ (the algorithm to which Note 1 in 29.9.13.8 [linalg.algs.blas1.ssq] refers) changed its algorithm recently (see Reference-LAPACK/lapack/pull/#494) so that the scaling factor is no longer necessarily the maximum of the input scaling factor and the absolute value of all the input elements. It's a better algorithm and we would like to be able to use it.
Both members of sum_of_squares_result<Scalar> have the same type,
Scalar. If the input mdspan's value_type represents a quantity
with units, this would not be correct. For example, if value_type
has units of distance (say [m]), the sum of squares should have units
of area ([m2]), while the scaling factor should have units of
distance ([m]).
Problem (1) means that the current wording is broken. I suggest two different ways to fix this.
Remove vector_sum_of_squares entirely (both overloads from
29.9.2 [linalg.syn], and the entire
29.9.13.8 [linalg.algs.blas1.ssq]). That way, we
won't be baking an old, less good algorithm into the Standard. Remove
Note 3 from 29.9.13.9 [linalg.algs.blas1.nrm2], which is the only
other reference to vector_sum_of_squares in the Standard.
Fix 29.9.13.8 [linalg.algs.blas1.ssq] by adding to the
Mandates element (para 2) that InVec::value_type and Scalar
are both floating-point types (so that we could fix this later if
we want), and remove 29.9.13.8 [linalg.algs.blas1.ssq] 3.1.
Optionally add Recommended Practice, though Note 1 already
suggests the intent.
I prefer just removing vector_sum_of_squares. Implementers who care
about QoI of vector_two_norm should already know what to do. If
somebody cares sufficiently, they can propose it back for C++29 and
think about how to make it work for generic number types.
[2025-10-17; Reflector poll. Status changed: New → LEWG.]
Set priority to 1 after reflector poll. Send to LEWG.
This is the subject of NB comment 169-276. LWG took a poll in the 2025-10-10 telecon and recommends that LEWG confirms this resolution.
Proposed resolution:
This wording is relative to N5014.
[Drafting note: The wording below implements option 1 of the issue discussion]
Modify 29.9.2 [linalg.syn], header <linalg> synopsis, as indicated:
namespace std::linalg {
[…]
// 29.9.13.8 [linalg.algs.blas1.ssq], scaled sum of squares of a vector's elements
template<class Scalar>
struct sum_of_squares_result {
Scalar scaling_factor;
};
template<in-vector InVec, class Scalar>
sum_of_squares_result<Scalar>
vector_sum_of_squares(InVec v, sum_of_squares_result<Scalar> init);
template<class ExecutionPolicy, in-vector InVec, class Scalar>
sum_of_squares_result<Scalar>
vector_sum_of_squares(ExecutionPolicy&& exec,
InVec v, sum_of_squares_result<Scalar> init);
[…]
}
Delete the entire 29.9.13.8 [linalg.algs.blas1.ssq] as indicated:
29.9.13.8 Scaled sum of squares of a vector's elements [linalg.algs.blas1.ssq]template<in-vector InVec, class Scalar> sum_of_squares_result<Scalar> vector_sum_of_squares(InVec v, sum_of_squares_result<Scalar> init); template<class ExecutionPolicy, in-vector InVec, class Scalar> sum_of_squares_result<Scalar> vector_sum_of_squares(ExecutionPolicy&& exec, InVec v, sum_of_squares_result<Scalar> init);
-1- [Note 1: These functions correspond to the LAPACK function xLASSQ[20]. — end note]-2- Mandates:decltype(abs-if-needed(declval<typename InVec::value_type>()))is convertible toScalar.-3- Effects: Returns a valueresultsuch that
(3.1) —result.scaling_factoris the maximum ofinit.scaling_factorandabs-if-needed(x[i])for alliin the domain ofv; and
(3.2) — lets2initbeinit.scaling_factor * init.scaling_factor * init.scaled_sum_of_squares
thenresult.scaling_factor * result.scaling_factor * result.scaled_sum_of_squaresequals the sum ofs2initand the squares ofabs-if-needed(x[i])for alliin the domain ofv.
-4- Remarks: IfInVec::value_type, andScalarare all floating-point types or specializations ofcomplex, and ifScalarhas higher precision thanInVec::value_type, then intermediate terms in the sum useScalar's precision or greater.
Modify 29.9.13.9 [linalg.algs.blas1.nrm2] as indicated:
template<in-vector InVec, class Scalar> Scalar vector_two_norm(InVec v, Scalar init); template<class ExecutionPolicy, in-vector InVec, class Scalar> Scalar vector_two_norm(ExecutionPolicy&& exec, InVec v, Scalar init);-1- [Note 1: […] ]
-2- Mandates: […] -3- Returns: […] [Note 2: […] ] -4- Remarks: […][Note 3: An implementation of this function for floating-point typesTcan use thescaled_sum_of_squaresresult fromvector_sum_of_squares(x, {.scaling_factor=1.0, .scaled_sum_of_squares=init}). — end note]
std::decay_t in the specification of ranges::distance is problematicSection: 24.4.4.3 [range.iter.op.distance] Status: New Submitter: Jiang An Opened: 2025-07-24 Last modified: 2025-08-29
Priority: 4
View all other issues in [range.iter.op.distance].
View all issues with New status.
Discussion:
This is discovered when implementing the resolution LWG 4242(i).
Per LWG 4242(i), it is intended to allow ranges::distance to handle
volatile-qualified iterator values.
However, the uses of decay_t (established per LWG 3664(i)) are still problematic,
because when sized_sentinel_for<S, decay_t<I>> is modeled, there's no
semantic or syntactic requirement that S shall work with volatile-qualified I.
operator== or operator- intendedly rejects volatile-qualified iterators. And
even when they accept volatile-qualified iterators, the additional semantic requirements
imposed by sized_sentinel_for<S, decay_t<I>> are still undesired.
I think we should only decay arrays and keep volatile for non-array arguments.
[2025-08-29; Reflector poll]
Set priority to 4 after reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 24.2 [iterator.synopsis], header <iterator> synopsis, as indicated:
[…]
namespace std: {
[…]
// 24.4.4.3 [range.iter.op.distance], ranges::distance
template<class T>
using distance-iterator-t = // exposition only
conditional_t<is_array_v<remove_reference_t<T>>,
decay_t<T>, remove_const_t<remove_reference_t<T>>>;
template<class I, sentinel_for<I> S>
requires (!sized_sentinel_for<S, I>)
constexpr iter_difference_t<I> distance(I first, S last); // freestanding
template<class I, sized_sentinel_for<decay_tdistance-iterator-t<I>> S>
constexpr iter_difference_t<decay_tdistance-iterator-t<I>> distance(I&& first, S last); // freestanding
template<range R>
constexpr range_difference_t<R> distance(R&& r); // freestanding
[…]
}
Modify 24.4.4.3 [range.iter.op.distance] as indicated:
template<class I, sized_sentinel_for<decay_tdistance-iterator-t<I>> S> constexpr iter_difference_t<decay_tdistance-iterator-t<I>> distance(I&& first, S last);-3- Effects: Equivalent to:
if constexpr (!is_array_v<remove_reference_t<I>>) return last - first; else return last - static_cast<decay_t<I>>(first);
std::optional<NonReturnable&> is ill-formed due to value_orSection: 22.5.4.6 [optional.ref.observe] Status: New Submitter: Jiang An Opened: 2025-07-25 Last modified: 2025-10-16
Priority: 1
View all issues with New status.
Discussion:
Currently, if T is an array type or a function type, instantiation of std::optional<T&>
is still ill-formed, because the return type of its value_or member function is specified as
remove_cv_t<T>, which is invalid as a return type.
T& from valid contained types. Given only value_or is
problematic here, perhaps we can avoid providing it if T is not returnable.
[2025-10-16; Reflector poll]
Set priority to 1 after reflector poll.
Why not just add Constraints: and use decay_t<T>
for the return type, instead of "not always present" which is currently
only used for member types, not member functions.
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
Modify 22.5.4.1 [optional.optional.ref.general], header
<iterator>synopsis, as indicated:namespace std { template<class T> class optional<T&> { […] constexpr T& value() const; // freestanding-deleted template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& u) const; // not always present […] }; }Modify 22.5.4.6 [optional.ref.observe] as indicated:
template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& u) const;-8- Let
-9- Mandates:Xberemove_cv_t<T>.is_constructible_v<X, T&> && is_convertible_v<U, X>istrue. -10- Effects: Equivalent to:return has_value() ? *val : static_cast<X>(std::forward<U>(u));-?- Remarks: This function template is present if and only if
Tis a non-array object type.
[2025-10-16; Jonathan provides new wording]
Proposed resolution:
This wording is relative to N5014.
Modify 22.5.4.6 [optional.ref.observe] as indicated:
template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& u) const;-?- Constraints:
Tis a non-array object type.-8- Let
-9- Mandates:Xberemove_cv_t<T>.is_constructible_v<X, T&> && is_convertible_v<U, X>istrue. -10- Effects: Equivalent to:return has_value() ? *val : static_cast<X>(std::forward<U>(u));-?- Remarks: The return type is unspecified if
Tis an array type or a non-object type. [Note ?: This is to avoid the declaration being ill-formed. — end note]
Section: 16.4.6.5 [member.functions] Status: New Submitter: Jiang An Opened: 2025-07-24 Last modified: 2025-08-29
Priority: 3
View other active issues in [member.functions].
View all other issues in [member.functions].
View all issues with New status.
Discussion:
16.4.6.5 [member.functions]/2 seems to allow that even when the constraints are not met, such a default constructor can be selected, because no restriction is imposed when no overload is originally selected. Per the discussion in LWG 2563(i) (closed as NAD), it even seems to allow the implementation to add a default constructor when there's originally none.
However, we're constraining some member functions, e.g. default constructors ofunique_ptr,
tuple, and variant. Allowance of more permissive overload sets of constructors effectively
renders the Constraints meaningless, because even if the implementation doesn't constrain
the default constructors at all, it can still satisfy the restriction in
16.4.6.5 [member.functions]/2 since LWG 2259(i).
Consider the following example:
struct S
{
S() = delete;
};
static_assert(std::is_default_constructible_v<std::tuple<S>>);
std::tuple<S> t;
Even though currently the default constructor of std::tuple is constrained (22.4.4.2 [tuple.cnstr]/6),
16.4.6.5 [member.functions]/2 seemingly allows the implementation to add another overload that's also
a default constructor, constrained by "is_default_constructible_v<Ti> is false
for at least one i", and then render the example well-formed or only error on actual
instantiation of the "additional" default constructor. Note that such an additional overload effectively
removes the Constraints element requirements.
[2025-08-29; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
sized-random-access-range and bidirectional-common
in <ranges>Section: 25.2 [ranges.syn], 25.4.6 [range.refinements], 25.5.3.1 [view.interface.general], 25.7.10.1 [range.take.overview], 25.7.12.1 [range.drop.overview], 25.7.12.2 [range.drop.view], 25.7.14.3 [range.join.iterator], 25.7.15.2 [range.join.with.view], 25.7.15.3 [range.join.with.iterator], 25.7.20.2 [range.common.view], 25.7.25.2 [range.zip.view], 25.7.30.2 [range.slide.view], 25.7.33.2 [range.cartesian.view] Status: New Submitter: Hewill Kang Opened: 2025-07-28 Last modified: 2025-10-23
Priority: 4
View other active issues in [ranges.syn].
View all other issues in [ranges.syn].
View all issues with New status.
Discussion:
P3179 introduces a new
exposition-only concept, sized-random-access-range, into <ranges> to simplify
the signature of parallel range algorithms.
However, this also applies more broadly to <ranges>, as we often need to determine whether a range satisfies
both sized_range and random_access_range in order to dispatch optimized branches.
common_range and
bidirectional_range to decide whether to provide backward traversal capabilities,
which is expressed by the exposition-only concept bidirectional-common introduced in
P2441.
Unfortunately, this concept currently only applies to a single section.
It would be much simpler and more readable if both concepts were available throughout <ranges>,
which would also allow newly introduced adapters or other library features to take advantage of them.
Note that since some of these simplifications change the order in which the concepts are spelled, they may not be
purely editorial.
[2025-10-23; Reflector poll.]
Set priority to 4 after reflector poll.
Use of exposition-only concepts is not necessary improving readability or produced error messages.
Proposed resolution:
This wording is relative to N5014.
Modify 25.2 [ranges.syn], header <ranges>
synopsis, as indicated:
// mostly freestanding #include <compare> // see 17.12.1 [compare.syn] #include <initializer_list> // see 17.11.2 [initializer.list.syn] #include <iterator> // see 24.2 [iterator.synopsis] namespace std::ranges { […] template<class T> concept sized-random-access-range = see below; // exposition only template<class T> concept common-bidirectional-range = see below; // exposition only […] }
Modify 25.4.6 [range.refinements] as indicated:
[…]-8- The exposition-only concept
sized-random-access-rangespecifies the requirements of arangetype that is sized and allows random access to its elements.template<class T> concept sized-random-access-range = // exposition only random_access_range<T> && sized_range<T>;[Note 1: This concept constrains some parallel algorithm overloads; see [algorithms]. — end note]
-?- The exposition-only conceptcommon-bidirectional-rangespecifies the requirements of arangetype that is bidirectional and whose iterator and sentinel types are the same.template<class T> concept common-bidirectional-range = // exposition only bidirectional_range<T> && common_range<T>;
Modify 25.5.3.1 [view.interface.general] as indicated:
-1- The class template
view_interfaceis a helper for defining view-like types that offer a container-like interface. It is parameterized with the type that is derived from it.namespace std::ranges { template<class D> requires is_class_v<D> && same_as<D, remove_cv_t<D>> class view_interface { private: […] public: […] constexpr decltype(auto) back() requiresbidirectional_range<D> && common_range<D>common-bidirectional-range<D>; constexpr decltype(auto) back() const requiresbidirectional_range<const D> && common_range<const D>common-bidirectional-range<const D>; […] }; }
Modify 25.5.3.2 [view.interface.members] as indicated:
constexpr decltype(auto) back() requiresbidirectional_range<D> && common_range<D>common-bidirectional-range<D>; constexpr decltype(auto) back() const requiresbidirectional_range<const D> && common_range<const D>common-bidirectional-range<const D>;-3- Hardened preconditions:
-4- Effects: Equivalent to:!empty()istrue.return *ranges::prev(ranges::end(derived()));
Modify 25.7.10.1 [range.take.overview] as indicated:
-2- The name
views::takedenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::take(E, F)is ill-formed. Otherwise, the expressionviews::take(E, F)is expression-equivalent to:
- […]
(2.2) — Otherwise, if
[…]Tmodelsrandom_access_rangeandsized_rangesized-random-access-rangeand is a specialization ofspan(23.7.2.2 [views.span]),basic_string_view(27.3 [string.view]), orranges::subrange(25.5.4 [range.subrange]), thenU(ranges::begin(E), ranges::begin(E) + std::min<D>(ranges::distance(E), F)), except thatEis evaluated only once, whereUis a type determined as follows:(2.3) — otherwise, if
Tis a specialization ofiota_view(25.6.4.2 [range.iota.view]) that modelsrandom_access_rangeandsized_rangesized-random-access-range, theniota_view(*ranges::begin(E), *(ranges::begin(E) + std::min<D>(ranges::distance(E), F))), except thatEis evaluated only once.
Modify 25.7.12.1 [range.drop.overview] as indicated:
-2- The name
views::dropdenotes a range adaptor object (25.7.2 [range.adaptor.object]). LetEandFbe expressions, letTberemove_cvref_t<decltype((E))>, and letDberange_difference_t<decltype((E))>. Ifdecltype((F))does not modelconvertible_to<D>,views::drop(E, F)is ill-formed. Otherwise, the expressionviews::drop(E, F)is expression-equivalent to:
- […]
(2.2) — Otherwise, if
[…]Tmodelsrandom_access_rangeandsized_rangesized-random-access-rangeand is(2.3) — Otherwise, if
Tis a specialization ofsubrange(25.5.4 [range.subrange]) that modelsrandom_access_rangeandsized_rangesized-random-access-range, thenT(ranges::begin(E) + std::min<D>(ranges::distance(E), F), ranges::end(E), to-unsigned-like(ranges::distance(E) - std::min<D>(ranges::distance(E), F))), except thatEandFare each evaluated only once.
Modify 25.7.12.2 [range.drop.view] as indicated:
namespace std::ranges { template<view V> class drop_view : public view_interface<drop_view<V>> { public: […] constexpr auto begin() requires (!(simple-view<V> &&[…]random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)); constexpr auto begin() const requiresrandom_access_range<const V> && sized_range<const V>sized-random-access-range<const V>; […] }; […] }constexpr auto begin() requires (!(simple-view<V> &&random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)); constexpr auto begin() const requiresrandom_access_range<const V> && sized_range<const V>sized-random-access-range<const V>;-3- Returns:
ranges::next(ranges::begin(base_), count_, ranges::end(base_)).
Modify 25.7.14.3 [range.join.iterator] as indicated:
namespace std::ranges { template<input_range V> requires view<V> && input_range<range_reference_t<V>> template<bool Const> struct join_view<V>::iterator { private: […] public: […] constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>; constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>; […] }; }-1-
iterator::iterator_conceptis defined as follows::[…]
(1.1) — If
[…]ref-is-glvalueistrue,Basemodelsbidirectional_range, andrange_reference_t<Base>modelsbothbidirectional_rangeandcommon_rangecommon-bidirectional-range, theniterator_conceptdenotesbidirectional_iterator_tag.constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>;-16- Effects: Equivalent to:
[…]constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional_range<range_reference_t<Base>> && common_range<range_reference_t<Base>>common-bidirectional-range<range_reference_t<Base>>;-17- Effects: Equivalent to:
[…]
Modify 25.7.15.2 [range.join.with.view] as indicated:
namespace std::ranges {
template<class R>
concept bidirectional-common = bidirectional_range<R> && common_range<R>; // exposition only
[…]
}
Modify 25.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> && concatable<range_reference_t<V>, Pattern> template<bool Const> class join_with_view<V, Pattern>::iterator { private: […] public: […] constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-commoncommon-bidirectional-range<InnerBase> &&bidirectional-commoncommon-bidirectional-range<PatternBase>; constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-commoncommon-bidirectional-range<InnerBase> &&bidirectional-commoncommon-bidirectional-range<PatternBase>; […] }; }-1-
iterator::iterator_conceptis defined as follows::[…]
(1.1) — If
[…]ref-is-glvalueistrue,Basemodelsbidirectional_range, andInnerBaseandPatternBaseeach modelbidirectional-commoncommon-bidirectional-range, theniterator_conceptdenotesbidirectional_iterator_tag.constexpr iterator& operator--() requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-commoncommon-bidirectional-range<InnerBase> &&bidirectional-commoncommon-bidirectional-range<PatternBase>;[…]-16- Effects: Equivalent to:
constexpr iterator operator--(int) requires ref-is-glvalue && bidirectional_range<Base> &&bidirectional-commoncommon-bidirectional-range<InnerBase> &&bidirectional-commoncommon-bidirectional-range<PatternBase>;-17- Effects: Equivalent to:
[…]
Modify 25.7.20.2 [range.common.view] as indicated:
namespace std::ranges {
template<view V>
requires (!common_range<V> && copyable<iterator_t<V>>)
class common_view : public view_interface<common_view<V>> {
private:
V base_ = V(); // exposition only
public:
[…]
constexpr auto begin() requires (!simple-view<V>) {
if constexpr (random_access_range<V> && sized_range<V>sized-random-access-range<V>)
return ranges::begin(base_);
else
return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::begin(base_));
}
constexpr auto begin() const requires range<const V> {
if constexpr (random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)
return ranges::begin(base_);
else
return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::begin(base_));
}
constexpr auto end() requires (!simple-view<V>) {
if constexpr (random_access_range<V> && sized_range<V>sized-random-access-range<V>)
return ranges::begin(base_) + ranges::distance(base_);
else
return common_iterator<iterator_t<V>, sentinel_t<V>>(ranges::end(base_));
}
constexpr auto end() const requires range<const V> {
if constexpr (random_access_range<const V> && sized_range<const V>sized-random-access-range<const V>)
return ranges::begin(base_) + ranges::distance(base_);
else
return common_iterator<iterator_t<const V>, sentinel_t<const V>>(ranges::end(base_));
}
[…]
};
[…]
}
Modify 25.7.25.2 [range.zip.view] as indicated:
namespace std::ranges {
template<class... Rs>
concept zip-is-common = // exposition only
(sizeof...(Rs) == 1 && (common_range<Rs> && ...)) ||
(!(bidirectional_range<Rs> && ...) && (common_range<Rs> && ...)) ||
((random_access_range<Rs> && ...) && (sized_range<Rs> && ...)sized-random-access-range<Rs> && ...);
[…]
}
Modify 25.7.30.2 [range.slide.view] as indicated:
namespace std::ranges {
template<class V>
concept slide-caches-nothing = random_access_range<V> && sized_range<V>sized-random-access-range<V>; // exposition only
template<class V>
concept slide-caches-last = // exposition only
!slide-caches-nothing<V> && bidirectional_range<V> && common_range<V>common-bidirectional-range<V>;
[…]
}
Modify 25.7.33.2 [range.cartesian.view] as indicated:
namespace std::ranges {
template<bool Const, class First, class... Vs>
concept cartesian-product-is-random-access = // exposition only
(random_access_range<maybe-const<Const, First>> && ... &&
(random_access_range<maybe-const<Const, Vs>>
&& sized_range<maybe-const<Const, Vs>>)sized-random-access-range<maybe-const<Const, Vs>>);
template<class R>
concept cartesian-product-common-arg = // exposition only
common_range<R> || (sized_range<R> && random_access_range<R>)sized-random-access-range<R>;
[…]
}
std::optional<T&>::iterator can't be a contiguous iterator for some TSection: 22.5.4.5 [optional.ref.iterators] Status: New Submitter: Jiang An Opened: 2025-08-05 Last modified: 2025-08-29
Priority: 1
View all issues with New status.
Discussion:
This is related to LWG 4304(i). When T is function type or an incomplete array type,
it is impossible to implement all requirements in 22.5.4.5 [optional.ref.iterators]/1.
T is an incomplete object type, we may want to support std::optional<T&>
as it's sometimes a replacement of T*. Perhaps we can require that the iterator type is always a
random access iterator, and additional models contiguous_iterator when T is complete.
When T is a function type, the possibly intended iterator would be not even an actual iterator.
But it seems that range-for loop over such an std::optional<T&> can work.
[2025-08-29; Reflector poll]
Set priority to 1 after reflector poll.
"How can end() work for a pointer to incomplete type? begin/end should
be constrained on object types, and Mandate complete object types.
The aliases shouldn't be defined for non-object types, but probably harmless."
Proposed resolution:
This wording is relative to N5014.
Modify 22.5.4.5 [optional.ref.iterators] as indicated:
using iterator = implementation-defined;-1-
-?- IfTIfTis an object type, this type modelscontiguous_iterator(24.3.4.14 [iterator.concept.contiguous])random_access_iterator(24.3.4.13 [iterator.concept.random.access]), meets the Cpp17RandomAccessIterator requirements (24.3.5.7 [random.access.iterators]), and meets the requirements for constexpr iterators (24.3.1 [iterator.requirements.general]), with value typeremove_cv_t<T>. The reference type isT&foriterator. WhenTis a complete object type, iterator additionally modelscontiguous_iterator(24.3.4.14 [iterator.concept.contiguous]).
-2-All requirements on container iterators (23.2.2.2 [container.reqmts]) apply tooptional::iterator.Tis a function type,iteratorsupports all operators required by therandom_access_iteratorconcept (24.3.4.13 [iterator.concept.random.access]) along with the<=>operator as specified for container iterators (23.2.2.2 [container.reqmts]).iteratordereferences to aTlvalue. These operators behave as ifiteratorwere an actual iterator iterating over a range ofT, and result in constant subexpressions whenever the behavior is well-defined. [Note ?: Such anoptional::iteratordoes not need to declare any member type because it is not an actual iterator type. — end note]
Section: 32.4.3.3 [thread.thread.constr], 32.4.3.6 [thread.thread.member] Status: Core Submitter: jim x Opened: 2025-08-06 Last modified: 2025-10-23
Priority: 4
View other active issues in [thread.thread.constr].
View all other issues in [thread.thread.constr].
View all issues with Core status.
Discussion:
32.4.3.3 [thread.thread.constr] p6 says
The completion of the invocation of the constructor synchronizes with the beginning of the invocation of the copy of
f.
The intended meaning is that "The completion of the invocation of the constructor" happens-before
"the beginning of the invocation of the copy of f.", so we can infer other happen-before relationships
based on this point.
An evaluation A happens before an evaluation B (or, equivalently, B happens after A) if either
(7.1) — […]
(7.2) — A synchronizes with B, or
That means A and B are first required to be expressions because the evaluation is in terms of expressions.
However, "The completion of the invocation of the constructor" and "the beginning of the invocation of the copy off" are not expressions.
The similar issue is 32.4.3.6 [thread.thread.member] p4
The completion of the thread represented by
*thissynchronizes with (6.10.2 [intro.multithread]) the corresponding successfuljoin()return.
[2025-10-23; Reflector poll; Status changed: New → Core and P4.]
P4 at best. Sentiment towards NAD.
Proposed resolution:
std::pmr::polymorphic_allocator::construct just call std::uninitialized_construct_using_allocator?Section: 20.5.3.3 [mem.poly.allocator.mem] Status: New Submitter: Jiang An Opened: 2025-08-06 Last modified: 2025-10-16
Priority: 3
View all other issues in [mem.poly.allocator.mem].
View all issues with New status.
Discussion:
This is closely related to LWG 3901(i) but only affects arguably pathological cases.
Uses-allocator construction for cv-qualified types needs to be well-formed, but it's questionable to requirepolymorphic_allocator::construct to support constructing objects via
pointer to a cv-qualified type. LWG 3870(i) banned such placement construction
for std::construct_at, which is depended by uninitialized_construct_using_allocator.
As a result, it seems non-conforming to just use uninitialized_construct_using_allocator in
polymorphic_allocator::construct since C++20, because the latter is still required to handle
cv-qualified target types. If the status quo is considered unintended and needed to be fixed,
perhaps we can just respecify polymorphic_allocator::construct to use
uninitialized_construct_using_allocator.
[2025-10-15; Reflector poll]
Set priority to 3 after reflector poll.
"What is the extent of the breakage?
pmr::polymorphic_allocator::new_object uses construct,
so does this break users of that function?"
We could modify new_object to allocate and construct
remove_cv_t<T> instead of T,
so that it could still use construct.
Proposed resolution:
This wording is relative to N5014.
[Drafting note: The Mandates: element allows implementations not to add constraints even if
uninitialized_construct_using_allocatoris constrained in the future. The Throws: element is made redundant by "Effects: Equivalent to", and it's already wrong because the exception can be thrown by a conversion function.]
Modify 20.5.3.3 [mem.poly.allocator.mem] as indicated:
template<class T, class... Args> void construct(T* p, Args&&... args);-14- Mandates:
-15- Effects:Uses-allocator construction ofTwith allocator*this(see 20.2.8.2 [allocator.uses.construction]) and constructor argumentsstd::forward<Args>(args)...uninitialized_construct_using_allocator(p, *this, std::forward<Args>(args)...)is well-formed.Constructs aEquivalent to:Tobject in the storage whose address is represented bypby uses-allocator construction with allocator*thisand constructor argumentsstd::forward<Args>(args)....uninitialized_construct_using_allocator(p, *this, std::forward<Args>(args)...);
-16- Throws: Nothing unless the constructor forTthrows.
pair in tuple's allocator_arg_t constructorsSection: 22.4.4.2 [tuple.cnstr] Status: New Submitter: Jiang An Opened: 2025-08-07 Last modified: 2025-10-14
Priority: 3
View other active issues in [tuple.cnstr].
View all other issues in [tuple.cnstr].
View all issues with New status.
Discussion:
Before P0591R4, only scoped_allocator_adaptor::construct and
polymorphic_allocator::construct specially handled pair for the purpose of uses-allocator
construction. The primary definition of uses-allocator construction (in e.g.,
N4659 [allocator.uses.construction]) did not specially handle pair.
The allocator_arg_t constructors of tuple, which were specified to construct tuple
elements with uses-allocator constructor (per e.g., N4659 [tuple.cnstr] p26),
did not specially handle pair either.
make_obj_using_allocator
in [allocator.uses.construction] p1 as:
When applied to the construction of an object of type
T, it is equivalent to initializing it with the value of the expressionmake_obj_using_allocator<T>(alloc, args...), described below.
And the new definition does handle pair. As the specification of allocator_arg_t constructors of
tuple (now in 22.4.4.2 [tuple.cnstr] p33) still refer to uses-allocator construction as-is,
these constructors should construct a pair element in a way equivalent to make_obj_using_allocator now.
#include <cstddef>
#include <utility>
#include <tuple>
#include <memory>
#include <vector>
#include <cassert>
template<class T>
class payload_ator {
int payload{};
public:
using value_type = T;
payload_ator() = default;
constexpr explicit payload_ator(int n) noexcept : payload{n} {}
template<class U>
constexpr explicit payload_ator(payload_ator<U> a) noexcept : payload{a.payload} {}
friend bool operator==(payload_ator, payload_ator) = default;
template<class U>
friend constexpr bool operator==(payload_ator x, payload_ator<U> y) noexcept {
return x.payload == y.payload;
}
constexpr T* allocate(std::size_t n) { return std::allocator<T>{}.allocate(n); }
constexpr void deallocate(T* p, std::size_t n) { return std::allocator<T>{}.deallocate(p, n); }
constexpr int get_payload() const noexcept { return payload; }
};
bool test() {
constexpr int in_v = 42;
using my_pair_t = std::pair<int, std::vector<int, payload_ator<int>>>;
std::tuple<my_pair_t> t(std::allocator_arg, payload_ator<int>{in_v});
auto out_v = std::get<0>(t).second.get_allocator().get_payload();
return in_v == out_v;
}
int main() {
assert(test()); // passes only if allocator_arg_t constructors of tuple specially handle pair
}
However, the behavioral changes of these constructors were not discussed in P0591R4, and existing implementations that claim full implementation of P0591R4 (MSVC STL and libstdc++) did not change these constructors (demo).
Given that implementations did not recognize changes ofallocator_arg_t constructors as part of the paper,
and special handling of pair would significantly complicate these constructors, perhaps we should
explicitly specify that these constructors behave as if special handling for pair were missing.
[2025-08-17; Pablo comments]
I don't agree with it or the PR. It seems like the implementations are simply lagging behind.
Since make_obj_using_allocator is in the standard, tuple is easy enough to implement simply
by delegating to that function. I regard the failure to handle pair in a general way to be a
defect in the C++11 specification and that handling it correctly, though technically a change
of behavior, is more likely to fix bugs than to create them. It is exactly this scattershot
restatement of uses-allocator construction for pair that I hoped to fix with P0591, even
though I missed tuple in my exposition.
[2025-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 22.4.4.2 [tuple.cnstr] as indicated:
template<class Alloc> constexpr explicit(see below ) tuple(allocator_arg_t, const Alloc& a); […] template<class Alloc, class U1, class U2> constexpr explicit(see below) tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&); template<class Alloc, class U1, class U2> constexpr explicit(see below) tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&); template<class Alloc, class U1, class U2> constexpr explicit(see below) tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&); template<class Alloc, class U1, class U2> constexpr explicit(see below) tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&&); template<class Alloc, tuple-like UTuple> constexpr explicit(see below) tuple(allocator_arg_t, const Alloc& a, UTuple&&);-32- Preconditions:
-33- Effects: Equivalent to the preceding constructors except that each element is constructed with uses-allocator construction (20.2.8.2 [allocator.uses.construction]), except that the construction behaves as if there were only oneAllocmeets the Cpp17Allocator requirements (16.4.4.6.1 [allocator.requirements.general]).uses_allocator_construction_argsoverload and the overload behaved the same as the first actual overload without Constraints.
mdspan layout mapping::operator()Section: 23.7.3.4 [mdspan.layout] Status: New Submitter: Luc Grosheintz Opened: 2025-08-13 Last modified: 2025-10-21
Priority: 2
View other active issues in [mdspan.layout].
View all other issues in [mdspan.layout].
View all issues with New status.
Discussion:
Numerous template classes in <mdspan> have template parameter IndexType.
While this template parameter is restricted to be a signed or unsigned integer,
these classes often accept user-defined classes that convert to IndexType.
OtherIndexType; or as a template parameter pack.
When passed as a template parameter pack, the common pattern is
template<class... OtherIndexTypes>
requires std::is_convertible_v<OtherIndexTypes, IndexType> && ...
void dummy(OtherIndexTypes... indices)
{
something(static_cast<IndexType>(std::move(indices))...);
}
This pattern allows passing in objects that convert to IndexType only as an rvalue reference, e.g.
class RValueInt
{
constexpr
operator int() && noexcept
{ return m_int; }
private:
int m_int;
};
This pattern can be found in:
a ctor of extents,
a ctor of mdspan,
mdspan::operator[].
The five standardized layout mappings use a different pattern in their operator(), namely,
static_cast<IndexType>(indices)...
This prevents the passing in objects of type RValueInt, because the
conversion isn't happening from an rvalue reference. This is addressed by
Items 1 - 5 in the Proposed Resolution.
layout_{left,right}_padded.
Namely, directly passing an object of type OtherIndexType to
LEAST-MULTIPLE-AT-LEAST. This is addressed in Items 6 and 7
in the Proposed Resolution.
This inconsistency was noticed while fixing
PR121061
and these changes have been applied to all layout mappings implemented in libstdc++.
[2025-10-21; Reflector poll.]
Set priority to 2 after reflector poll.
The resolution bypasses extents_type::index-cast that would
validate if input value is representable.
We should require convertibility without regard to const and value category.
Proposed resolution:
This wording is relative to N5014.
Modify 23.7.3.4.5.3 [mdspan.layout.left.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... i) const noexcept;-2- Constraints: […]
-3- Preconditions: […] -4- Effects: LetPbe a parameter pack such thatis_same_v<index_sequence_for<Indices...>, index_sequence<P...>>is
true. Equivalent to:return ((static_cast<index_type>(std::move(i)) * stride(P)) + ... + 0);
Modify 23.7.3.4.6.3 [mdspan.layout.right.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... i) const noexcept;-2- Constraints: […]
-3- Preconditions: […] -4- Effects: LetPbe a parameter pack such thatis_same_v<index_sequence_for<Indices...>, index_sequence<P...>>is
true. Equivalent to:return ((static_cast<index_type>(std::move(i)) * stride(P)) + ... + 0);
Modify 23.7.3.4.7.4 [mdspan.layout.stride.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... i) const noexcept;-2- Constraints: […]
-3- Preconditions: […] -4- Effects: LetPbe a parameter pack such thatis_same_v<index_sequence_for<Indices...>, index_sequence<P...>>is
true. Equivalent to:return ((static_cast<index_type>(std::move(i)) * stride(P)) + ... + 0);
Modify 23.7.3.4.8.4 [mdspan.layout.leftpad.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... idxs) const noexcept;-3- Constraints: […]
-4- Preconditions: […] -5- Returns:((static_cast<index_type>(std::move(idxs)) * stride(P_rank)) + ... + 0).
Modify 23.7.3.4.9.4 [mdspan.layout.rightpad.obs] as indicated:
template<class... Indices> constexpr index_type operator()(Indices... idxs) const noexcept;-3- Constraints: […]
-4- Preconditions: […] -5- Returns:((static_cast<index_type>(std::move(idxs)) * stride(P_rank)) + ... + 0).
Modify 23.7.3.4.8.3 [mdspan.layout.leftpad.cons] as indicated:
template<class OtherIndexType> constexpr mapping(const extents_type& ext, OtherIndexType padding);Let
-3- Constraints: […] -4- Preconditions:pad = static_cast<index_type>(std::move(padding)).
(4.1) —
paddingis representable as a value of typeindex_type.(4.2) —
pad is greater than zero.extents_type::index-cast(pad)(4.3) — If
rank_is greater than one, thenLEAST-MULTIPLE-AT-LEAST(pad, ext.extent(0))is representable as a value of typeindex_type.(4.4) — If
rank_is greater than one, then the product ofLEAST-MULTIPLE-AT-LEAST(pad, ext.extent(0))and all valuesext.extent(k)withkin the range of[1, rank_)is representable as a value of typeindex_type.(4.5) — If
padding_valueis not equal todynamic_extent,padding_valueequals.extents_type::index-cast(pad)pad-5- Effects: […]
Modify 23.7.3.4.9.3 [mdspan.layout.rightpad.cons] as indicated:
template<class OtherIndexType> constexpr mapping(const extents_type& ext, OtherIndexType padding);Let
-3- Constraints: […] -4- Preconditions:pad = static_cast<index_type>(std::move(padding)).
(4.1) —
paddingis representable as a value of typeindex_type.(4.2) —
pad is greater than zero.extents_type::index-cast(pad)(4.3) — If
rank_is greater than one, thenLEAST-MULTIPLE-AT-LEAST(pad, ext.extent(rank_ - 1))is representable as a value of typeindex_type.(4.4) — If
rank_is greater than one, then the product ofLEAST-MULTIPLE-AT-LEAST(pad, ext.extent(rank_ - 1))and all valuesext.extent(k)withkin the range of[1, rank_ - 1)is representable as a value of typeindex_type.(4.5) — If
padding_valueis not equal todynamic_extent,padding_valueequals.extents_type::index-cast(pad)pad-5- Effects: […]
{can_}substitute specification is ill-formedSection: 21.4.13 [meta.reflection.substitute] Status: New Submitter: Matthias Wippich Opened: 2025-08-15 Last modified: 2025-10-22
Priority: 1
View all issues with New status.
Discussion:
can_substitute and substitute are currently specified in terms of splices in a template argument list:
Returns:
trueifZ<[:Args:]...>is a valid template-id (13.3 [temp.names]) that does not name a function whose type contains an undeduced placeholder type. Otherwise,false.
21.4.13 [meta.reflection.substitute] p7:
Returns:
^^Z<[:Args:]...>.
This wording was introduced in P2996R11. However, merging in changes from
P3687 "Final Adjustments to C++26 Reflection" in P2996R13 changed
the rules for splices in this context. This makes can_substitute and substitute as specified
currently ill-formed. We cannot use the given syntax to splice an arbitrary choice of values,
types and templates anymore.
[2025-10-22; Reflector poll.]
Set priority to 1 after reflector poll.
Proposed resolution:
Section: 22.10.4 [func.require] Status: New Submitter: Tomasz Kamiński Opened: 2025-08-19 Last modified: 2025-10-14
Priority: 3
View other active issues in [func.require].
View all other issues in [func.require].
View all issues with New status.
Discussion:
The wording for argument forwarding call wrappers in 22.10.4 [func.require] p3,
This forwarding step delivers rvalue arguments as rvalue references and lvalue arguments as lvalue references.
requires that each wrapper binds a temporary to rvalue reference
(materializing it), and then pass that xvalue. This essentially
codifies an implementation where wrappers provide an operator() that
accepts Args&&.... This is fine for most of the wrappers.
bind_front(f)/bind_back(f) without bound args could return a copy of f
bind_front<f>()/bind_back<f>() could produce a
__function_wrapper<f>, that for function pointers can be invoked using
a surrogate function call.
However, such implementation strategies are currently disallowed per
22.10.4 [func.require] p3, as invoking the function wrapper with
a prvalue bind_front(f)(T()) requires a temporary to be materialized,
and then moved into the parameter of f. For example:
struct M
{
M() { std::cout << "Default" < std::endl; }
M(M&& m) { std::cout << "Move" < std::endl; }
};
struct F
{
void operator()(M m) {}
} f;
The call f(M{}) will print only "Default" but bind_front(f)(M{}) is
required to produce "Default" and "Move". We should allow
implementations to elide the move operations, but not require it.
The suggested changes by this issue have been
implemented in libstdc++
for bind_front/bind_back.
[2025-10-14; Reflector poll]
Set priority to 3 after reflector poll.
The submitter prefers to withdraw the issue because the proposed resolution
changes the result of overload resolution in unspecified ways. It would
no longer be specified whether bind_front(F{})({}) works or not,
as it would depend whether bind_front(F{}) returned a call wrapper or the
F object without wrapping. It would also make the effects of
bind_front(S{})(0, 0) unspecified if the call could resolve to either
S::operator()(long, long) or S::operator()(int, void*), depending on
whether bind_front(S{}) returns a call wrapper or the S object.
Proposed resolution:
This wording is relative to N5014.
Modify 22.10.4 [func.require] as indicated:
-3- Every call wrapper (22.10.3 [func.def]) meets the Cpp17MoveConstructible and Cpp17Destructible requirements. An argument forwarding call wrapper is a call wrapper that can be called with an arbitrary argument list and delivers the arguments to the target object
as references. This forwarding step deliversrvalue arguments as rvalue references and lvalue arguments as lvalue references.:
(3.?) — lvalue arguments as lvalues,
(3.?) — xvalue arguments as xvalues,
(3.?) — prvalue arguments as either prvalues or xvalues.
hive operations involving insertion/erasure should have 𝒪(log n) time complexitySection: 23.3.9 [hive] Status: New Submitter: Matt Bentley Opened: 2025-08-19 Last modified: 2025-09-28
Priority: Not Prioritized
View all issues with New status.
Discussion:
Under 23.3.9.4 [hive.modifiers] p4 complexity is stated as "Constant.
Exactly one object of type T is constructed."
Log(n) (on most platforms, log64 or log32) in the
capacity of the element block selected to insert into. This time complexity only occurs
during the operation to find an erased element memory location within a block
which is known to have one, and does not involve the elements themselves.
This is both the simplest and fastest solution to supporting small types
in hive without artificial widening that I have come across.
Further, I have discovered that this approach can be extended to larger
block capacities via
"bitset stacking"
while retaining 𝒪(log n) intra-block lookup time complexity,
regardless of block capacity. Overall this approach would be useful for embedded
and other memory-scarse platforms as it reduces the 16-bit-per-element cost of the
reference implementation down to a 1-bit-per-element cost. For 64-bit and larger types,
there are other ways to obtain this reduction without
losing 𝒪(1) lookup but it is unclear whether those methods would
in fact be faster. For approaches involving bitset-stacking, the logarithmic
complexity also occurs during erasure, specifically adding a couple of extra
instructions for every 64x (i.e. bit-depth) increase in block capacity. But again
this does not involve the elements and is logarithmic in the capacity of the block
erased from.
Regardless, the increased complexity during insertion is necessary for small-type support.
There was ambiguity as to whether this should result in a change to hive
time complexity when discussed on the reflector, as it is unrelated to element numbers (unless
all elements fit within one block), but it is related to block capacities, which are defined
as part of the hive technical specification.
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
[Drafting note: I am unclear on whether
assign()andassign_range()operations would require specification since they also have the capability to reuse existing erased element memory spaces, but we do not currently supply time complexity wording for these in the standard in general and I'm unsure why that is.]
Modify 23.3.9.1 [hive.overview] as indicated:
-1- A
-2- […] -3- Erasures use unspecified techniqueshiveis a type of sequence containerthat provides constant-time insertion and erasure operations. Swhere storage is automatically managed in multiple memory blocks, referred to as element blocks. Insertion position is determined by the container, andinsertionmay re-use the memory locations of erased elements. Insertions are either constant time or logarithmic in the capacity of the element block inserted into.of constant time complexityto identify the memory locations of erased elements, which are subsequently skipped during iteration in constant time, as opposed to relocating subsequent elements during erasure. These techniques are either constant time or logarithmic in the capacity of the element block erased from.Modify 23.3.9.2 [hive.cons] as indicated:
hive& operator=(const hive& x);-25- Preconditions: […]
-26- Effects: […] -27- Complexity: Linear insize() + x.size(). Additionally at worst𝒪(log n)in the capacity of each element block which an element is constructed within.hive& operator=(hive&& x) noexcept(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value);-28- Preconditions: […]
-29- Effects: […] -30- Postconditions: […] -31- Complexity: Linear insize(). If(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || get_allocator() == x.get_allocator())is
false, also linear inx.size()and additionally at worst𝒪(log n)in the capacity of each element block which an element is constructed within.Modify 23.3.9.3 [hive.capacity] as indicated:
void shrink_to_fit();-8- Preconditions: […]
-9- Effects: […] -10- Complexity: If reallocation happens, linear in the size of the sequence and at worst𝒪(log n)in the capacity of each element block which elements are reallocated into. -11- Remarks: […][…]
void reshape(hive_limits block_limits);-21- Preconditions: […]
-22- Effects: […] -23- Postconditions: […] -24- Complexity: Linear in the number of element blocks in*this. If reallocation happens, also linear in the number of elements reallocated and at worst𝒪(log n)in the capacity of each element block which elements are reallocated into. -25- Remarks: […]Modify 23.3.9.4 [hive.modifiers] as indicated:
template<class... Args> iterator emplace(Args&&... args); template<class... Args> iterator emplace_hint(const_iterator hint, Args&&... args);-1- Preconditions: […]
-2- Effects: […] -3- Returns: […] -4- Complexity:ConstantAt worst𝒪(log n)in the capacity of the element block which the element is constructed within. Exactly one object of typeTis constructed.[…]
void insert(initializer_list<T> rg); template<container-compatible-range<T> R> void insert_range(R&& rg);-7- Preconditions: […]
-8- Effects: […] -9- Complexity: Linear in the number of elements inserted. Additionally at worst𝒪(log n)in the capacity of each element block which an element is constructed within. Exactly one object of typeTis constructed for each element inserted. -10- Remarks: […]void insert(size_type n, const T& x);-11- Preconditions: […]
-12- Effects: […] -13- Complexity: Linear inn. Additionally at worst𝒪(log n)in the capacity of each element block which an element is constructed within.. Exactly one object of typeTis constructed for each element inserted. -14- Remarks: […][…]
iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last);-16- Complexity: Linear in the number of elements erased and for each erased element at worst
𝒪(log n)in the capacity of the block containing the element. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks.Modify 23.3.9.5 [hive.operations] as indicated:
[Drafting note: If issue LWG 4323(i) is decided to be acted upon and the proposed solution accepted, the proposed complexity wording becomes:
-11- Complexity: If
empty()isfalse, exactlysize() - 1applications of the corresponding predicate, otherwise no applications of the predicate. For each element erased as a result of this function call, at worst𝒪(log n)in the capacity of the block containing the element. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks.]
template<class BinaryPredicate = equal_to<T>> size_type unique(BinaryPredicate binary_pred = BinaryPredicate());-7- Preconditions: […]
-8- Effects: […] -9- Returns: […] -10- Throws: […] -11- Complexity: Ifempty()isfalse, exactlysize() - 1applications of the corresponding predicate, otherwise no applications of the predicate. Additionally, for each element erased as a result of this function call, at worst𝒪(log n)in the capacity of each block containing the element. -12- Remarks: […]
[2025-09-26; Matt comments and provides improved wording]
Past LWG/LEWG telecon discussion around this topic concluded that because elements are not involved, and the logarithmic action is within the capacity of a block (a fixed number), not the size of the sequence, and the actual performance cost is negligible, that the complexity of these actions are in fact constant. But there is some disagreement on this.
One possibility is to add additional complexity data to each of the effected functions. This would impact onemplace, range insert, fill insert, shrink_to_fit, reshape, copy/move
assignment operator, erase and unique. However I feel this is overkill and may confuse
implementors as the log(n) complexity is not permitted to involve elements.
Having looked into it and sought feedback I think a blanket note on 23.3.9.1 [hive.overview] p3
would be sufficient, such that the time complexity is limited to "techniques to identify the memory
locations of erased elements". Otherwise we need to stay with the previous resolution that this
is in fact constant time behaviour.
Proposed resolution:
This wording is relative to N5014.
Modify 23.3.9.1 [hive.overview] as indicated:
-1- A
-2- […] -3- Erasures use unspecified techniqueshiveis a type of sequence container that provides constant-time insertion and erasure operations. Storage is automatically managed in multiple memory blocks, referred to as element blocks. Insertion position is determined by the container, and may re-use the memory locations of erased elements.of constant time complexityto identify the memory locations of erased elements, which are subsequently skipped during iteration in constant time, as opposed to relocating subsequent elements during erasure. The same or different techniques may be utilized to find and re-use these locations during subsequent insertions. [Note: The techniques are permitted to be at worst logarithmic in the capacity of the element blocks being inserted into or erased from, while maintaining constant-time iteration, to allow latitude for implementation-specific optimizations. — end note]
Section: 20.4 [mem.composite.types] Status: New Submitter: Jonathan B. Coe Opened: 2025-08-20 Last modified: 2025-10-07
Priority: 2
View all issues with New status.
Discussion:
The class templates indirect<T> and polymorphic<T> allow the template argument T
to be an incomplete type.
T is incomplete: constraints are written so that requirements
on incomplete types are not evaluated at class instantiation time.
For constructors with additional template parameters, there are currently constraints written on the potentially
incomplete type T and the additional template parameters. Such constraints will not be evaluated at class
instantiation time but could be explicitly evaluated in contexts where support for an incomplete T is required.
template<typename U>
class A {
U u;
public:
A(const SomeType&) requires std::is_constructible_v<U, SomeType>
{
// […]
}
};
when U is indirect<T> or polymorphic<T> for some type T, the existence
of the requires clause will require that T is a complete type for constraints on indirect or polymorphic
to be evaluated.
T should be converted to Mandates on T so that constraint evaluation
does not require T to be a complete type.
[2025-10-07; Reflector poll]
Set priority to 2 after reflector poll.
"Do we have a concrete use case of an A-like type? Is making the traits give
wrong answers preferable to hard errors? I don't think the polymorphic part
makes sense. Since the constraints still require U/UU to be complete,
either T is a base class and it must be complete, or it is not and it doesn't
matter."
"The problematic members are non-template functions, so they are instantiated early and the requires clauses are checked too soon. Think this can be solved without giving up SFINAE-friendliness, by making the affected members themselves templates."
Proposed resolution:
This wording is relative to N5014.
Modify 20.4.1.3 [indirect.ctor] as indicated:
template<class U = T> constexpr explicit indirect(U&& u);-17- Constraints:
(17.1) —
is_same_v<remove_cvref_t<U>, indirect>isfalse,(17.2) —
is_same_v<remove_cvref_t<U>, in_place_t>isfalse, and
(17.3) —is_constructible_v<T, U>istrue, and(17.4) —
is_default_constructible_v<Allocator>istrue.-?- Mandates:
-18- Effects: […]is_constructible_v<T, U>istrue.template<class U = T> constexpr explicit indirect(allocator_arg_t, const Allocator& a, U&& u);-19- Constraints:
(19.1) —
is_same_v<remove_cvref_t<U>, indirect>isfalse, and(19.2) —
is_same_v<remove_cvref_t<U>, in_place_t>isfalse., and
(19.3) —is_constructible_v<T, U>istrue-?- Mandates:
-20- Effects: […]is_constructible_v<T, U>istrue.template<class... Us> constexpr explicit indirect(in_place_t, Us&&... us);-21- Constraints:
(21.1) —is_constructible_v<T, Us...>istrue, and
(21.2) —is_default_constructible_v<Allocator>istrue.-?- Mandates:
-22- Effects: […]is_constructible_v<T, Us...>istrue.template<class... Us> constexpr explicit indirect(allocator_arg_t, const Allocator& a, in_place_t, Us&& ...us);-23-
-24- Effects: […]ConstraintsMandates:is_constructible_v<T, Us...>istruetemplate<class I, class... Us> constexpr explicit indirect(in_place_t, initializer_list<I> ilist, Us&&... us);-25- Constraints:
(25.1) —is_constructible_v<T, initializer_list<I>&, Us...>istrue, and
(25.2) —is_default_constructible_v<Allocator>istrue.-?- Mandates:
-26- Effects: […]is_constructible_v<T, initializer_list<I>&, Us...>istrue.template<class I, class... Us> constexpr explicit indirect(allocator_arg_t, const Allocator& a, in_place_t, initializer_list<I> ilist, Us&&... us);-27-
-28- Effects: […]ConstraintsMandates:is_constructible_v<T, initializer_list<I>&, Us...>istrue
Modify 20.4.2.3 [polymorphic.ctor] as indicated:
template<class U = T> constexpr explicit polymorphic(U&& u);-12- Constraints: Where
UUisremove_cvref_t<U>,
(12.1) —
is_same_v<UU, polymorphic>isfalse,
(12.2) —derived_from<UU, T>istrue,(12.3) —
is_constructible_v<UU, U>istrue,(12.4) —
is_copy_constructible_v<UU>istrue,(12.5) —
UUis not a specialization ofin_place_type_t, and(12.6) —
is_default_constructible_v<Allocator>istrue.-?- Mandates:
-13- Effects: […]derived_from<UU, T>istrue.template<class U = T> constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, U&& u);-14- Constraints: Where
UUisremove_cvref_t<U>,
(14.1) —
is_same_v<UU, polymorphic>isfalse,
(14.2) —derived_from<UU, T>istrue,(14.3) —
is_constructible_v<UU, U>istrue,(14.4) —
is_copy_constructible_v<UU>istrue, and(14.5) —
UUis not a specialization ofin_place_type_t.-?- Mandates:
-15- Effects: […]derived_from<UU, T>istrue.template<class U, class... Ts> constexpr explicit polymorphic(in_place_type_t<U>, Ts&&... ts);-16- Constraints:
(16.1) —
is_same_v<remove_cvref_t<U>, U>istrue,
(16.2) —derived_from<U, T>istrue,(16.3) —
is_constructible_v<U, Ts>istrue,(16.4) —
is_copy_constructible_v<U>istrue, and(16.5) —
is_default_constructible_v<Allocator>istrue.-?- Mandates:
-17- Effects: […]derived_from<U, T>istrue.template<class U, class... Ts> constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, in_place_type_t<U>, Ts&&... ts);-18- Constraints:
(18.1) —
is_same_v<remove_cvref_t<U>, U>istrue,
(18.2) —derived_from<U, T>istrue,(18.3) —
is_constructible_v<U, Ts>istrue, and(18.4) —
is_copy_constructible_v<U>istrue.-?- Mandates:
-19- Effects: […]derived_from<U, T>istrue.template<class U, class I, class... Us> constexpr explicit polymorphic(in_place_type_t<U>, initializer_list<I> ilist, Us&&... us);-20- Constraints:
(20.1) —
is_same_v<remove_cvref_t<U>, U>istrue,
(20.2) —derived_from<U, T>istrue,(20.3) —
is_constructible_v<U, initializer_list<I>&, Us...>istrue,(20.4) —
is_copy_constructible_v<U>istrue, and(20.5) —
is_default_constructible_v<Allocator>istrue.-?- Mandates:
-21- Effects: […]derived_from<U, T>istrue.template<class U, class I, class... Us> constexpr explicit polymorphic(allocator_arg_t, const Allocator& a, in_place_type_t<U>, initializer_list<I> ilist, Us&&... us);-22- Constraints:
(22.1) —
is_same_v<remove_cvref_t<U>, U>istrue,
(22.2) —derived_from<U, T>istrue,(22.3) —
is_constructible_v<U, initializer_list<I>&, Us...>istrue, and(22.4) —
is_copy_constructible_v<U>istrue.-?- Mandates:
-23- Effects: […]derived_from<U, T>istrue.
hive::unique time complexity should incorporate potential block removal complexitySection: 23.3.9.5 [hive.operations] Status: New Submitter: Matt Bentley Opened: 2025-08-24 Last modified: 2025-10-22
Priority: 3
View all issues with New status.
Discussion:
Currently we specify for erase 23.3.9.4 [hive.modifiers] p16:
Complexity: Linear in the number of elements erased. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks.
This is to allow for potential hive implementations as vectors of
pointers to blocks, as opposed to linked-lists of blocks (reference
implementation approach). In that scenario removing a block would
involve a vector erasure and subsequent pointer-to-block relocations.
swap-and-pop of block pointers would not work as this would re-arrange
the sequence during erasure. Anyway, we have neglected to apply this
same complexity to hive::unique. It would be rare that this would occur
for unique, as it would involve (1) a block A with only one non-erased
element left in it and (2) a block B preceding/following block
A in the sequence, whose last/first element is equal to the element
in block A. But it is possible, so the same consideration in terms
of time complexity applies.
This is a separate but related issue to LWG 4320(i), but it would affect the outcome wording of that issue for
unique.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 23.3.9.5 [hive.operations] as indicated:
[Drafting note: If issue LWG 4320(i) is decided to be acted upon and the proposed solution accepted, the proposed complexity wording becomes:
-11- Complexity: If
empty()isfalse, exactlysize() - 1applications of the corresponding predicate, otherwise no applications of the predicate. For each element erased as a result of this function call, at worst𝒪(log n)in the capacity of the block containing the element. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks.]
template<class BinaryPredicate = equal_to<T>> size_type unique(BinaryPredicate binary_pred = BinaryPredicate());-7- Preconditions: […]
-8- Effects: […] -9- Returns: […] -10- Throws: […] -11- Complexity: Ifempty()isfalse, exactlysize() - 1applications of the corresponding predicate, otherwise no applications of the predicate. Additionally, if any active blocks become empty of elements as a result of the function call, at worst linear in the number of element blocks. -12- Remarks: […]
unique_ptr<void>::operator* is not SFINAE-friendlySection: 20.3.1.3.5 [unique.ptr.single.observers] Status: New Submitter: Hewill Kang Opened: 2025-08-24 Last modified: 2025-10-22
Priority: 3
View other active issues in [unique.ptr.single.observers].
View all other issues in [unique.ptr.single.observers].
View all issues with New status.
Discussion:
LWG 2762(i) added a conditional noexcept specification to unique_ptr::operator*
to make it consistent with shared_ptr::operator*:
constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));
This unexpectedly makes unique_ptr<void>::operator* no longer SFINAE-friendly,
for example:
#include <memory>
template<class T> concept dereferenceable = requires(T& t) { *t; };
static_assert( dereferenceable<int *>);
static_assert(!dereferenceable<void*>);
static_assert( dereferenceable<std::shared_ptr<int >>);
static_assert(!dereferenceable<std::shared_ptr<void>>);
static_assert( dereferenceable<std::unique_ptr<int >>);
static_assert( dereferenceable<std::unique_ptr<void>>); // hard error
Given that the standard intends for operator* of shared_ptr and unique_ptr to be
SFINAE-friendly based on 20.3.2.2.6 [util.smartptr.shared.obs], regardless of the value of
static_assert, it is reasonable to assume that there should be no hard error here.
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
Modify 20.3.1.3.5 [unique.ptr.single.observers] as indicated:
constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>())see below);-1- Mandates:
-2- Preconditions:reference_converts_from_temporary_v<add_lvalue_reference_t<T>, decltype(*declval<pointer>())>isfalse.get() != nullptristrue. -3- Returns:*get(). -?- Remarks:: The exception specification is equivalent to:!requires { *declval<pointer>(); } || requires { { *declval<pointer>() } noexcept; }
[2025-08-26; Reflector discussion]
During reflector triaging it had been pointed out that a better solution would be to constrain the
operator* directly. The proposed wording has been updated to that effect.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 20.3.1.3.5 [unique.ptr.single.observers] as indicated:
constexpr add_lvalue_reference_t<T> operator*() const noexcept(noexcept(*declval<pointer>()));-?- Constraints:
-1- Mandates:*declval<pointer>()is a well-formed expression.reference_converts_from_temporary_v<add_lvalue_reference_t<T>, decltype(*declval<pointer>())>isfalse. -2- Preconditions:get() != nullptristrue. -3- Returns:*get().
std::indirect's operator== still does not support incomplete typesSection: 20.4.1.8 [indirect.relops], 20.4.1.9 [indirect.comp.with.t] Status: New Submitter: Hewill Kang Opened: 2025-08-24 Last modified: 2025-10-14
Priority: 2
View all issues with New status.
Discussion:
std::indirect's `operator==
intentionally
uses Mandates instead of Constraints to support incomplete types. However, its
function signature has the following noexcept specification:
template<class U, class AA>
constexpr bool operator==(const indirect& lhs, const indirect<U, AA>& rhs)
noexcept(noexcept(*lhs == *rhs));
That is, we check whether the expression *lhs == *rhs throws, which unfortunately leads to
the following hard error:
struct Incomplete;
static_assert(std::equality_comparable<std::indirect<Incomplete>>);
// hard error, no match for 'operator==' (operand types are 'const Incomplete' and 'const Incomplete')
This makes operator== not SFINAE-friendly for incomplete types, which defeats the purpose.
noexcept(*lhs == *rhs) seems insufficient because the result of *lhs == *rhs
might still throw during conversion to bool.
We could add a note similar to 22.10.6.1 [refwrap.general].
[2025-10-14; Reflector poll]
Set priority to 2 after reflector poll.
Even if we don't want to support incomplete types, we still need to fix
the noexcept-specifier to consider whether the conversion to bool throws,
e.g. noexcept(noexcept(bool(*lhs == *rhs))).
"The proposed resolution may result in the noexcept operator giving different
results when evaluated in different translation units where the type T of
indirect was incomplete or not. Ill-formed seems safer than inconsistent."
Proposed resolution:
This wording is relative to N5014.
[Drafting note:: We introduce the exposition-only function
Please note that the seemingly unresolvedFUNbelow to mimic the implicit conversion tobool. As a drive-by effect this helps us simplifying (and clarifying, see LWG 484(i)) the existing Mandates element.Tin therequiresexpression below names the first template parameter of theindirectclass template. ]
Modify 20.4.1.8 [indirect.relops] as indicated:
template<class U, class AA> constexpr bool operator==(const indirect& lhs, const indirect<U, AA>& rhs) noexcept(noexcept(*lhs == *rhs)see below);-?- Let
FUNdenote the exposition-only functionbool FUN(bool) noexcept;-1- Mandates: The expression
-2- Returns: IfFUN(*lhs == *rhs)is well-formedand its result is convertible to.boollhsis valueless orrhsis valueless,lhs.valueless_after_move() == rhs.valueless_after_move(); otherwise*lhs == *rhs. -?- Remarks: The exception specification is equivalent to:requires (const T& lhs, const U& rhs) { { FUN(lhs == rhs) } noexcept; }
Modify 20.4.1.9 [indirect.comp.with.t] as indicated:
template<class U> constexpr bool operator==(const indirect& lhs, const U& rhs) noexcept(noexcept(*lhs == rhs)see below);-?- Let
FUNdenote the exposition-only functionbool FUN(bool) noexcept;-1- Mandates: The expression
-2- Returns: IfFUN(*lhs == rhs)is well-formedand its result is convertible to.boollhsis valueless,false; otherwise*lhs == rhs. -?- Remarks: The exception specification is equivalent to:requires (const T& lhs, const U& rhs) { { FUN(lhs == rhs) } noexcept; }
forwarding_query_t by inheriting from itSection: 33.5.1 [exec.fwd.env] Status: New Submitter: Lewis Baker Opened: 2025-08-25 Last modified: 2025-10-17
Priority: 4
View all issues with New status.
Discussion:
The definition of the forwarding_query_t (33.5.1 [exec.fwd.env]) states that a query
is a forwarding-query if either the expression q.query(forwarding_query) is a constant expression
that evaluates to true or the query type inherits from forwarding_query_t.
struct get_allocator_t { unspecified };
While this definition allows for the members of the class to be unspecified, it still explicitly
specifies that it has an empty list of base-classes and therefore cannot customize
forwarding_query_t by inheriting from forwarding_query_t.
forwarding_query_t or otherwise remove the
support for customizing forwarding_query_t by inheriting from it.
Proposed Approach:
Remove the rule that checks for inheritance from forwarding_query_t. Any query can already
customize forwarding_query_t by defining a query(forwarding_query_t) member function, so
there is no loss of functionality from doing this and it would simplify the design.
[2025-10-17; Reflector poll.]
Set priority to 4 after reflector poll.
"The fact that we didn't use customization by derivation yet doesn't mean we should remove that customization mechanism."
Proposed resolution:
This wording is relative to N5014.
Modify 33.5.1 [exec.fwd.env] as indicated:
-2- The name
forwarding_querydenotes a query object. For some query objectqof typeQ,forwarding_query(q)is expression-equivalent to:
(2.1) —
Mandates: The expression above has typeMANDATE-NOTHROW(q.query(forwarding_query))if that expression is well-formed.booland is a core constant expression ifqis a core constant expression.
(2.2) — Otherwise,trueifderived_from<Q, forwarding_query_t>istrue.(2.3) — Otherwise,
false.
Section: 33.6 [exec.sched] Status: New Submitter: Lewis Baker Opened: 2025-08-25 Last modified: 2025-10-23
Priority: 3
View other active issues in [exec.sched].
View all other issues in [exec.sched].
View all issues with New status.
Discussion:
The wording 33.6 [exec.sched] p4 says:
For any two values
sch1andsch2of some scheduler typeSch,sch1 == sch2shall returntrueonly if bothsch1andsch2share the same associated execution resource.
However, I don't think this requirement is sufficient for schedulers to be considered equal as it is possible to have two different schedulers that share the same associated execution resource but that have different behaviour.
For example, two schedulers of the same type and having the same associated execution resource but where one schedules items with high priority and others with low priority. Ideally we want two schedulers to be equal if and only if they can be used interchangeably such that using either scheduler to schedule work has the same behaviour. Note that some use cases may also want to compare two schedulers as to whether or not they share the same associated execution resource, independently of their scheduling behaviour, however this could be added as a separate operator later, e.g.std::execution::same_resource(sch1, sch2).
While the above wording does still permit implementations to return false even if the associated
execution resources are the same, it is not clear how this definition applies to schedulers
such as an inline_scheduler (added in P3552) in which has no specific associated
execution resource but where instances nevertheless have the same scheduling behaviour.
One approach to improving this would be to change p4 to refer to the schedulers having the
same scheduling behaviour rather than having the same associated execution resource.
For example, modify 33.6 [exec.sched] p4 to refer to schedulers having the same
scheduling behaviour rather than having the same associated execution resource.
For any two values
sch1andsch2of some scheduler typeSch,sch1 == sch2shall returntrueonly if bothsch1andsch2share the same associated execution resourcehave the same scheduling behaviour.
However, this then raises the question of exactly what "the same scheduling behaviour" is.
For example, what if two schedulers have the same associated execution resource and the
same behaviour for a schedule() operation, but have different behaviours for customizations
of the bulk() algorithm?
[2025-10-23; Reflector poll.]
Set priority to 3 after reflector poll.
[2025-10-23; .]
"Would prefer if 'the same scheduling behavior' was added, instead of replacing existing wording."
"scheduler requires equality_comparable, so equality of schedulers must be
equality preserving, so equality already implies scheduler interchangeability.
Any additional specification is redundant and should be struck."
"I think task_scheduler(parallel_scheduler) == parallel_scheduler currently
(and possibly incorrectly) says true, because they share the same execution
resource. But the behaviour is dramatically different: when using bulk
the first will execute sequentially and the second will execute in parallel."
Proposed resolution:
Section: 33.13.3 [exec.affine.on] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-23
Priority: 3
View other active issues in [exec.affine.on].
View all other issues in [exec.affine.on].
View all issues with LEWG status.
Discussion:
There are no customisations of affine_on for other
algorithms specified. For example, affine_on(just(), sched)
can be equivalent to just() because just()
completes inline and, thus, on the correct execution context.
[2025-10-23; Reflector poll. Status changed: New → LEWG]
Set priority to 3 after reflector poll. Send to LEWG.
"Becomes moot if we remove customization entirely."
Proposed resolution:
affine_on semanticsSection: 33.13.3 [exec.affine.on] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-23
Priority: 2
View other active issues in [exec.affine.on].
View all other issues in [exec.affine.on].
View all issues with LEWG status.
Discussion:
The specification of affine_on uses "current execution
resource" and it is unclear what that means exactly. Additionally,
it is unclear what the difference between affine_on
and continues_on is. The intended difference for
affine_on is to avoid unnecessary scheduling which
continues_on is already allowed to do in some cases,
too.
The intended semantics is that affine_on will either
complete inline on whatever execution agent it was started on or
it will complete asynchronously on the specified execution context.
With this formulation affine_on may complete on one
of two different execution context if it is started on an execution
context that is different from the one specified by the scheduler.
[2025-10-23; Reflector poll. Status changed: New → LEWG]
Set priority to 2 after reflector poll. Send to LEWG.
Proposed resolution:
affine_on shape may be wrongSection: 33.13.3 [exec.affine.on] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-17
Priority: 2
View other active issues in [exec.affine.on].
View all other issues in [exec.affine.on].
View all issues with LEWG status.
Discussion:
affine_on is specified to take a sender and a scheduler
as arguments. The scheduler is meant to match the scheduler obtained
from the get_scheduler query on the receiver's
environment. Thus, the scheduler argument is redundant and the
semantics become weird if these two schedulers don't match. The
affine_on algorithm should only take the sender as
argument.
[2025-10-17; Reflector poll. Status changed: New → LEWG.]
Set priority to 2 after reflector poll. Send to LEWG.
Proposed resolution:
affine_on shouldn't forward the stop token to the scheduling operationSection: 33.13.3 [exec.affine.on] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-23
Priority: 2
View other active issues in [exec.affine.on].
View all other issues in [exec.affine.on].
View all issues with LEWG status.
Discussion:
The main purpose of affine_on is to make sure work
resumes on a specific execution context. The scheduling operation
may take some time and work may be cancelled in the meantime. If
this cancellation causes the scheduling to be cancelled work cleaning
up after the cancellation would be executed on the wrong execution
context. Thus, the stop token from the receiver's environment should
only be forwarded when connecting the sender but not
to the scheduling operation.
[2025-10-23; Reflector poll. Status changed: New → LEWG]
Set priority to 2 after reflector poll. Send to LEWG.
"This affects the cancelation behaviour so should also be seen by SG1."
Proposed resolution:
task uses unusual allocator customisationSection: 33.13.6.5 [task.promise] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-17
Priority: 2
View other active issues in [task.promise].
View all other issues in [task.promise].
View all issues with LEWG status.
Discussion:
Unlike generator the allocator customisation of
task constraints the allocator type used for the
coroutine to be convertible to the configured
allocator_type. This prevents easy use of an allocator
especially when no allocator is configured and the default
(std::allocator<std::byte>) is used. The reason
for this constraint is that the get_allocator is
forwarded to co_awaited senders and is intended to be
the same as the allocator used for the coroutine frame.
It may be reasonable to allow use of an arbitrary allocator when
there is no explicit configuration of the allocator_type.
In this case it may also be resonable to not support the
get_allocator query when co_awaiting
senders.
[2025-10-17; Reflector poll. Status changed: New → LEWG.]
Set priority to 2 after reflector poll. Send to LEWG.
Proposed resolution:
task<...>::promise_type supports arbitrary allocator_arg positionSection: 33.13.6.5 [task.promise] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-17
Priority: 2
View other active issues in [task.promise].
View all other issues in [task.promise].
View all issues with LEWG status.
Discussion:
Normally the allocator_arg argument has to be the first
argument when present. For task<...>::promise_type
the allocator_arg can appear at an arbitrary position
(except the last because it always needs to be followed by the
allocator). This permission is inconsistent and the position of the
allocator_arg argument and the allocator should be
limited to come first.
For containers the optional support for allocators is implemented
once for every container. For coroutines the optional support for
allocators is implemented once for every coroutine definition. To
support an optional allocator the coroutine definition needs to use
an allocator and either gets duplicated not using an allocator or
a forwarding function is added which adds the default allocator.
With the flexible allocator position optional allocator support can be provided
using a trailing argument list, i.e., adding , auto&&....
Instead of constraining task it may be more reasonable
to add the flexibility to generator.
[2025-10-17; Reflector poll. Status changed: New → LEWG.]
Set priority to 2 after reflector poll.
Proposed resolution:
task shadows the environment's allocatorSection: 33.13.6.5 [task.promise] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-17
Priority: 2
View other active issues in [task.promise].
View all other issues in [task.promise].
View all issues with LEWG status.
Discussion:
Normally, the get_allocator query forwards the allocator
from the receiver's environment. For task the
get_allocator query used for co_awaited
senders uses the allocator passed when creating the coroutine or
the default if there was none. It should use the receiver's
environment, at least, if the receiver's environment supports a
get_allocator query.
Supporting the receiver's allocator isn't always possible: the used allocator type needs to be known when the coroutine is created. At that time the receiver isn't known, yet. As a result the receiver's environment may provide an allocator which is incompatible with the allocator type used by the coroutine. It may be possible to use the receiver's allcoator if it is convertible to the allocator type used by the coroutine and to produce a compile-time error otherwise.
[2025-10-17; Reflector poll. Status changed: New → LEWG.]
Set priority to 2 after reflector poll. Send to LEWG.
Proposed resolution:
bulk vs. task_schedulerSection: 33.13.5 [exec.task.scheduler] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-23
Priority: 2
View other active issues in [exec.task.scheduler].
View all other issues in [exec.task.scheduler].
View all issues with LEWG status.
Discussion:
Normally, the scheduler type used by an operation can be deduced
when a sender is connected to a receiver from the
receiver's environment. The body of a coroutine cannot know about
the receiver the task sender gets connected
to. The implication is that the type of the scheduler used by the
coroutine needs to be known when the task is created.
To still allow custom schedulers used when connecting, the type-erased
scheduler task_scheduler is used. However, that leads
to surprises when algorithms are customised for a scheduler as is,
e.g., the case for bulk when used with a
parallel_scheduler: if bulk is
co_awaited within a coroutine using
task_scheduler it will use the default implementation
of bulk which sequentially executes the work, even if
the task_scheduler was initialised with a
parallel_scheduler (the exact invocation may actually
be slightly different or need to use bulk_chunked or
bulk_unchunked but that isn't the point being made):
struct env {
auto query(ex::get_scheduler_t) const noexcept { return ex::parallel_scheduler(); }
};
struct work {
auto operator()(std::size_t s){ /*...*/ };
};
ex::sync_wait(
ex::write_env(ex::bulk(ex::just(), 16u, work{}),
env{}
));
ex::sync_wait(ex::write_env(
[]()->ex::task<void, ex::env<>>>{ co_await ex::bulk(ex::just(), 16u, work{}); }(),
env{}
));
The two invocations should probably both execute the work in parallel
but the coroutine version doesnt: it uses the task_scheduler
which doesnt have a specialised version of bulk to
potentially delegate in a type-erased form to the underlying
scheduler. It is straight forward to move the write_env
wrapper inside the coroutine which fixes the problem in this case
but this need introduces the potential for a subtle performance
bug. The problem is sadly not limited to a particular scheduler or
a particular algorithm: any scheduler/algorithm combination which
may get specialised can suffer from the specialised algorithm not
being picked up.
There are a few ways this problem can be addressed (this list of options is almost certainly incomplete):
task_scheduler.task_scheduler to deal
with a set of algorithms for which it provides a type-erased
interface. The interface would likely be more constrained and it
would use virtual dispatch at run-time. However, the set of covered
algorithms would necessarily be limited in some form.task_scheduler, i.e., customise these
algorithms for task_scheduler such that a compile-time
error is produced.
A user who knows that the main purpose of a coroutine is to executed
an algorithm customised for a certain scheduler can use task<T,
E> with an environment E specifying exactly
that scheduler type. However, this use may be nested within some
sender being co_awaited and users need to be aware
that the customisation wouldnt be picked up. Any approach I'm
currently aware of will have the problem that customised versions
of an algorithm are not used for algorithms we are currently unaware
of.
[2025-10-23; Reflector poll. Status changed: New → LEWG]
Set priority to 2 after reflector poll. Send to LEWG.
"It seems like there are several kinds of problems with sender algorithm customization that have related causes and might have a single solution."
Proposed resolution:
co_await a taskSection: 33.13.6.2 [task.class] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-23
Priority: 2
View other active issues in [task.class].
View all other issues in [task.class].
View all issues with LEWG status.
Discussion:
The task type doesn't have an operator
co_await(). Thus, a task can't be
co_awaited directly from a coroutine. Using
as_awaitable on the task object also
doesn't work because this function requires a reference to the
promise as second argument.
task could define an operator co_await() that
returns an awaitable object or directly provide an awaiter
interface. There are, however, two complications:
task needs to get a
scheduler from the object starting the task. While
the promise can be obtained from the coroutine handle passed to
await_suspend() these normally wouldn't have an
associated environment supporting a get_scheduler query.
unhandled_stopped() on the promise type which is
generally not available. It could be argued that cancellation isn't
really a problem because the it is unlikely that the environment
associated with the promise supports a get_stop_token
query.
It is worth noting that senders in general do not support an
operator co_await(), i.e., other senders are also not
awaitable directly. On the other hand, task could
become the generic adapater to make senders awaitable from
coroutines.
[2025-10-23; Reflector poll. Status changed: New → LEWG]
Set priority to 2 after reflector poll. Send to LEWG.
Proposed resolution:
task's coroutine frame may be released lateSection: 33.13.6 [exec.task] Status: New Submitter: Dietmar Kühl Opened: 2025-08-31 Last modified: 2025-10-17
Priority: 2
View all issues with New status.
Discussion:
The specification of task doesn't spell out when the
coroutine frame is destroyed (i.e., when handle.destroy()
is called). As a result the lifetime of arguments passed to the
coroutine and/or of local variables in the coroutine body may be
longer than expected.
The intention is that the coroutine frame is destroyed before any
of the completion functions is called. One implication of this
requirement is that the result and error objects can't be stored
in the promise_type when the completion function is
called although the exposition-only members result
and errors imply exactly that. Instead the data
needs to be stored in the operation state object or it needs to be
moved to a different place before calling destroy().
The proposed resolution is to add a paragraph to the specification
of promise_type in 33.13.6.5 [task.promise] that spells
out that the coroutine frame is destroyed before any of the completion
functions is called. To actually do that the exposition-only members
result and errors can't
remain as members of the promise_type. While writing
the relevant change it turned out that errors
is a variant which only ever stores an
exception_ptr (the other potential errors are immediately
reported via the awaiter return from yield_value).
Thus, the variant can be replaced with an
exception_ptr.
[2025-10-17; Reflector poll.]
Set priority to 2 after reflector poll.
"P/R is incomplete - also needs to update at least promise_type::uncaught_exception()."
Proposed resolution:
In 33.13.6.4 [task.state], add
exposition-only data members result and
error to the exposition-only class
state:
namespace std::execution {
template<class T, class Environment>
template<receiver Rcvr>
class task<T, Environment>::state { // exposition only
...
Environment environment; // exposition only
optional<T> result; // exposition only; present only if is_void_v<T> is false
exception_ptr error; // exposition-only
};
}
Remove the exposition-only data members
result and errors from
the class promise_type in
33.13.6.5 [task.promise]:
namespace std::execution {
template<class T, class Environment>
class task<T, Environment>::promise_type {
...
stop_token_type token; // exposition only
optional<T> result; // exposition only; present only if is_void_v<T> is false
error-variant errors; // exposition only
};
}
The definition of error-variant isn't needed, i.e., remove 33.13.6.5 [task.promise] paragraph 2:
-2-error-variantis avariant<monostate, remove_cvref_t<E>...>, with duplicate types removed, whereE...are template arguments of the specialization ofexecution::completion_signaturesdenoted byerror_types.
In 33.13.6.5 [task.promise] change paragraph 7 to use the members added to state:
auto final_suspend() noexcept-7- Returns: An awaitable object of unspecified type (7.6.2.4 [expr.await]) whose member functions arrange for the completion of the asynchronous operation associated with
STATE(*this)by invoking. Letstbe a reference toSTATE(*this). The asynchronous completion first destroys the coroutine frame usingst.handle.destroy()and then invokes:
- -7.1-
set_error(std::move(ifRCVR(*this)st.rcvr), std::move(est.error))errors.index()is greater than zero andeis the value held byerrorsbool(st.error)istrue, otherwise- -7.2-
set_value(std::move(ifRCVR(*this)st.rcvr))is_void<T>istrue, and otherwise- -7.3-
set_value(std::move(.RCVR(*this)st.rcvr), *st.result)
Change the specification of yield_value to destroy the coroutine frame before invoking the set_error completion, i.e., change 33.13.6.5 [task.promise] paragraph 9:
-9- Returns: An awaitable object of unspecified type ([expr.await]) whose member functions arrange for the calling coroutine to be suspended and then completes the asynchronous operation associated with
STATE(*this)by. Letstbe a reference toSTATE(*this). Then the asynchronous operation completes by first destroying the coroutine frame usingst.handle.destroy()and then invokingset_error(std::move(.RCVR(*this)st.rcvr), Cerr(std::move(err.error)))
Change the specification of unhandled_stopped to destroy the coroutine frame before invoking the set_stopped completion, i.e., change 33.13.6.5 [task.promise] paragraph 13:
coroutine_handle<> unhandled_stopped();-13- Effects: Completes the asynchronous operation associated with
STATE(*this)by. Letstbe a reference toSTATE(*this). The asynchronous operation is completed by first destroying the coroutine frame usingst.handle.destroy()and then invokingset_stopped(std::move(.RCVR(*this)st.rcvr))
affine_on has no specification for the defaultSection: 33.13.3 [exec.affine.on] Status: New Submitter: Dietmar Kühl Opened: 2025-09-01 Last modified: 2025-10-17
Priority: 2
View other active issues in [exec.affine.on].
View all other issues in [exec.affine.on].
View all issues with New status.
Discussion:
The wording of affine_on doesnt have a specification
for the default implementation. For other algorithms the default
implementation is specified.
The intention for affine_on was to all
optimisation/customisation in a way reducing the necessary scheduling:
if the implementation can determine if a sender completed already
on the correct execution agent it should be allowed to avoid
scheduling. A specification should provide enough lattitude to
allow that.
[2025-10-17; Reflector poll.]
Set priority to 2 after reflector poll.
Proposed resolution:
Add a new paragraph to the specification of affine_on
in 33.13.3 [exec.affine.on] providing
a specification for the default implementation:
-6- Let
sndrandenvbe subexpressions such thatSndrisdecltype((sndr)). Ifsender-for<Sndr, affine_on_t>isfalse, then the expressionaffine_on.transform_sender(sndr, env)is ill-formed; otherwise, it is equivalent to:auto [_, sch, child] = sndr; return transform_sender( query-with-default(get_domain, sch, default_domain()), continues_on(std::move(child), std::move(sch)));except that
schis only evaluated once.
task's stop source is always createdSection: 33.13.6.5 [task.promise] Status: New Submitter: Dietmar Kühl Opened: 2025-09-01 Last modified: 2025-10-17
Priority: 2
View other active issues in [task.promise].
View all other issues in [task.promise].
View all issues with New status.
Discussion:
The type task<...>::promise_type has exposition-only
members source and token.
These can be interpreted as always existing which would be a
performance issue for former and an unnecessary constraints for the
latter (because stop tokens aren't required to be default constructible).
The intent is that the stop token obtained from the
get_stop_token query of the receiver's environment is
used. Only if this type is different from the task's
stop_token_type a stop source of type
stop_source_type needs to be created when the
get_stop_token query is used on the promise type's
environment. The stop token doesn't need to be stored at all: it
can either be obtained from the receiver's environment or from the
stop source. The fix is to show the stop source as an optionally
present member of of the operation state and it should be of type
std::optional<stop_source_type> to imply that
it is only created when accessed.
[2025-10-17; Reflector poll.]
Set priority to 2 after reflector poll.
Proposed resolution:
task doesn't support symmetric transferSection: 33.13.6.2 [task.class] Status: LEWG Submitter: Dietmar Kühl Opened: 2025-09-01 Last modified: 2025-10-17
Priority: 2
View other active issues in [task.class].
View all other issues in [task.class].
View all issues with LEWG status.
Discussion:
The specification of task doesn't require symmetric
transfer which can help with stack overflow. Also, when another
task is co_awaited the scheduler on which
the task resumes is known and can be used to avoid
unnecessary scheduling by comparing the scheduler currently installed
by in two tasks involved.
[2025-10-17; Reflector poll. Status changed: New → LEWG.]
Set priority to 2 after reflector poll. Send to LEWG.
Proposed resolution:
insert_range when available?Section: 23.6.8 [flat.map], 23.6.9 [flat.multimap], 23.6.11 [flat.set], 23.6.12 [flat.multiset] Status: LEWG Submitter: Jonathan Wakely Opened: 2025-09-05 Last modified: 2025-10-21
Priority: 2
View other active issues in [flat.map].
View all other issues in [flat.map].
View all issues with LEWG status.
Discussion:
As specified in 23.6.3.4 [queue.mod],
std::queue::push_range is guaranteed to use c.append_range if that exists.
For flat_map and its flat friends, we only ever use c.insert and never try
to use c.insert_range, c.append_range, etc.
LWG thinks the "as if by" wording allows implementations to use insert_range
when the container type being adapted is std::vector but not for user-defined
containers.
Should the flat adaptors follow std::queue and guarantee that they will use
the xxx_range member if they exist?
That would mean that user containers need to ensure that any member functions
with those names must behave as expected, but we already require that for
std::queue.
[2025-10-21; Reflector poll. Status changed: New → LEWG.]
Set priority to 2 after reflector poll.
Use append_range to be consistent with other adapters.
Send to LEWG for confirmation.
Proposed resolution:
Section: 23.6.9.1 [flat.multimap.overview] Status: New Submitter: Jonathan Wakely Opened: 2025-09-05 Last modified: 2025-10-21
Priority: 3
View all issues with New status.
Discussion:
23.6.9.1 [flat.multimap.overview] p4 says:
Except as otherwise noted, operations onflat_multimapare equivalent to those offlat_map, except thatflat_multimapoperations do not remove or replace elements with equal keys.[Example 1:
flat_multimapconstructors and emplace do not erase non-unique elements after sorting them. — end example]
This doesn't really work, because there is no flat_map operation that is
equivalent to the constructors and insert and insert_range functions that
take a sorted_equivalent_t tag argument. The flat_map operations that the
wording intends to refer to take a sorted_unique_t tag argument.
It's a bit of a stretch to say they're equivalent when they don't even have
the same parameters, so we need some weasel words here.
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
Define them in terms of insert_range(std::forward<R>(rg))
with updated Complexity.
Proposed resolution:
MANDATE-NOTHROW in CPOs should not enclose CPO argument sub-expressionsSection: 33 [exec] Status: New Submitter: Lewis Baker Opened: 2025-08-25 Last modified: 2025-10-23
Priority: 3
View all issues with New status.
Discussion:
There are a number of CPOs defined in 33 [exec] which have behaviour specified in terms of being
expression-equivalent to a MANDATE-NOTHROW expression.
noexcept.
However, the way that these CPOs are currently specified in terms of sub-expressions means that we are currently
requiring that all of the expressions passed as arguments to the CPO are also noexcept. Outside of defining
these CPOs as preprocessor macros, this is unimplementable — and also undesirable behaviour.
For example, 33.7.2 [exec.set.value] defines set_value(rcvr, vs...) to be equivalent to
MANDATE-NOTHROW(rcvr.set_value(vs...)) for sub-expressions rcvr and pack of sub-expressions
vs.
In 33.1 [exec.general] p5 we define MANDATE-NOTHROW(expr) as expression-equivalent to
expr but mandate that noexcept(expr) is true.
So in the above definition of set_value(rcvr, vs...) we are actually requiring that the expression
noexcept(rcvr.set_value(vs...)) is true.
This is only true if all of the sub-expressions are noexcept, i.e. all of the following expressions are true.
noexcept(rcvr),
(noexcept(vs) && ...),
the member-function call to rcvr.set_value(vs...) including any implicit conversions of arguments.
This means that if, for example, one of the sub-expressions in the pack vs was a call to some potentially-throwing
function then the overall set_value expression would be violating the mandates requirement.
struct my_receiver
{
void set_value(int x) noexcept;
};
int get_value() noexcept(false);
my_receiver r;
std::execution::set_value(r, get_value()); // fails MANDATE-NOTHROW mandates
Instead, we need to redefine these CPOs as being expression-equivalent to something that does not require that the
argument expressions to the CPO themselves are noexcept — only what will be in the body of the CPO function.
set_value(rcvr, vs...) as expression-equivalent to:
[](auto&& rcvr2, auto&&... vs2) noexcept ->
decltype(auto) requires requires { std::forward<decltype(rcvr2)>(rcvr2).set_value(std::forward<decltype(vs2)>(vs2)...); }
{
return MANDATE-NOTHROW(std::forward<decltype(rcvr2)>(rcvr2).set_value(std::forward<decltype(vs2)>(vs2)...));
}(rcvr, vs...)
The following sections all contain problematic uses of MANDATE-NOTHROW:
33.5.2 [exec.get.allocator]
33.5.3 [exec.get.stop.token]
33.5.4 [exec.get.env]
33.5.5 [exec.get.domain]
33.5.6 [exec.get.scheduler]
33.5.8 [exec.get.fwd.progress]
33.5.9 [exec.get.compl.sched]
33.5.10 [exec.get.await.adapt]
33.7.2 [exec.set.value]
33.7.3 [exec.set.error]
33.7.4 [exec.set.stopped]
33.8.2 [exec.opstate.start]
[2025-10-23; Reflector poll.]
Set priority to 3 after reflector poll.
Proposed resolution:
weakly_parallel as the default forward_progress_guaranteeSection: 33.5.8 [exec.get.fwd.progress] Status: SG1 Submitter: Lewis Baker Opened: 2025-08-25 Last modified: 2025-10-20
Priority: 1
View all issues with SG1 status.
Discussion:
The get_forward_progress_guarantee CPO is intended to allow querying a scheduler as for what sort of
forward-progress guarantee it provides. Algorithms may use this to determine whether it is safe to execute
certain operations on a given scheduler. If a scheduler does not customize this query, the query will
fall back to returning a forward-progress guarantee of weakly_parallel.
parallel by default, as this will be by far the most common kind of scheduler, i.e. a scheduler that
executes on std::thread-like execution agents and that maintains a queue of scheduled tasks.
I expect it to be common that authors of schedulers may forget to customize the
get_forward_progress_guarantee_t query and just leave it at the default. This will likely leave their
scheduler reporting a weaker guarantee than it actually provides and thus not being usable within generic
algorithms that require at least parallel forward progress.
For example, the run_loop execution context defined in 33.12.1 [exec.run.loop] does not define
its scheduler to customize the get_forward_progress_guarantee_t. This means it will report the default
value of weakly_parallel.
However, the scheduled operations will run on the thread that calls run_loop::run() and thus will
inherit its forward-progress guarantees. As this function might block and is therefore unsafe to invoke
it from a thread/agent with weakly_parallel forward progress guarantees (which should probably be
explicitly specified as having undefined-behaviour) we can safely assume that run_loop's scheduler
can provide parallel forward-progress guarantee.
It's not clear whether the current run_loop specification defaulting to its scheduler having
weakly_parallel forward progress guarantee is intentional or unintentional here. However, forgetting
to define the get_forward_progress_guarantee query on a scheduler is something I expect to be fairly common.
Schedulers that provide weakly_parallel (or in future, concurrent) forward progress guarantees require
implementations to be much more aware of the fact that these are the guarantees they are providing and
thus could be more expected to customize the get_forward_progress_guarantee query to return the
respective values.
[2025-10-20; Reflector poll. Status changed: New → SG1]
Set priority to 1 after reflector poll. Send to SG1 and LEWG.
"If there is a default, it should be the weakest possible one. If that is an unfortunate choice I’d rather prefer no default and mandate that the query gets implemented. Providing a default which is stronger than the weakest possible creates logic errors. Accidentally claiming weaker than the actual value is only a performance error."
"This is tension between the default being promising the least and the default being the most likely thing a user wants to do. Assuming the least powerful guarantees unless the user has opted in is safer. Changing this choice requires going back to LEWG or SG1."
"Plenty of reasonable schedulers are weakly parallel at best. It's the right default. If your scheduler offers better than that, you would naturally remember to customize it."
"It seems that the authors of run_loop::scheduler did not naturally remember to customize it.
It's possible the intent was that run_loop::scheduler should not offer better
than weakly_parallel forward progress, but it was not discussed in P2300.
The absence of an explicit implementation of the query could either be
intentional or an accidental omission.
Perhaps this is an indication that there should not be a default forward-progress guarantee for schedulers?"
Proposed resolution:
This wording is relative to N5014.
Modify 33.5.8 [exec.get.fwd.progress] as indicated:
-2- The name
get_forward_progress_guaranteedenotes a query object. For a subexpressionsch, letSchbedecltype((sch)). IfSchdoes not satisfyscheduler,get_forward_progress_guaranteeis ill-formed. Otherwise,get_forward_progress_guarantee(sch)is expression-equivalent to:
(2.1) —
Mandates: The type of the expression above isMANDATE-NOTHROW(AS-CONST(sch).query(get_forward_progress_guarantee))if that expression is well-formed.forward_progress_guarantee.(2.2) — Otherwise,
forward_progress_guarantee::.weakly_parallel
connect-awaitable() should mandate rcvr can receive all completion-signals rather than using constraintsSection: 33.9.10 [exec.connect] Status: New Submitter: Lewis Baker Opened: 2025-08-27 Last modified: 2025-10-23
Priority: 2
View other active issues in [exec.connect].
View all other issues in [exec.connect].
View all issues with New status.
Discussion:
In 33.9.10 [exec.connect] p6, the wording for the connect() customization-point defines a case that is
expression-equivalent to connect-awaitable(new_sndr, rcvr).
connect-awaitable function is specified in p5 as having the signature:
operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs>;
The requires-clause constrains this overload to ensure that the receiver, rcvr, can accept all of the
completion signatures listed in Sigs. This means that connect() will SFINAE out if the receiver cannot
accept all of the completion signatures, rather than this being ill-formed.
new-sndr.connect(rcvr)
which does not constrain on this. It is also redundant with the Mandates element in p6 which mandates
that the following is satisfied:
receiver_of<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>>
The constraint on connect-awaitable should either be removed or replaced with a
Mandates element. As connect-awaitable is only used within the definition of
connect() and as connect() already mandates that receiver_of is satisfied,
it seems sufficient to just remove this constraint from connect-awaitable.
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
"Is this actually redundant? Where is Sigs required to match (or at least be a subset of)
completion_signatures_of<Sndr, env_of_t<Recvr>>?"
Proposed resolution:
This wording is relative to N5014.
Modify 33.9.10 [exec.connect] as indicated:
-5- Let
Vname the typeawait-result-type<DS, connect-awaitable-promise>, letSigsname the typecompletion_signatures< SET-VALUE-SIG(V), // see 33.9.3 [exec.snd.concepts] set_error_t(exception_ptr), set_stopped_t()>and let
connect-awaitablebe an exposition-only coroutine defined as follows:namespace std::execution { […] operation-state-task connect-awaitable(DS sndr, DR rcvr)requires receiver_of<DR, Sigs>{ exception_ptr ep; try { if constexpr (same_as<V, void>) { co_await std::move(sndr); co_await suspend-complete(set_value, std::move(rcvr)); } else { co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr)); } } catch(...) { ep = current_exception(); } co_await suspend-complete(set_error, std::move(rcvr), std::move(ep)); } }
connect() should use get_allocator(get_env(rcvr)) to allocate the coroutine-state for a
connect-awaitable coroutineSection: 33.9.10 [exec.connect] Status: New Submitter: Lewis Baker Opened: 2025-08-27 Last modified: 2025-10-23
Priority: 2
View other active issues in [exec.connect].
View all other issues in [exec.connect].
View all issues with New status.
Discussion:
The wording for connect() (33.9.10 [exec.connect]) handles passing awaitable types as the sender argument
by calling the connect-awaitable() coroutine and having it execute a co_await expression.
connect-awaitable() coroutine will typically need to dynamically allocate storage for
the coroutine state and, as specified, this currently just always uses the global default allocator.
This is because the connect-awaitable-promise type does not define any member operator
new/delete overloads.
It seems desirable for this facility to use the allocator obtained from the receiver, by calling
get_allocator(get_env(rcvr)), in order to allocate storage for the coroutine-state instead of always
using global operator new. This would give the user at least some level of control over how this
allocation is performed.
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
"The get_allocator query doesn't have a default and I think that isn't
covered in the proposed resolution."
Proposed resolution:
This wording is relative to N5014.
Modify 33.9.10 [exec.connect] as indicated:
[Drafting note: This should cover the design intent, although we may want to spell this out more explicitly in terms of the exact semantics in a similar way to 25.8.5 [coro.generator.promise] p17, which lists overloads of operator
new()and describes the rebound allocator type which allocates storage in chunks of size__STDCPP_DEFAULT_NEW_ALIGNMENT__.]
-5- Let
Vname the typeawait-result-type<DS, connect-awaitable-promise>, letSigsname the type[…]and let
connect-awaitablebe an exposition-only coroutine defined as follows:namespace std::execution { […] }Any dynamically allocated storage required for the coroutine state allocated by an invocation of the form
-6- […]connect-awaitable(sndr, rcvr)is allocated using the allocator obtained fromget_allocator(get_env(rcvr)).
connect-awaitable should use is_void_v to check for result-type of
co_await expression instead of same_as<void>Section: 33.9.10 [exec.connect] Status: New Submitter: Lewis Baker Opened: 2025-08-27 Last modified: 2025-10-23
Priority: 4
View other active issues in [exec.connect].
View all other issues in [exec.connect].
View all issues with New status.
Discussion:
The wording in 33.9.10 [exec.connect] p5 defines the connect-awaitable() function as follows:
operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> {
exception_ptr ep;
try {
if constexpr (same_as<V, void>) {
co_await std::move(sndr);
co_await suspend-complete(set_value, std::move(rcvr));
} else {
co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr));
}
} catch(...) {
ep = current_exception();
}
co_await suspend-complete(set_error, std::move(rcvr), std::move(ep));
}
The use of same_as<V, void> in the if-constexpr condition does not cover the case where the
result of the co_await expression has type cv void. It should use is_void_v<V>
instead to allow it to match all void types, not just unqualified void.
[2025-10-23; Reflector poll.]
Set priority to 4 after reflector poll.
"NAD, V is await-result-type<...> which is decltype(...)
and you never get cv void from decltype (7.2.2 [expr.type] p2)."
"But using is_void_v more clearly expresses the intent."
Proposed resolution:
This wording is relative to N5014.
Modify 33.9.10 [exec.connect] as indicated:
-5- Let
Vname the typeawait-result-type<DS, connect-awaitable-promise>, letSigsname the typecompletion_signatures< SET-VALUE-SIG(V), // see 33.9.3 [exec.snd.concepts] set_error_t(exception_ptr), set_stopped_t()>and let
connect-awaitablebe an exposition-only coroutine defined as follows:namespace std::execution { […] operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> { exception_ptr ep; try { if constexpr (same_as<V, void>is_void_v<V>) { co_await std::move(sndr); co_await suspend-complete(set_value, std::move(rcvr)); } else { co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr)); } } catch(...) { ep = current_exception(); } co_await suspend-complete(set_error, std::move(rcvr), std::move(ep)); } }
Section: 33.13.1 [exec.as.awaitable] Status: New Submitter: Lewis Baker Opened: 2025-08-27 Last modified: 2025-10-23
Priority: 2
View other active issues in [exec.as.awaitable].
View all other issues in [exec.as.awaitable].
View all issues with New status.
Discussion:
In 33.13.1 [exec.as.awaitable] bullet 7.2 it states:
(7.2) — Otherwise,
Preconditions:(void(p), expr)ifis-awaitable<Expr, U>istrue, whereUis an unspecified class type that is notPromiseand that lacks a member namedawait_transform.is-awaitable<Expr, Promise>istrueand the expressionco_await exprin a coroutine with promise typeUis expression-equivalent to the same expression in a coroutine with promise typePromise.
The "Preconditions:" sentence there refers to static properties of the program and so seems like a better fit for a Mandates: element or for folding into the constraint.
Also, in the part of the precondition above which says "… and the expressionco_await expr in a
coroutine with promise type U is expression-equivalent to the same expression in a coroutine with promise
type Promise" it is unclear how this can be satisfied, as the types involved are different and therefore
the expression cannot be expression-equivalent.
I think perhaps what is intended here is something along the lines of the first expression having
"effects equivalent to" the second expression, instead of "expression-equivalent to"?
However, I think there is a more direct way to express the intent here, by instead just requiring that
decltype(GET-AWAITER(expr)) satisfies is-awaiter<Promise>.
This checks whether expr would be a valid type to return from a Promise::await_transform() function.
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
"Intent of the original wording seems to be that GET-AWAITER(expr) should be the same as GET-AWAITER(expr, p) and this rewording loses that. Don't understand the rationale for the new wording either." (More details in the reflector thread in Sept. 2025)
Proposed resolution:
This wording is relative to N5014.
Modify 33.13.1 [exec.as.awaitable] as indicated:
-7-
as_awaitableis a customization point object. For subexpressionsexprandpwherepis an lvalue,Exprnames the typedecltype((expr))andPromisenames the typedecay_t<decltype((p))>,as_awaitable(expr, p)is expression-equivalent to, except that the evaluations ofexprandpare indeterminately sequenced:
(7.1) —
Mandates:expr.as_awaitable(p)if that expression is well-formed.is-awaitable<A, Promise>istrue, whereAis the type of the expression above.(7.2) — Otherwise,
(void(p), expr)ifdecltype(GET-AWAITER(expr))satisfiesis-awaiter<Promise>.is-awaitable<Expr, U>istrue, whereUis an unspecified class type that is notPromiseand that lacks a member namedawait_transform.Preconditions:is-awaitable<Expr, Promise>istrueand the expressionco_await exprin a coroutine with promise typeUis expression-equivalent to the same expression in a coroutine with promise typePromise.(7.3) — […]
(7.4) — […]
(7.5) — […]
as_awaitable(expr, p) does not define semantics of call if p is not an lvalueSection: 33.13.1 [exec.as.awaitable] Status: New Submitter: Lewis Baker Opened: 2025-08-27 Last modified: 2025-09-17
Priority: Not Prioritized
View other active issues in [exec.as.awaitable].
View all other issues in [exec.as.awaitable].
View all issues with New status.
Discussion:
The wording in 33.13.1 [exec.as.awaitable] p7 defines the semantics of a call to as_awaitable(expr, p)
where p is an lvalue. However, it does not specify what the behaviour is if p is not an lvalue.
as_awaitable(expr, p) is ill-formed if p is not an lvalue.
Proposed resolution:
This wording is relative to N5014.
Modify 33.13.1 [exec.as.awaitable] as indicated:
-7-
as_awaitableis a customization point object. For subexpressionsexprandpwherepis an lvalue,Exprnames the typedecltype((expr))andPromisenames the typedecay_t<decltype((p))>, ifpis not an lvalue,as_awaitable(expr, p)is ill-formed, otherwiseas_awaitable(expr, p)is expression-equivalent to, except that the evaluations ofexprandpare indeterminately sequenced:
awaitable-sender concept should qualify use of awaitable-receiver typeSection: 33.13.1 [exec.as.awaitable] Status: New Submitter: Lewis Baker Opened: 2025-08-27 Last modified: 2025-10-23
Priority: 2
View other active issues in [exec.as.awaitable].
View all other issues in [exec.as.awaitable].
View all issues with New status.
Discussion:
In 33.13.1 [exec.as.awaitable] p1 there is an exposition-only helper concept
awaitable-sender defined as follows:
namespace std::execution {
template<class Sndr, class Promise>
concept awaitable-sender =
single-sender<Sndr, env_of_t<Promise>> &&
sender_to<Sndr, awaitable-receiver> && // see below
requires (Promise& p) {
{ p.unhandled_stopped() } -> convertible_to<coroutine_handle<>>;
};
}
The mention of the type awaitable-receiver here does not refer to any exposition-only type
defined at namespace-scope. It seems to, instead, be referring to the nested member-type
sender-awaitable<Sndr, Promise>::awaitable-receiver and so should be
qualified as such.
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
"We should move the declaration of sender-awaitable before the concept."
Proposed resolution:
This wording is relative to N5014.
Modify 33.13.1 [exec.as.awaitable] as indicated:
-1-
as_awaitabletransforms an object into one that is awaitable within a particular coroutine. Subclause 33.13 [exec.coro.util] makes use of the following exposition-only entities:namespace std::execution { template<class Sndr, class Promise> concept awaitable-sender = single-sender<Sndr, env_of_t<Promise>> && sender_to<Sndr, typename sender-awaitable<Sndr, Promise>::awaitable-receiver> && // see below requires (Promise& p) { { p.unhandled_stopped() } -> convertible_to<coroutine_handle<>>; }; […] }
awaitable-receiver::set_value should use Mandates instead of constraintsSection: 33.13.1 [exec.as.awaitable] Status: LEWG Submitter: Lewis Baker Opened: 2025-08-28 Last modified: 2025-10-23
Priority: 1
View other active issues in [exec.as.awaitable].
View all other issues in [exec.as.awaitable].
View all issues with LEWG status.
Discussion:
In 33.13.1 [exec.as.awaitable] bullet 4.1 the awaitable-receiver::set_value member function
is defined as having a constraint that the result-type is constructible from the values.
If
constructible_from<result-type, decltype((vs))...>is satisfied, the expressionset_value(rcvr, vs...)is equivalent to:try { rcvr.result-ptr->template emplace<1>(vs...); } catch(...) { rcvr.result-ptr->template emplace<2>(current_exception()); } rcvr.continuation.resume();Otherwise,
set_value(rcvr, vs...)is ill-formed.
Should we be using mandates here instead of constraints (or alternatively just drop the constraint altogether)? There shouldn't be any need to change behaviour based on whether or not the receiver's completion methods are well-formed or not.
It is worth noting that there is inconsistent use of constraints onset_value methods in other receiver
implementations throughout 33 [exec].
For example: The following set_value member function applies constraints:
In 33.9.2 [exec.snd.expos] basic-receiver::set_value constrains that check that it can accept those specific value arguments
While the following set_value member functions do not apply constraints:
In 33.9.12.10 [exec.let] receiver2::set_value
In 33.9.12.18 [exec.spawn.future] spawn-future-receiver::set_value
in 33.9.13.1 [exec.sync.wait] sync-wait-receiver::set_value
We should probably try to be consistent on whether or not set_value implementations
should use constraints or mandates. Given that it is not allowed to form calls to the
receiver unless that overload is present in the completion_signatures, it may be worth
just making them all mandates. This would tend to make uses of the receiver_of concept
less useful as satisfying receiver_of<R, Sig> would not necessarily
guarantee that actually trying to call each of R's corresponding completion functions
will result in a well-formed program. It is arguable that this is already the status-quo, however.
[2025-10-23; Reflector poll. Status changed: New → LEWG]
Set priority to 1 after reflector poll.
"Send to LEWG if we want to go this way, which makes receiver_of mostly meaningless
and calls into question why we even have the concept."
Proposed resolution:
This wording is relative to N5014.
Modify 33.13.1 [exec.as.awaitable] as indicated:
-4- Let
rcvrbe an rvalue expression of typeawaitable-receiver, letcrcvrbe a const lvalue that refers torcvr, letvsbe a pack of subexpressions, and leterrbe an expression of typeErr. Then:
(4.1) —
IfThe expressionconstructible_from<result-type, decltype((vs))...>is satisfied, tset_value(rcvr, vs...)is equivalent to:try { rcvr.result-ptr->template emplace<1>(vs...); } catch(...) { rcvr.result-ptr->template emplace<2>(current_exception()); } rcvr.continuation.resume();
Otherwise,Mandates:set_value(rcvr, vs...)is ill-formedconstructible_from<result-type, decltype((vs))...>is satisfied.(4.2) — […]
(4.3) — […]
(4.4) — […]
Modify 33.9.2 [exec.snd.expos] after p25 as indicated:
[…]
template<class Sndr, class Rcvr, class Index>
requires valid-specialization<env-type, Index, Sndr, Rcvr>
struct basic-receiver { // exposition only
using receiver_concept = receiver_t;
using tag-t = tag_of_t<Sndr>; // exposition only
using state-t = state-type<Sndr, Rcvr>; // exposition only
static constexpr const auto& complete = impls-for<tag-t>::complete; // exposition only
template<class... Args>
requires callable<decltype(complete), Index, state-t&, Rcvr&, set_value_t, Args...>
void set_value(Args&&... args) && noexcept {
complete(Index(), op->state, op->rcvr, set_value_t(), std::forward<Args>(args)...);
}
template<class Error>
requires callable<decltype(complete), Index, state-t&, Rcvr&, set_error_t, Error>
void set_error(Error&& err) && noexcept {
complete(Index(), op->state, op->rcvr, set_error_t(), std::forward<Error>(err));
}
void set_stopped() && noexcept
requires callable<decltype(complete), Index, state-t&, Rcvr&, set_stopped_t> {
complete(Index(), op->state, op->rcvr, set_stopped_t());
}
auto get_env() const noexcept -> env-type<Index, Sndr, Rcvr> {
return impls-for<tag-t>::get-env(Index(), op->state, op->rcvr);
}
basic-state<Sndr, Rcvr>* op; // exposition only
};
[…]
operator<< and operator>> for unspecified I/O manipulatorsSection: 31.7.7 [std.manip], 31.7.8 [ext.manip], 31.7.9 [quoted.manip] Status: New Submitter: Jiang An Opened: 2025-09-05 Last modified: 2025-10-21
Priority: 4
View other active issues in [std.manip].
View all other issues in [std.manip].
View all issues with New status.
Discussion:
Currently, it is unspecified whether any of operator<< or operator>>
for the "unspecified" return type of an I/O manipulating function is SFINAE-friendly.
#include <iostream>
#include <iomanip>
int main()
{
std::cout << std::setfill(L'*');
}
It seems better to eliminate such implementation divergence. Perhaps it's also better to require them to be SFINAE-friendly to reduce potential conflicts with user-provided operators.
If it's intended to allow implementation divergence, perhaps we should clarify the intent.[2025-10-21; Reflector poll.]
Set priority to 4 after reflector poll.
"Does it matter in practice? If the type is unspecified, it's fine for misuse to be non-portable."
"Would like a diagnostic, don't care about SFINAE. Not clear to me that the current
setfill wording disallows the mixed-character-type case at all ..."
"NAD, not been a problem for a quarter of a century."
Proposed resolution:
boyer_moore_searcher and boyer_moore_horspool_searcher should be constexpr-friendlySection: 22.10.18.3 [func.search.bm], 22.10.18.4 [func.search.bmh] Status: LEWG Submitter: Hewill Kang Opened: 2025-09-03 Last modified: 2025-10-21
Priority: 4
View other active issues in [func.search.bm].
View all other issues in [func.search.bm].
View all issues with LEWG status.
Discussion:
Currently, boyer_moore_searcher and boyer_moore_horspool_searcher are not
constexpr-friendly because their underlying implementation needs to precompute
the shift table, which usually requires a vector or unordered_map to store.
constexpr-friendly.
Although std::hash still lacks constexpr support, users can provide their own
hash functions to use unordered containers at compile time.
Given that both boyer_moore_searcher and boyer_moore_horspool_searcher can
take a custom hash, it makes perfect sense that they could be constexpr-friendly.
Not to mention that library implementations usually simply use arrays instead of
hash tables for the common string case because characters only have 256 values,
so unordered_map is not actually used.
[2025-10-21 Reflector poll; Status changed: New → LEWG and priority set to P4.]
Proposed resolution:
This wording is relative to N5014.
Modify 22.10.18.3 [func.search.bm] as indicated:
namespace std { template<class RandomAccessIterator1, class Hash = hash<typename iterator_traits<RandomAccessIterator1>::value_type>, class BinaryPredicate = equal_to<>> class boyer_moore_searcher { public: constexpr boyer_moore_searcher(RandomAccessIterator1 pat_first, RandomAccessIterator1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate()); template<class RandomAccessIterator2> constexpr pair<RandomAccessIterator2, RandomAccessIterator2> operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const; private: RandomAccessIterator1 pat_first_; // exposition only RandomAccessIterator1 pat_last_; // exposition only Hash hash_; // exposition only BinaryPredicate pred_; // exposition only }; }constexpr boyer_moore_searcher(RandomAccessIterator1 pat_first, RandomAccessIterator1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());-1- Preconditions: The value type of
RandomAccessIterator1meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.[…]template<class RandomAccessIterator2> constexpr pair<RandomAccessIterator2, RandomAccessIterator2> operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;-5- Mandates:
RandomAccessIterator1andRandomAccessIterator2have the same value type.
Modify 22.10.18.4 [func.search.bmh] as indicated:
namespace std { template<class RandomAccessIterator1, class Hash = hash<typename iterator_traits<RandomAccessIterator1>::value_type>, class BinaryPredicate = equal_to<>> class boyer_moore_horspool_searcher { public: constexpr boyer_moore_horspool_searcher(RandomAccessIterator1 pat_first, RandomAccessIterator1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate()); template<class RandomAccessIterator2> constexpr pair<RandomAccessIterator2, RandomAccessIterator2> operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const; private: RandomAccessIterator1 pat_first_; // exposition only RandomAccessIterator1 pat_last_; // exposition only Hash hash_; // exposition only BinaryPredicate pred_; // exposition only }; }constexpr boyer_moore_horspool_searcher(RandomAccessIterator1 pat_first, RandomAccessIterator1 pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());-1- Preconditions: The value type of
RandomAccessIterator1meets the Cpp17DefaultConstructible, Cpp17CopyConstructible, and Cpp17CopyAssignable requirements.[…]template<class RandomAccessIterator2> constexpr pair<RandomAccessIterator2, RandomAccessIterator2> operator()(RandomAccessIterator2 first, RandomAccessIterator2 last) const;-5- Mandates:
RandomAccessIterator1andRandomAccessIterator2have the same value type.
optional<T&>::or_elseSection: 22.5.4.7 [optional.ref.monadic] Status: New Submitter: Hewill Kang Opened: 2025-09-07 Last modified: 2025-10-16
Priority: 4
View other active issues in [optional.ref.monadic].
View all other issues in [optional.ref.monadic].
View all issues with New status.
Discussion:
optional<T&>::or_else currently returns *val when it has a value,
which calls the optional(U&&) constructor which in turn calls
convert-ref-init-val which in turn calls addressof.
optional<T&>'s
default copy constructor to just copy a pointer.
[2025-10-16; Reflector poll]
Set priority to 4 after reflector poll.
"NAD, no observable difference." "Still simpler to not have to reason about whether it matters, let's do it."
Proposed resolution:
This wording is relative to N5014.
Modify 22.5.4.7 [optional.ref.monadic] as indicated:
template<class F> constexpr optional or_else(F&& f) const;-7- Constraints:
-8- Mandates:Fmodelsinvocable.is_same_v<remove_cvref_t<invoke_result_t<F>>, optional>istrue. -9- Effects: Equivalent to:if (has_value()) { return *thisval; } else { return std::forward<F>(f)(); }
transform_senderSection: 33.9.5 [exec.domain.default] Status: New Submitter: Eric Niebler Opened: 2025-08-31 Last modified: 2025-10-23
Priority: 1
View all other issues in [exec.domain.default].
View all issues with New status.
Discussion:
The following has been reported by Trevor Gray to me:
There is a potential stack-use-after-scope in
I'll give an example of the problem usingexecution::transform_senderwithexecution::default_domain::transform_sender.starts_onwith thedefault_domain.starts_ondefines atransform_sendersoexecution::transform_senderwill expand to:return transform_sender( dom, dom.transform_sender(std::forward<Sndr>(sndr), env...), env...);where
Execution flow:domis thedefault_domainandsndrisstarts_on.
dom.transform_sender(std::forward<Sndr>(sndr), env...)usesdefault_domainto invokestart_on'stransform_sender. The return type isT(whereTis alet_valuesender)
transform_sender(dom, declval<T>(), env...)is then run which usesdefault_domainto just returnstd::forward<T>(t).This means the value returned from the entire expression is
T&&which a reference to a temporary variable in the frame oftransform_senderwhich is no longer valid after the return.
In the reference implementation, this scenario does not create a dangling reference because
its implementation of default_domain::transform_sender does not conform to the spec. By default,
it returns an rvalue sender as a prvalue instead of an xvalue as the spec requires.
[2025-10-23; Reflector poll.]
Set priority to 1 after reflector poll.
"There's an NB comment to remove transform_sender entirely."
"Nothing seems to prevent Sndr from being a reference type, so I would
expect decay_t or remove_cvref_t (or decayed-typeof) instead
of the static_cast."
"I think that's intentional. The dangling happens if transform_sender is
invoked with an xvalue (where Sndr is deduced as a type). When invoked with
and lvalue (where Sndr is deduced as an lvalue reference) we don't want to
make a copy. So the static_cast makes a copy of an xvalue and not of an lvalue."
Proposed resolution:
This wording is relative to N5014.
Modify 33.9.5 [exec.domain.default] as indicated:
template<sender Sndr, queryable... Env> requires (sizeof...(Env) <= 1) constexpr sender decltype(auto) transform_sender(Sndr&& sndr, const Env&... env) noexcept(see below);-2- Let
ebe the expressiontag_of_t<Sndr>().transform_sender(std::forward<Sndr>(sndr), env...)if that expression is well-formed; otherwise,
-3- Returns:static_cast<Sndr>(std::forward<Sndr>(sndr)).e. -4- Remarks: The exception specification is equivalent tonoexcept(e).
check-types function for upon_error and upon_stopped is wrongSection: 33.9.12.9 [exec.then] Status: New Submitter: Eric Niebler Opened: 2025-08-31 Last modified: 2025-10-23
Priority: 2
View all issues with New status.
Discussion:
The following has been reported by Trevor Gray:
In 33.9.12.9 [exec.then] p5, the impls-for<decayed-typeof<then-cpo>>::check-types
unction is specified as follows:
template<class Sndr, class... Env> static consteval void check-types();Effects: Equivalent to:
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>(); auto fn = []<class... Ts>(set_value_t(*)(Ts...)) { if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts...>) throw unspecified-exception(); }; cs.for-each(overload-set{fn, [](auto){}});where
unspecified-exceptionis a type derived fromexception.
The line auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
is correct when then-cpo is then but not when it is upon_error or upon_stopped.
upon_error it should be:
auto fn = []<class... Ts>(set_error_t(*)(Ts...)) {
and for upon_stopped it should be:
auto fn = []<class... Ts>(set_stopped_t(*)(Ts...)) {
We can achieve that by replacing set_value_t in the problematic line with decayed-typeof<set-cpo>.
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 33.9.12.9 [exec.then] as indicated:
template<class Sndr, class... Env> static consteval void check-types();-5- Effects: Equivalent to:
auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>(); auto fn = []<class... Ts>(set_value_tdecayed-typeof<set-cpo>(*)(Ts...)) { if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts...>) throw unspecified-exception(); }; cs.for-each(overload-set{fn, [](auto){}});where
unspecified-exceptionis a type derived fromexception.
function_ref should provide result_typeSection: 22.10.17.6.2 [func.wrap.ref.class] Status: New Submitter: Hewill Kang Opened: 2025-09-12 Last modified: 2025-10-22
Priority: 3
View all issues with New status.
Discussion:
Currently, function, move_only_function, and copyable_function all have a
type member result_type, but function_ref does not:
static_assert(is_same_v< function <int(int)>::result_type, int>);
static_assert(is_same_v<move_only_function <int(int)>::result_type, int>);
static_assert(is_same_v< copyable_function <int(int)>::result_type, int>);
static_assert(is_same_v< function_ref<int(int)>::result_type, int>); // error
It seems worthwhile to also provide it for the latter, as it is consistent with the other wrappers and allows the user to easily extract the return type.
[2025-10-03; Hewill comments]
Additionally, given that some third-party implementations also provide a mechanism for
extracting the return type, for example, type_safe::function_ref provides a public member
type alias
return_type, and llvm::function_ref can work with
llvm::function_traits to obtain the return type, providing such a member
type alias for std::function_ref meets user potential demands.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
Alternative (with incoming paper targeting LEWG) of deprecating
result_type in existing wrappers was suggested.
Proposed resolution:
This wording is relative to N5014.
Modify 22.10.17.6.2 [func.wrap.ref.class] as indicated:
namespace std {
template<class R, class... ArgTypes>
class function_ref<R(ArgTypes...) cv noexcept(noex)> {
public:
using result_type = R;
// 22.10.17.6.3 [func.wrap.ref.ctor], constructors and assignment operators
template<class F> function_ref(F*) noexcept;
[…]
};
}
flat_meow range insertion behavior is unclear if in-place merge cannot allocate additional memorySection: 23.6.8.7 [flat.map.modifiers], 23.6.11.5 [flat.set.modifiers], 23.6.12.5 [flat.multiset.modifiers] Status: New Submitter: Tim Song Opened: 2025-09-09 Last modified: 2025-10-21
Priority: 3
View other active issues in [flat.map.modifiers].
View all other issues in [flat.map.modifiers].
View all issues with New status.
Discussion:
The range insertion operations of flat_map/multimap/set/multiset have this Remarks: element:
Since this operation performs an in-place merge, it may allocate memory.
It is not clear what happens if that allocation fails. If the inplace_merge algorithm is used,
it will fall back to a less efficient 𝒪(N log N) algorithm, but this is not reflected
in the Complexity: element. Alternatively, if the allocation failure is reported by an exception,
that should be called out in the spec.
[2025-10-21; Reflector poll.]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 29.10.9.4 [simd.mask.unary] Status: New Submitter: Matthias Kretz Opened: 2025-09-15 Last modified: 2025-10-17
Priority: 1
View other active issues in [simd.mask.unary].
View all other issues in [simd.mask.unary].
View all issues with New status.
Discussion:
29.10.9.4 [simd.mask.unary] spells out the return type with the ABI tag of
the basic_mask specialization. That's problematic / overconstrained.
Consider Intel SandyBridge/IvyBridge-like targets:
vec<float>::size() -> 8 vec<int>::size() -> 4 mask<float>::size() -> 8
The ABI tag in this case encodes for vec<float> that one object holds 8
elements and is passed via one register. vec<int> uses a
different ABI tag that says 4 elements passed via one register.
vec<int, 8>'s ABI tag says 8 elements passed via two registers.
+mask<float>() return? The working draft says it must
return a basic_vec<int, mask<float>::abi_type>. And
mask<float>::abi_type is constrained to be the same as
vec<float>::abi_type. The working draft thus makes it
impossible to implement ABI tags that encode number of elements + number of
registers (+ bit-masks vs. vector-masks, but that's irrelevant for this
issue). Instead, an ABI tag would have to encode the native SIMD width of all
vectorizable types. And that's unnecessarily making compatible types
incompatible. Also we make it harder to add to the set of vectorizable types
in the future.The issue is even worse for an implementation that implements
vec<complex<T>> using different ABI tags. Encoding
whether the value-type is complex into the ABI is useful because it impacts
how the mask is stored (mask<complex<float>, 8> is
internally stored as a 16-element bit-mask (for interleaved complex), while
mask<double, 8> is stored as an 8-element bit-mask). The ABI
tag can also be used to implement interleaved vs. contiguous storage, which
is useful for different architectures. If we require
+mask<complex<float>>() to be of a different type than
any vec<long long> would ever be, that's just brittle and
unnecessary template bloat.
[2025-10-17; Reflector poll.]
Set priority to 1 after reflector poll.
"Should be addressed together with 4238(i)."
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.2 [simd.expos] as indicated:
using simd-size-type = see below; // exposition only template<size_t Bytes> using integer-from = see below; // exposition only template<class T, class Abi> constexpr simd-size-type simd-size-v = see below; // exposition only template<class T> constexpr size_t mask-element-size = see below; // exposition only template <size_t Bytes, class Abi> using simd-vec-from-mask-t = see below; // exposition only […]
Modify 29.10.2.1 [simd.expos.defn] as indicated:
template<class T> constexpr size_t mask-element-size = see below; // exposition only-4-
mask-element-size<basic_mask<Bytes, Abi>>has the valueBytes.template <size_t Bytes, class Abi> using simd-vec-from-mask-t = see below;-?-
-?-simd-vec-from-mask-t<Bytes, Abi>is an alias for an enabled specialization ofbasic_vecif and only ifbasic_mask<Bytes, Abi>is a data-parallel type andinteger-from<Bytes>is valid and a vectorizable type.simd-vec-from-mask-t<Bytes, Abi>::size() == basic_mask<Bytes, Abi>::size()istrue. -?-typename simd-vec-from-mask-t<Bytes, Abi>::value_typeisinteger-from<Bytes>
Modify 29.10.9.1 [simd.mask.overview], class template basic_mask overview synopsis, as indicated:
namespace std::simd {
template<size_t Bytes, class Abi> class basic_mask {
public:
[…]
// 29.10.9.4 [simd.mask.unary], basic_mask unary operators
constexpr basic_mask operator!() const noexcept;
constexpr basic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator+() const noexcept;
constexpr basic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator-() const noexcept;
constexpr basic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator~() const noexcept;
[…]
}
Modify 29.10.9.4 [simd.mask.unary] as indicated:
constexpr basic_mask operator!() const noexcept; constexprbasic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator+() const noexcept; constexprbasic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator-() const noexcept; constexprbasic_vec<integer-from<Bytes>, Abi>simd-vec-from-mask-t<Bytes, Abi> operator~() const noexcept;-1- Let
-2- Returns: […]opbe the operator.
std::basic_string's data() and operator[] specificationSection: 27.4.3.6 [string.access] Status: New Submitter: Peter Bindels Opened: 2025-09-16 Last modified: 2025-10-21
Priority: 4
View all other issues in [string.access].
View all issues with New status.
Discussion:
From the working draft N5014, the specification for operator[] in 27.4.3.6 [string.access]
p2 says:
Returns:
*(begin() + pos)ifpos < size(). Otherwise, returns a reference to an object of typecharTwith valuecharT(), where modifying the object to any value other thancharT()leads to undefined behavior.
The specification for data() in 27.4.3.8.1 [string.accessors] p1 (and p4) says, however:
Returns: A pointer
psuch thatp + i == addressof(operator[](i))for eachiin[0, size()].
The former implies that str[str.size()] is allowed to be the address of any null terminator,
while the latter restricts it to only being the null terminator belonging to the string.
operator[] to
Returns:
*(begin() + pos)ifpos <= size(). The program shall not modify the value stored atsize()to any value other thancharT(); otherwise, the behavior is undefined.
This moves it inline with the data() specification. Given the hardened precondition that
pos <= size() this does not change behavior for any in-contract access, and we do
not define what the feature does when called with broken preconditions. I have been looking at
the latter but that will be an EWG paper instead.
[2025-10-21; Reflector poll.]
Set priority to 4 after reflector poll.
"NAD. begin() + size() is not dereferenceable and should remain that way."
"Saying "if pos <= size() is redundant given the precondition above."
"The resolution removes any guarantee that the value at str[str.size()]
is charT(). Furthermore, the premise of the issue is incorrect,
returning the address of a different null terminator not belonging to the
string would make traversing it with other string operations UB, so it
has to return a reference to a terminator that's within the same array."
"*(begin() = size()) is UB, but could use *(data() + size()) instead.
Personally I'd like *end() to be valid, but that's certainly LEWG business
requiring a paper."
Proposed resolution:
This wording is relative to N5014.
Modify 27.4.3.6 [string.access] as indicated:
constexpr const_reference operator[](size_type pos) const; constexpr reference operator[](size_type pos);-1- Hardened preconditions:
-2- Returns:pos <= size()istrue.*(begin() + pos)ifpos <= size().Otherwise, returns a reference to an object of type-3- Throws: Nothing. -4- Complexity: Constant time. -?- Remarks The program shall not modify the value stored atcharTwith valuecharT(), where modifying the object to any value other thancharT()leads to undefined behavior.size()to any value other thancharT(); otherwise, the behavior is undefined
hive::reserve() needs Throws: element adjusted to match block min/max considerationsSection: 23.3.9.3 [hive.capacity] Status: New Submitter: Matt Bentley Opened: 2025-09-17 Last modified: 2025-10-22
Priority: 3
View other active issues in [hive.capacity].
View all other issues in [hive.capacity].
View all issues with New status.
Discussion:
This issue comes from Bloomberg as part of their C++26 comments via Incits. To summarize their case, in a
call to reserve(n),
if (n > capacity() && capacity() + current-limits.min > max_size()),
reserve should throw, e.g when max_size=100, capacity=80, current-limits.min and
current-limits.max are 40 and n=90.
max_size() to 140 and n to 130,
we can see that although we could add one block with a capacity of current-limits.min,
adding another would be impossible; we still cannot make capacity >= n without also
being > max_size.
This is currently not stated in the Throws: element. I've implemented the requested additional
throws and they are easily achievable.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
There was discussion if reserve() is allowed to deallocate unused blocks,
that materialized into LWG 4380(i).
Proposed resolution:
This wording is relative to N5014.
Modify 23.3.9.3 [hive.capacity] as indicated:
void reserve(size_type n);-3- Effects: If
-4- Postconditions:n <= capacity()istrue, there are no effects. Otherwise increasescapacity()by allocating reserved blocks.capacity() >= nistrue. -5- Throws:length_errorifncapacity()cannot be made>= nwithout being> max_size(), as well as any exceptions thrown by the allocator. […]
hive::reserve() complexity does not reflect potential deallocation of blocks
Section: 23.3.9.3 [hive.capacity] Status: New Submitter: Matt Bentley Opened: 2025-09-23 Last modified: 2025-10-22
Priority: 3
View other active issues in [hive.capacity].
View all other issues in [hive.capacity].
View all issues with New status.
Discussion:
As noted by Arthur in issue 4379(i), reserve(n) could choose to deallocate some
reserved (empty) blocks if in doing so it could then allocate blocks which brought capacity()
closer to n.
capacity() is 460, n is 480, the current-limits.min is
40 and there is a reserved block of capacity 70, the latter could be deallocated and a
new reserved block allocated of capacity 90, bringing total capacity to 480. As opposed
to allocating a new reserved block at the min capacity of 40, bringing the total capacity
to 500.
The implicit assumption in this issue is that we want reserve() to be able to deallocate.
No-one has objected to this before, but if you want to, please speak up.
Anyway, the Complexity of reserve() does not presently reflect this ability.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
An explicit objection was raised that the Effects: should state
whether reserve() is allowed, or even required, to deallocate blocks.
Proposed resolution:
This wording is relative to N5014.
Modify 23.3.9.3 [hive.capacity] as indicated:
void reserve(size_type n);-3- Effects: If
-4- Postconditions:n <= capacity()istrue, there are no effects. Otherwise increasescapacity()by allocating reserved blocks.capacity() >= nistrue. -5- Throws:length_errorifn> max_size(), as well as any exceptions thrown by the allocator. -6- Complexity: It does not change the size of the sequence and takes at most linear time in the number of reserved blocks allocated and deallocated. -7- Remarks: All references, pointers, and iterators referring to elements in*this, as well as the past-the-end iterator, remain valid.
std::ranges::to specification using CTAD not supported by core language
Section: 25.5.7.2 [range.utility.conv.to] Status: New Submitter: Jens Maurer Opened: 2025-09-23 Last modified: 2025-10-20
Priority: 2
View other active issues in [range.utility.conv.to].
View all other issues in [range.utility.conv.to].
View all issues with New status.
Discussion:
25.5.7.2 [range.utility.conv.to] p4 defines a DEDUCE_EXPR
that attempts CTAD (class template argument deduction) on a template template
parameter C.
[2025-10-20; Reflector poll.]
Set priority to 2 after reflector poll.
The core language needs to be fixed so this works.
Proposed resolution:
This wording is relative to N5014.
Modify 25.2 [ranges.syn], header <ranges> synopsis, as indicated:
[…]
namespace std::ranges {
[…]
// 25.5.7 [range.utility.conv], range conversions
template<class C, input_range R, class... Args> requires (!view<C>)
constexpr C to(R&& r, Args&&... args);
template<template<class...> class C, input_range R, class... Args>
constexpr auto to(R&& r, Args&&... args);
template<class C, class... Args> requires (!view<C>)
constexpr auto to(Args&&... args);
template<template<class...> class C, class... Args>
constexpr auto to(Args&&... args);
[…]
}
Modify 25.5.7.2 [range.utility.conv.to] as indicated:
template<template<class...> class C, input_range R, class... Args> constexpr auto to(R&& r, Args&&... args);
-3- Letinput-iteratorbe an exposition-only type:struct input-iterator { // exposition only using iterator_category = input_iterator_tag; using value_type = range_value_t<R>; using difference_type = ptrdiff_t; using pointer = add_pointer_t<range_reference_t<R>>; using reference = range_reference_t<R>; reference operator*() const; pointer operator->() const; input-iterator& operator++(); input-iterator operator++(int); bool operator==(const input-iterator&) const; };
[Note 1:input-iteratormeets the syntactic requirements of Cpp17InputIterator. — end note]-4- LetDEDUCE_EXPRbe defined as follows:
(4.1) —C(declval<R>(), declval<Args>()...)if that is a valid expression,
(4.2) — otherwise,C(from_range, declval<R>(), declval<Args>()...)if that is a valid expression,
(4.3) — otherwise,C(declval<input-iterator>(), declval<input-iterator>(), declval<Args>()...)
if that is a valid expression,
(4.4) — otherwise, the program is ill-formed.
-5- Returns:to<decltype(DEDUCE_EXPR)>(std::forward<R>(r), std::forward<Args>(args)...).
simd::basic_mask(bool) overload needs to be more constrained
Section: 29.10.9.2 [simd.mask.ctor] Status: New Submitter: Matthias Kretz Opened: 2025-09-24 Last modified: 2025-10-10
Priority: Not Prioritized
View all issues with New status.
Discussion:
29.10.9.2 [simd.mask.ctor] defines the overloads basic_mask(bool) and
basic_mask(unsigned_integral auto). This leads to the following pitfall:
auto g0() {
unsigned short k = 0xf;
return simd::mask<float, 8>(k); // mov eax, 15
}
auto g1() {
unsigned short k = 0xf;
return simd::mask<float, 8>(k >> 1); // mov eax, -1 ⚠️
}
auto g2() {
unsigned int k = 0xf;
return simd::mask<float, 8>(k >> 1); // mov eax, 7
}
In g1, k is promoted to int, shifted and then passed to
the mask constructor. Instead of failing, int(0x7) is
converted to bool and the mask thus initialized to all true.
simd::mask<float>(true_type());
unsigned_integral<bool> is true =>
same_as<bool> auto instead of 'bool' makes
the overload set ambiguous
float is convertible to bool, thus
simd::mask<float>(1.f) continues to compile
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
Modify 29.10.9.1 [simd.mask.overview],
class template basic_masksynopsis, as indicated:namespace std::simd { template<size_t Bytes, class Abi> class basic_mask { public: […] constexpr basic_mask() noexcept = default; // 29.10.9.2 [simd.mask.ctor], basic_mask constructors constexpr explicit basic_mask(value_type) noexcept; template<size_t UBytes, class UAbi> constexpr explicit basic_mask(const basic_mask<UBytes, UAbi>&) noexcept; template<class G> constexpr explicit basic_mask(G&& gen) noexcept; constexpr basic_mask(const bitset<size()>& b) noexcept; constexpr explicit basic_mask(unsigned_integral auto val) noexcept; basic_mask(signed_integral auto) = delete; […] }; }
[2025-10-06; Matthias Kretz improves wording after reflector discussion]
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.9.1 [simd.mask.overview], class template basic_mask synopsis, as indicated:
namespace std::simd {
template<size_t Bytes, class Abi> class basic_mask {
public:
[…]
constexpr basic_mask() noexcept = default;
// 29.10.9.2 [simd.mask.ctor], basic_mask constructors
constexpr explicit basic_mask(same_as<value_type> auto) noexcept;
template<size_t UBytes, class UAbi>
constexpr explicit basic_mask(const basic_mask<UBytes, UAbi>&) noexcept;
template<class G>
constexpr explicit basic_mask(G&& gen) noexcept;
template<same_as<bitset<size()>> T>
constexpr basic_mask(const Tbitset<size()>& b) noexcept;
template<unsigned_integral T> requires (!same_as<T, value_type>)
constexpr explicit basic_mask(Tunsigned_integral auto val) noexcept;
[…]
};
}
Modify 29.10.9.2 [simd.mask.ctor] as indicated:
constexpr explicit basic_mask(same_as<value_type> auto x) noexcept;[…]-1- Effects: Initializes each element with
x.template<same_as<bitset<size()>> T> constexpr basic_mask(const Tbitset<size()>& b) noexcept;-7- Effects: Initializes the
ith element withb[i]for alliin the range[0, size()).template<unsigned_integral T> requires (!same_as<T, value_type>) constexpr explicit basic_mask(Tunsigned_integral autoval) noexcept;-8- Effects: Initializes the first
Melements to the corresponding bit values inval, […]
constant_wrapper's pseudo-mutators are underconstrained
Section: 21.3.5 [const.wrap.class] Status: New Submitter: Hewill Kang Opened: 2025-09-24 Last modified: 2025-10-17
Priority: 1
View all issues with New status.
Discussion:
Unlike other operators, constant_wrapper's pseudo-mutators only require that the wrapped type has
corresponding mutators, but do not require them to be constexpr or to return a sensible value.
This inconsistency loses the SFINAE friendliness (demo):
#include <type_traits>
void test(auto t) {
if constexpr (requires { +t; }) // ok
+t;
if constexpr (requires { -t; }) // ok
-t;
if constexpr (requires { ++t; }) // hard error
++t;
if constexpr (requires { --t; }) // hard error
--t;
}
struct S {
/* constexpr */ int operator+() const { return 0; }
/* constexpr */ int operator++() { return 0; }
constexpr void operator-() const { }
constexpr void operator--() { }
};
int main() {
test(std::cw<S{}>);
}
Since these pseudo-mutators have constraints, it is reasonable to further require constant expressions.
[2025-10-17; Reflector poll.]
Set priority to 1 after reflector poll.
operator+= changed between P2781R4 and P2781R5, intent is unclear.
Proposed resolution:
This wording is relative to N5014.
Modify 21.3.5 [const.wrap.class], class template constant_wrapper synopsis, as indicated:
[Drafting note: The requires clause follows the form of
constant_wrapper's function call operator.]
struct cw-operators { // exposition only
[…]
// pseudo-mutators
template<constexpr-param T>
constexpr auto operator++(this T) noexcept
requires requires(T::value_type x) { constant_wrapper<++x>(); }
{ return constant_wrapper<[] { auto c = T::value; return ++c; }()>{}; }
template<constexpr-param T>
constexpr auto operator++(this T, int) noexcept
requires requires(T::value_type x) { constant_wrapper<x++>(); }
{ return constant_wrapper<[] { auto c = T::value; return c++; }()>{}; }
template<constexpr-param T>
constexpr auto operator--(this T) noexcept
requires requires(T::value_type x) { constant_wrapper<--x>(); }
{ return constant_wrapper<[] { auto c = T::value; return --c; }()>{}; }
template<constexpr-param T>
constexpr auto operator--(this T, int) noexcept
requires requires(T::value_type x) { constant_wrapper<x-->(); }
{ return constant_wrapper<[] { auto c = T::value; return c--; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator+=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x += R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v += R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator-=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x -= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v -= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator*=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x *= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v *= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator/=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x /= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v /= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator%=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x %= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v %= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator&=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x &= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v &= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator|=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x |= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v |= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator^=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x ^= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v ^= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator<<=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x <<= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v <<= R::value; }()>{}; }
template<constexpr-param T, constexpr-param R>
constexpr auto operator>>=(this T, R) noexcept
requires requires(T::value_type x) { constant_wrapper<x >>= R::value>(); }
{ return constant_wrapper<[] { auto v = T::value; return v >>= R::value; }()>{}; }
};
<simd> doesn't provide std::begin/end
Section: 24.7 [iterator.range] Status: New Submitter: Hewill Kang Opened: 2025-09-26 Last modified: 2025-10-17
Priority: 3
View other active issues in [iterator.range].
View all other issues in [iterator.range].
View all issues with New status.
Discussion:
std::simd::basic_vec and std::simd::basic_mask are ranges since P3480R6,
it is reasonable to enable range access utilities when introducing <simd>.
[2025-10-17; Reflector poll.]
Set priority to 3 after reflector poll.
"This will be the first time we add the free-function begn/end interface to support a type where end returns a sentinel.
I would not want to set precedent here without at least taking the time to think about it, and acknowledge a deliberate intent."
"The intent has been deliberate since P0184R0."
Proposed resolution:
This wording is relative to N5014.
Modify 24.7 [iterator.range] as indicated:
-1- In addition to being available via inclusion of the
<iterator>header, the function templates in 24.7 [iterator.range] are available when any of the following headers are included:<array>,<deque>,<flat_map>,<flat_set>,<forward_list>,<hive>,<inplace_vector>,<list>,<map>,<regex>,<set>,<simd>,<span>,<string>,<string_view>,<unordered_map>,<unordered_set>, and<vector>.
std::simd::select(bool c, const T& a, const U& b) is underconstrained
Section: 29.10.8.13 [simd.alg] Status: New Submitter: Hewill Kang Opened: 2025-09-27 Last modified: 2025-10-22
Priority: 3
View all issues with New status.
Discussion:
This function currently only requires that c ? a : b be a well-formed expression, which simply
returns c ? a : b.
basic_vec, basic_mask, or vectorizable type,
requiring T and U to be copyable seems reasonable since they are trivially copyable.
It shouldn't take non-copyable objects and produce hard errors in the function body.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
Split opinion between further pinning down exact condition (is_constructible_v,
decltype(auto(c ? a : b))), and accepting the PR (copyable) as this is
not meant to be generic facility.
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.3 [simd.syn] as indicated:
namespace std::simd {
[…]
// 29.10.8.13 [simd.alg], algorithms
[…]
template<copyableclass T, copyableclass U>
constexpr auto select(bool c, const T& a, const U& b)
-> remove_cvref_t<decltype(c ? a : b)>;
[…]
};
Modify 29.10.8.13 [simd.alg] as indicated:
template<copyableclassT, copyableclassU> constexpr auto select(bool c, const T& a, const U& b) -> remove_cvref_t<decltype(c ? a : b)>;-9- Effects: Equivalent to:
return c ? a : b;
<stacktrace> doesn't provide std::begin/end
Section: 24.7 [iterator.range] Status: Open Submitter: Hewill Kang Opened: 2025-09-27 Last modified: 2025-10-20
Priority: 3
View other active issues in [iterator.range].
View all other issues in [iterator.range].
View all issues with Open status.
Discussion:
basic_stacktrace is explicitly specified as a reversible container, an allocator-aware container,
and a const-qualified sequence container, with members begin/end, rbegin/rend,
cbegin/cend, crbegin/crend, empty, size, etc.
<stacktrace>, just like other containers.
[2025-10-07; Reflector poll]
Approved as Tentatively Ready, but this is a duplicate of 3625(i) which will be resolved by P3016R6. So move Status New → Open.
[2025-10-20; Set to same priority as 3625(i) (i.e. P3).]
Proposed resolution:
This wording is relative to N5014.
Modify 24.7 [iterator.range] as indicated:
-1- In addition to being available via inclusion of the
<iterator>header, the function templates in 24.7 [iterator.range] are available when any of the following headers are included:<array>,<deque>,<flat_map>,<flat_set>,<forward_list>,<hive>,<inplace_vector>,<list>,<map>,<regex>,<set>,<span>,<stacktrace>,<string>,<string_view>,<unordered_map>,<unordered_set>, and<vector>.
va_start with C23Section: 17.14.2 [cstdarg.syn] Status: New Submitter: Jakub Jelinek Opened: 2025-10-01 Last modified: 2025-10-14
Priority: 1
View other active issues in [cstdarg.syn].
View all other issues in [cstdarg.syn].
View all issues with New status.
Discussion:
P3348R4 changed the va_start macro to match C23,
but the following wording from C is not present in C++:
If any additional arguments expand to include unbalanced parentheses, or a preprocessing token that does not convert to a token, the behavior is undefined.
The importance of that wording was not realized during review of P3348R4.
The wording is intended to ensure that any discarded arguments to
va_start are actually lexable by the compiler,
rather than containing unbalanced parentheses or brackets.
It also makes the following undefined:
#define BAD ); format_disk(
va_start(ap, BAD);
[2025-10-14; Reflector poll]
Set priority to 1 after reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 17.14.2 [cstdarg.syn] as indicated:
(1.2) — If more than one argument is present for
va_startand any of the second or subsequent arguments expands to include unbalanced parentheses, or a preprocessing token that does not convert to a token, the program is ill-formed, no diagnostic required. The preprocessing tokens comprising the second and subsequent arguments tova_start(if any) are discarded. [Note 1:va_startaccepts a second argument for compatibility with prior revisions of C++. — end note]
ranges::for_each possibly behaves differently from range-based forSection: 25.4.2 [range.range] Status: SG9 Submitter: Jiang An Opened: 2025-09-28 Last modified: 2025-10-23
Priority: 2
View all other issues in [range.range].
View all issues with SG9 status.
Discussion:
It was found in the blog post
"When ranges::for_each behaves differently from for" that ranges::for_each
can behave differently from range-based for, because
ranges::begin and ranges::end possibly use different rules, i.e. one calls a member
and the other calls an ADL-found non-member function, and
these CPOs continue to perform ADL when a member begin/end is found but the
function call is not valid, while the range-for stops and renders the program ill-formed.
Perhaps the intent of Ranges was that the ranges::range concept should be stricter than
plain range-for and all range types can be iterated via range-for with the same semantics
as ranges::for_each. However, it seems very difficult (if not impossible) for a library
implementation to tell whether a class has member begin/end but the corresponding member
call is ill-formed with C++20 core language rules, and such determination is critical for
eliminating the semantic differences between ranges::for_each and range-for.
[2025-10-23; Reflector poll; Status changed: New → SG9 and P2.]
This is certainly evoluationary question and should go to LEWG/SG9. It would disallow having unrelated begin/end members, where the range interface is provided by hidden friends instead of those members.
Proposed resolution:
This wording is relative to N5014.
Two mutually exclusive resolutions are proposed here. One enforces semantic-identity checks, while the other doesn't and makes weird types satisfy but not model the range concept. I prefer the stricter one because the semantic-identity checks are fully static, but this probably requires compilers to add new intrinsics when reflection is absent.
Option A: (stricter)
Modify 25.3.2 [range.access.begin] as indicated:
-2- Given a subexpression
Ewith typeT, lettbe an lvalue that denotes the reified object forE. Then:
(2.1) — If
Eis an rvalue andenable_borrowed_range<remove_cv_t<T>>isfalse,ranges::begin(E)is ill-formed.(2.2) — Otherwise, if
Tis an array type (9.3.4.5 [dcl.array]) andremove_all_extents_t<T>is an incomplete type,ranges::begin(E)is ill-formed with no diagnostic required.(2.3) — Otherwise, if
Tis an array type,ranges::begin(E)is expression-equivalent tot + 0.(2.4) — Otherwise, if
auto(t.begin())is a valid expression whose type modelsinput_or_output_iterator,ranges::begin(E)is expression-equivalent toauto(t.begin()).(2.?) — Otherwise, if
remove_cvref_t<T>is a class type and search forbeginin the scope of that class finds at least one declaration,ranges::begin(E)is ill-formed.(2.5) — Otherwise, if
Tis a class or enumeration type andauto(begin(t))is a valid expression whose type modelsinput_or_output_iteratorwhere the meaning ofbeginis established as-if by performing argument-dependent lookup only (6.5.4 [basic.lookup.argdep]), thenranges::begin(E)is expression-equivalent to that expression.(2.6) — Otherwise,
ranges::begin(E)is ill-formed.
Modify 25.3.3 [range.access.end] as indicated:
-2- Given a subexpression
Ewith typeT, lettbe an lvalue that denotes the reified object forE. Then:
(2.1) — If
Eis an rvalue andenable_borrowed_range<remove_cv_t<T>>isfalse,ranges::end(E)is ill-formed.(2.2) — Otherwise, if
Tis an array type (9.3.4.5 [dcl.array]) andremove_all_extents_t<T>is an incomplete type,ranges::end(E)is ill-formed with no diagnostic required.(2.3) — Otherwise, if
Tis an array of unknown bound,ranges::end(E)is ill-formed.(2.4) — Otherwise, if
Tis an array,ranges::end(E)is expression-equivalent tot + extent_v<T>.(2.5) — Otherwise, if
auto(t.end())is a valid expression whose type modelssentinel_for<iterator_t<T>>thenranges::end(E)is expression-equivalent toauto(t.end()).(2.?) — Otherwise, if
remove_cvref_t<T>is a class type and search forendin the scope of that class finds at least one declaration,ranges::end(E)is ill-formed.(2.6) — Otherwise, if
Tis a class or enumeration type andauto(end(t))is a valid expression whose type modelssentinel_for<iterator_t<T>>where the meaning of end is established as-if by performing argument-dependent lookup only (6.5.4 [basic.lookup.argdep]), thenranges::end(E)is expression-equivalent to that expression.(2.7) — Otherwise,
ranges::end(E)is ill-formed.
Modify 25.4.2 [range.range] as indicated:
-1- […]
template<class T> concept range = requires(T& t) { ranges::begin(t); // sometimes equality-preserving (see below) ranges::end(t); } && has-consistent-begin-end<T>; // see below-2- […]
-3- […] -?-has-consistent-begin-end<T>is a constant expression of typebool, and it istrueif and only if for thetintroduced in the requires-expression above, either
(?.1) — both
ranges::begin(t)andranges::end(t)are specified to selectauto(t.begin())andauto(t.end())respectively, or(?.2) — both
ranges::begin(t)andranges::end(t)are specified not to selectauto(t.begin())andauto(t.end())respectively.
Option B: (looser)
Modify 25.4.2 [range.range] as indicated:
-1- […]
template<class T> concept range = requires(T& t) { ranges::begin(t); // sometimes equality-preserving (see below) ranges::end(t); }-2- Given an expression
tsuch thatdecltype((t))isT&,Tmodelsrangeonly if
(2.1) — […]
(2.2) — […]
(2.3) — […]
(2.?) — The range-based
forstatementfor (auto&& x: t);is well-formed, and variable definitionsauto begin = begin-expr;andauto end = end-expr;in the equivalent form (8.6.5 [stmt.ranged]) of that statement are semantically equivalent toauto begin = ranges::begin(t);andauto end = ranges::end(t);respectively.
simd::basic_vec(U&&) default template parameterSection: 29.10.7.2 [simd.ctor] Status: LEWG Submitter: Hewill Kang Opened: 2025-09-29 Last modified: 2025-10-22
Priority: 4
View other active issues in [simd.ctor].
View all other issues in [simd.ctor].
View all issues with LEWG status.
Discussion:
simd::basic_vec supports complex after P2663R7, defaulting
the template parameter of its broadcast constructor seems to be reasonable, as this allows the
intuitive spelling:
simd::vec<complex<double>> sc1 ({-1.0, 0.5}); // current ill-formed
simd::vec<complex<double>> sc2 = {{-1.0, 0.5}}; // current ill-formed
[2025-10-22; Reflector poll. Status changed: New → LEWG with priority 4.]
This narrows design space wrt using braces for initialization
of basic_vec, especially if we allow user-defined types.
LWG 4230(i) would address this by allowing
vec<complex<double>>(1., 1.);
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.7.1 [simd.overview] as indicated:
namespace std::simd {
template<class T, class Abi> class basic_vec {
public:
[…]
template<class U = T>
constexpr explicit(see below) basic_vec(U&& value) noexcept;
[…]
};
}
Modify 29.10.7.2 [simd.ctor] as indicated:
template<class U = T> constexpr explicit(see below) basic_vec(U&& value) noexcept;-1- […]
simd::unchecked_load misses difference type castingSection: 29.10.8.7 [simd.loadstore] Status: New Submitter: Hewill Kang Opened: 2025-09-29 Last modified: 2025-10-22
Priority: 3
View other active issues in [simd.loadstore].
View all other issues in [simd.loadstore].
View all issues with New status.
Discussion:
Currently, simd::unchecked_load/partial_load/unchecked_store/partial_store construct a
span via span(first, n) when taking an iterator first and its difference type n.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
Question was raised if we should use size_t instead of
iter_difference_t for consistency with span.
Proposed resolution:
This wording is relative to N5014.
In subclause 29.10.8.7 [simd.loadstore] replace all occurrences of
R(first, n)
by
R(first, static_cast<size_t>(n))
simd::unchecked_scatter_to is underconstrainedSection: 29.10.8.11 [simd.permute.memory] Status: New Submitter: Hewill Kang Opened: 2025-09-29 Last modified: 2025-10-22
Priority: 2
View all issues with New status.
Discussion:
Both simd::unchecked_scatter_to and simd::partial_scatter_to are used to write a
simd::vec into a range R.
R to be contiguous_range and sized_range.
Requiring R to be output_range is also necessary; otherwise, the
constant_range cannot be written.
[2025-10-22; Reflector poll.]
Set priority to 2 after reflector poll.
This issue is related to LWG 4420(i).
The Constrains needs to be updated to handle conversions between float
and float16_t and similar cases.
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.3 [simd.syn] as indicated:
namespace std::simd {
[…]
template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags>
requires ranges::sized_range<R> && ranges::output_range<R, typename V::value_type>
constexpr void
unchecked_scatter_to(const V& v, R&& out,
const I& indices, flags<Flags...> f = {});
template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags>
requires ranges::sized_range<R> && ranges::output_range<R, typename V::value_type>
constexpr void
unchecked_scatter_to(const V& v, R&& out, const typename I::mask_type& mask,
const I& indices, flags<Flags...> f = {});
template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags>
requires ranges::sized_range<R> && ranges::output_range<R, typename V::value_type>
constexpr void
partial_scatter_to(const V& v, R&& out,
const I& indices, flags<Flags...> f = {});
template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags>
requires ranges::sized_range<R> && ranges::output_range<R, typename V::value_type>
constexpr void
partial_scatter_to(const V& v, R&& out, const typename I::mask_type& mask,
const I& indices, flags<Flags...> f = {});
[…]
}
Modify 29.10.8.11 [simd.permute.memory] as indicated:
template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags> requires ranges::sized_range<R> && ranges::output_range<R, typename V::value_type> constexpr void unchecked_scatter_to(const V& v, R&& out, const I& indices, flags<Flags...> f = {}); template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags> requires ranges::sized_range<R> && ranges::output_range<R, typename V::value_type> constexpr void unchecked_scatter_to(const V& v, R&& out, const typename I::mask_type& mask, const I& indices, flags<Flags...> f = {});-10- Let
[…]maskbetypename I::mask_type(true)for the overload with nomaskparameter.template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags> requires ranges::sized_range<R> && ranges::output_range<R, typename V::value_type> constexpr void partial_scatter_to(const V& v, R&& out, const I& indices, flags<Flags...> f = {}); template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags> requires ranges::sized_range<R> && ranges::output_range<R, typename V::value_type> constexpr void partial_scatter_to(const V& v, R&& out, const typename I::mask_type& mask, const I& indices, flags<Flags...> f = {});-13- Let
[…]maskbetypename I::mask_type(true)for the overload with nomaskparameter.
simd::unchecked_load(I first, S last) construct span maybe ill-formedSection: 29.10.8.7 [simd.loadstore] Status: New Submitter: Hewill Kang Opened: 2025-09-30 Last modified: 2025-10-17
Priority: 3
View other active issues in [simd.loadstore].
View all other issues in [simd.loadstore].
View all issues with New status.
Discussion:
Currently, simd::unchecked_load/partial_load/unchecked_store/partial_store
constructs a span via span(first, last) when taking an iterator-sentinel pair
first and last.
struct I {
using value_type = int;
using difference_type = int;
using iterator_category = std::contiguous_iterator_tag;
// contiguous iterator operators
// ...
operator int() const;
};
int main() {
std::simd::unchecked_load(I{}, I{});
}
Above, unchecked_load invokes unchecked_load(I first, S last) and we attempt to
construct span through span(first, last).
However, this is invalid because the constructor requires that the sentinel type should not be convertible to
size_t, so we fall back into span(first, n) via implicitly converting I to size_t. Such
behavior is subtle and likely unintended.
struct I {
using value_type = int;
using difference_type = int;
using iterator_category = std::contiguous_iterator_tag;
// contiguous iterator operators
// ...
operator int() &&;
};
int main() {
std::simd::unchecked_load(I{}, I{});
}
We still attempt to construct the span by calling span(first, last), which is invalid,
but because the lvalue sentinel cannot be converted to size_t, the call of span(first, n)
is also invalid. This makes the construction of the span ill-formed and leads to a hard error in
the function body.
[2025-10-17; Reflector poll.]
Set priority to 3 after reflector poll.
"unchecked_load(first, last) needs to be equivalent to unchecked_load(span(first, last)).
If span converts last to size_t then simd should do the same, not span(first, last-first).
Should simd (first,last) overloads have the same
is_convertible_v<End, size_t>
is false constraint as span?"
Proposed resolution:
This wording is relative to N5014.
In subclause 29.10.8.7 [simd.loadstore] replace all occurrences of
R(first, last)
by
R(first, static_cast<size_t>(last - first))
write_env implementation-detail lambda should have explicit return typeSection: 33.9.12.3 [exec.write.env] Status: New Submitter: Robert A.H. Leahy Opened: 2025-09-29 Last modified: 2025-10-23
Priority: 2
View all issues with New status.
Discussion:
In 33.9.12.3 [exec.write.env] the impls for std::execution::write_env has get-env
specified as:
static constexpr auto get-env =
[](auto, const auto& state, const auto& rcvr) noexcept {
return see-below;
};
This uses automatic return type deduction which means that the body of the lambda is actually instantiated in a SFINAE-unfriendly way when attempting to compute various properties of the lambda (invocability, return type, et cetera). This is undesirable and surprising as has been discovered in actual use/deployment (see: https://github.com/NVIDIA/stdexec/pull/1654).
The fix is to explicitly provide a return type for the lambda which computes the type of the body. Note: This issue may indicate that the use of automatic return type deduction in the specification of sender algorithms more generally ought to be reconsidered/-examined.[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
Proposed resolution:
inplace_vector(from_range_t, R&& rg)Section: 23.2.4 [sequence.reqmts], 23.3.16.2 [inplace.vector.cons] Status: LEWG Submitter: Hewill Kang Opened: 2025-10-01 Last modified: 2025-10-22
Priority: 3
View other active issues in [sequence.reqmts].
View all other issues in [sequence.reqmts].
View all issues with LEWG status.
Discussion:
Consider:
std::array<int, 42> a; std::inplace_vector<int, 5> v(std::from_range, a);
The above throws std::bad_alloc at runtime because the size of array is larger than
capacity of inplace_vector. However, we should reject it at compile time since the
array size is a constant expression.
<simd>,
it's worth applying that here as well. Compile-time errors are better than runtime ones.
[2025-10-22; Reflector poll. Status changed: New → LEWG and P3.]
General support for change, after LEWG approval.
Suggestion was made that this could be extended to all containers,
but is unlikely to be triggred in real word, as it requires ranges
with static size greater than size_t(-1).
Proposed resolution:
This wording is relative to N5014.
Modify 23.2.4 [sequence.reqmts] as indicated:
a.assign_range(rg)-60- Result:
-61- Mandates:voidassignable_from<T&, ranges::range_reference_t<R>>is modeled. Forinplace_vector, ifranges::size(rg)is a constant expression thenranges::size(rg)≤a.max_size().
Modify 23.3.16.2 [inplace.vector.cons] as indicated:
template<container-compatible-range<T> R> constexpr inplace_vector(from_range_t, R&& rg);-?- Mandates: If
-9- Effects: Constructs anranges::size(rg)is a constant expression thenranges::size(rg)≤N.inplace_vectorwith the elements of the rangerg. -10- Complexity: Linear inranges::distance(rg).
span(R&& r)Section: 23.7.2.2.2 [span.cons] Status: New Submitter: Hewill Kang Opened: 2025-10-02 Last modified: 2025-10-20
Priority: 3
View all other issues in [span.cons].
View all issues with New status.
Discussion:
It is preferable to reject span<int, 42>(views::empty<int>)
at compile-time after P2280R4, since applying ranges::size on those
ranges is a constant expression now.
[2025-10-20; Reflector poll.]
Set priority to 3 after reflector poll.
The opinions on reflector discussion where split regarding, if this should be considered LEWG matter.
Question was raised, if ranges::size(r) == N is required
to be usable at compile-time for integer-class types.
Proposed resolution:
This wording is relative to N5014.
Modify 23.7.2.2.2 [span.cons] as indicated:
template<class R> constexpr explicit(extent != dynamic_extent) span(R&& r);-?- Mandates: If
-16- Constraints: Letextentis not equal todynamic_extentandranges::size(r)is a constant expression, thenranges::size(r) == extentistrue.Uberemove_reference_t<ranges::range_reference_t<R>>. […]
enable_nonlocking_formatter_optimization for durations with custom repSection: 30.12 [time.format] Status: New Submitter: Tomasz Kamiński Opened: 2025-10-02 Last modified: 2025-10-14
Priority: 3
View other active issues in [time.format].
View all other issues in [time.format].
View all issues with New status.
Discussion:
Currently the enable_nonlocking_formatter_optimization is enabled for
duration<Rep, Ratio> if it is enabled for Rep.
template<class Rep, class Period>
constexpr bool enable_nonlocking_formatter_optimization<chrono::duration<Rep, Period>>
= enable_nonlocking_formatter_optimization<Rep>;
However, this does not take into the consideration that for custom Rep types, the arithmetic
operations on Rep may also lock the stream leading to deadlock (for example log on overflow).
Since they are required to handle the specifiers such as %S we should specialize
enable_nonlocking_formatter_optimization only for built-in types:
template<class Rep, class Period>
constexpr bool enable_nonlocking_formatter_optimization<chrono::duration<Rep, Period>>
= is_arithmetic_v<Rep>;
Furthermore, for all types that are currently templated on Duration (hh_mm_ss, sys_time,
local_time, etc.), we enable_nonlocking_formatter_optimization by default. This again does
not take into consideration the arithmetic operations performed as duration. We should specialize
enable_nonlocking_formatter_optimization for all of them to be enabled if
enable_nonlocking_formatter_optimization is enabled for duration:
template<class Duration>
constexpr bool enable_nonlocking_formatter_optimization<chrono::hh_mm_ss<Duration>>
= enable_nonlocking_formatter_optimization<Duration>;
Note, that forwarding to enable_nonlocking_formatter_optimization on Duration instead of
checking Duration::rep allows users to specialize enable_nonlocking_formatter_optimization for
Durations with there custom representation types.
[2025-10-14; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
This wording is relative to N5014.
Modify 30.12 [time.format] as indicated:
-8- For
chrono::duration,chrono::hh_mm_ss,chrono::sys_time,chrono::utc_time,chrono::tai_time,chrono::gps_time,chrono::file_time,chrono::local_time,chrono::local-time-format-t, andchrono::zoned_timethe library only provides the following specializations ofenable_nonlocking_formatter_optimization:template<class Rep, class Period> constexpr bool enable_nonlocking_formatter_optimization< chrono::duration<Rep, Period>> =enable_nonlocking_formatter_optimizationis_arithmetic_v<Rep>; template<class Duration> constexpr bool enable_nonlocking_formatter_optimization<chrono::hh_mm_ss<Duration>> = enable_nonlocking_formatter_optimization<Duration>; template<class Duration> constexpr bool enable_nonlocking_formatter_optimization<chrono::sys_time<Duration>> = enable_nonlocking_formatter_optimization<Duration>; template<class Duration> constexpr bool enable_nonlocking_formatter_optimization<chrono::utc_time<Duration>> = enable_nonlocking_formatter_optimization<Duration>; template<class Duration> constexpr bool enable_nonlocking_formatter_optimization<chrono::tai_time<Duration>> = enable_nonlocking_formatter_optimization<Duration>; template<class Duration> constexpr bool enable_nonlocking_formatter_optimization<chrono::gps_time<Duration>> = enable_nonlocking_formatter_optimization<Duration>; template<class Duration> constexpr bool enable_nonlocking_formatter_optimization<chrono::file_time<Duration>> = enable_nonlocking_formatter_optimization<Duration>; template<class Duration> constexpr bool enable_nonlocking_formatter_optimization<chrono::local_time<Duration>> = enable_nonlocking_formatter_optimization<Duration>; template<class Duration> constexpr bool enable_nonlocking_formatter_optimization<chrono::local-time-format-t<Duration>> = enable_nonlocking_formatter_optimization<Duration>;
-9- Forchrono::zoned_timethe library only provides the following specialization ofenable_nonlocking_formatter_optimization:template<class Duration> constexpr bool enable_nonlocking_formatter_optimization< chrono::zoned_time<Duration, const std::chrono::time_zone*>> =trueenable_nonlocking_formatter_optimization<Duration>;
join_view should be sized_range when applied to ranges of simd::vecSection: 25.7.14.2 [range.join.view], 25.7.15.2 [range.join.with.view], 25.7.16.2 [range.lazy.split.view] Status: LEWG Submitter: Hewill Kang Opened: 2025-10-02 Last modified: 2025-10-23
Priority: 3
View other active issues in [range.join.view].
View all other issues in [range.join.view].
View all issues with LEWG status.
Discussion:
Consider:
Collecting asimd::vec into a vector for output is a common use case. P3480R6
makes simd::vec a range so we can simply flatten it with views::join (original example from
the paper):
std::vector<std::simd::vec<float>> data; auto range_of_float = data | std::views::join;
In this case, it makes sense for join_view to be sized_range because simd::vec::size()
is a constant expression that can be multiplied by the original vector size to get the
result size of the join_view.
<ranges>, we use the tiny-range concept
to consider types that can obtain static sizes specifically, and simd::vec
seems to be a good fit.
[2025-10-23; Reflector poll; Status changed: New → LEWG and P3.]
Introducting statically sized ranges and handling them in views, should be handle as a paper.
The proposed change is also relevant to fixed-size span.
Proposed resolution:
This wording is relative to N5014.
Modify 25.7.14.2 [range.join.view] as indicated:
[Drafting note: The proposed wording follows the
tiny-range's way to check ifR::size()is a constant expression instead of further checkingranges::size(r)for simplicity.]
namespace std::ranges {
template<auto> struct require-constant; // exposition only
template<class R>
concept static-sized-range = // exposition only
sized_range<R> &&
requires { typename require-constant<remove_reference_t<R>::size()>; };
template<input_range V>
requires view<V> && input_range<range_reference_t<V>>>
class join_view : public view_interface<join_view<V>> {
[…]
public:
[…]
constexpr auto size()
requires sized_range<V> && static-sized-range<InnerRng> {
using CT = common_type_t<range_size_t<V>, range_size_t<InnerRng>>;
return CT(ranges::size(base_)) * CT(remove_reference_t<InnerRng>::size());
}
constexpr auto size() const
requires sized_range<const V> &&
static-sized-range<range_reference_t<const V>> {
using InnerConstRng = range_reference_t<const V>;
using CT = common_type_t<range_size_t<V>, range_size_t<InnerConstRng>>;
return CT(ranges::size(base_)) * CT(remove_reference_t<InnerConstRng>::size());
}
};
[…]
}
Modify 25.7.15.2 [range.join.with.view] as indicated:
namespace std::ranges {
[…]
template<input_range V, forward_range Pattern>
requires view<V> && input_range<range_reference_t<V>>
&& view<Pattern>
&& concatable<range_reference_t<V>, Pattern>
class join_with_view : public view_interface<join_with_view<V, Pattern>> {
[…]
public:
[…]
constexpr auto size()
requires sized_range<V> && sized_range<Pattern> &&
static-sized-range<InnerRng> {
using CT = common_type_t<
range_size_t<V>, range_size_t<InnerRng>, range_size_t<Pattern>>;
const auto base_size = ranges::size(base_);
if (base_size == 0)
return CT(0);
return CT(base_size) * CT(remove_reference_t<InnerRng>::size()) +
CT(base_size - 1) * CT(ranges::size(pattern_));
}
constexpr auto size() const
requires sized_range<const V> && sized_range<const Pattern> &&
static-sized-range<range_reference_t<const V>> {
using InnerConstRng = range_reference_t<const V>;
using CT = common_type_t<
range_size_t<const V>, range_size_t<InnerConstRng>, range_size_t<const Pattern>>;
const auto base_size = ranges::size(base_);
if (base_size == 0)
return CT(0);
return CT(base_size) * CT(remove_reference_t<InnerConstRng>::size()) +
CT(base_size - 1) * CT(ranges::size(pattern_));
}
};
[…]
}
Modify 25.7.16.2 [range.lazy.split.view] as indicated:
namespace std::ranges {
template<auto> struct require-constant; // exposition only
template<class R>
concept tiny-range = // exposition only
static-sized-range<R>sized_range<R> &&
requires { typename require-constant<remove_reference_t<R>::size()>; } &&
(remove_reference_t<R>::size() <= 1);
[…]
}
Section: 29.10.9.1 [simd.mask.overview] Status: New Submitter: Arthur O'Dwyer Opened: 2025-10-02 Last modified: 2025-10-22
Priority: 3
View all issues with New status.
Discussion:
29.10.9.1 [simd.mask.overview] has
namespace std::simd {
template<class V>
class simd-iterator { // exposition only
V* data_ = nullptr; // exposition only
simd-size-type offset_ = 0; // exposition only
constexpr simd-iterator(V& d, simd-size-type off) noexcept; // exposition only
[…]
};
[…]
template<size_t Bytes, class Abi>
class basic_mask {
public:
using value_type = bool;
using abi_type = Abi;
using iterator = simd-iterator<basic_mask>;
using const_iterator = simd-iterator<const basic_mask>;
constexpr iterator begin() noexcept { return {*this, 0}; }
constexpr const_iterator begin() const noexcept { return {*this, 0}; }
constexpr const_iterator cbegin() const noexcept { return {*this, 0}; }
constexpr default_sentinel_t end() const noexcept { return {}; }
constexpr default_sentinel_t cend() const noexcept { return {}; }
[…]
It's unclear whether the "exposition-only" constructor is required to be present. If it is present,
as written, without explicit, then {someBasicMask, 0} becomes a valid initializer for an iterator.
Evidence in favor of its intentionality: the use of return {*this, 0} in basic_mask::begin().
(The constructor is private, but it still participates in overload resolution and will ambiguate
other possible conversions.) But this makes many expressions ambiguous that could be unambiguous to a human.
using Mask = std::simd::mask<int>;
void overloaded(std::string, std::pair<Mask, int> kv);
void overloaded(std::string, Mask::iterator it);
int main() {
Mask m;
overloaded("the pair is", {m, 0}); // ambiguous?
}
At the very least, we should say that this list-initialization is intentional, and add wording to
class simd-iterator and/or remove the "exposition only" from simd-iterator's
constructor. That makes it clear that the above program is indeed intended to be ambiguous. But IMO
we should instead simply make the above program valid.
[2025-10-22; Reflector poll.]
Set priority to 3 after reflector poll.
simd-iterator can only be constructed only by basic_vec,
and basic_mask objects.
This is NAD as user cannot rely on standard types not being constructible per
16.4.6.5 [member.functions] p2. The example would not be fixed by adding
the explicit in a conforming implementation, and changes in return
specification are editorial.
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.6 [simd.iterator] as indicated:
namespace std::simd {
template<class V>
class simd-iterator { // exposition only
V* data_ = nullptr; // exposition only
simd-size-type offset_ = 0; // exposition only
constexpr explicit simd-iterator(V& d, simd-size-type off) noexcept; // exposition only
[…]
};
}
Modify 29.10.7.1 [simd.overview] as indicated:
namespace std::simd {
template<size_t T, class Abi> class basic_vec {
public:
using value_type = T;
using mask_type = basic_mask<sizeof(T), Abi>;
using abi_type = Abi;
using iterator = simd-iterator<basic_vec>;
using const_iterator = simd-iterator<const basic_vec>;
constexpr iterator begin() noexcept { return iterator({*this, 0}); }
constexpr const_iterator begin() const noexcept { return const_iterator({*this, 0}); }
constexpr const_iterator cbegin() const noexcept { return const_iterator({*this, 0}); }
constexpr default_sentinel_t end() const noexcept { return {}; }
constexpr default_sentinel_t cend() const noexcept { return {}; }
[…]
};
}
Modify 29.10.9.1 [simd.mask.overview] as indicated:
namespace std::simd {
template<size_t Bytes, class Abi> class basic_mask {
public:
using value_type = bool;
using abi_type = Abi;
using iterator = simd-iterator<basic_mask>;
using const_iterator = simd-iterator<const basic_mask>;
constexpr iterator begin() noexcept { return iterator({*this, 0}); }
constexpr const_iterator begin() const noexcept { return const_iterator({*this, 0}); }
constexpr const_iterator cbegin() const noexcept { return const_iterator({*this, 0}); }
constexpr default_sentinel_t end() const noexcept { return {}; }
constexpr default_sentinel_t cend() const noexcept { return {}; }
[…]
};
}
mdspan constructor should disallow derived to base conversionsSection: 23.7.3.6.2 [mdspan.mdspan.cons] Status: New Submitter: Hewill Kang Opened: 2025-10-05 Last modified: 2025-10-23
Priority: 3
View all issues with New status.
Discussion:
Unlike ranges::subrange or span, mdspan syntactically allows a multidimensional
viewing base class via a derived class pointer (demo):
#include <span>
#include <ranges>
#include <mdspan>
struct Base {};
struct Derived : Base {};
std::array<Derived, 12> arr;
std::ranges::subrange<Base*> s(arr); // error, slicing
std::span<Base> sp(arr.data(), arr.size()); // error, slicing
std::mdspan<Base, std::dims<1>> msp(arr.data(), arr.size()); // ok
Given that we intend to reject object slicing for both default_accessor and
aligned_accessor, there seems no reason not to reject this invalid pointer
arithmetic for mdspan.
[2025-10-23; Reflector poll.]
Set priority to 3 after reflector poll.
Even if data_handle_type is a pointer to value_type, this does not mean
that it points to contiguous range of value_type objects. Accessor may
provide different access parttern.
Proposed resolution:
This wording is relative to N5014.
[Drafting note: The exposition-only concept
convertible-to-non-slicingcomes from 25.5.4.1 [range.subrange.general].]
Modify 23.7.3.6.1 [mdspan.mdspan.overview] as indicated:
namespace std {
template<class ElementType, class Extents, class LayoutPolicy = layout_right,
class AccessorPolicy = default_accessor<ElementType>>
class mdspan {
public:
using extents_type = Extents;
using layout_type = LayoutPolicy;
using accessor_type = AccessorPolicy;
using mapping_type = typename layout_type::template mapping<extents_type>;
using element_type = ElementType;
using value_type = remove_cv_t<element_type>;
using index_type = typename extents_type::index_type;
using size_type = typename extents_type::size_type;
using rank_type = typename extents_type::rank_type;
using data_handle_type = typename accessor_type::data_handle_type;
using reference = typename accessor_type::reference;
[…]
template<class... OtherIndexTypes>
constexpr explicit mdspan(convertible-to-non-slicing<data_handle_type> auto ptr, OtherIndexTypes... exts);
template<class OtherIndexType, size_t N>
constexpr explicit(N != rank_dynamic())
mdspan(convertible-to-non-slicing<data_handle_type> auto p, span<OtherIndexType, N> exts);
template<class OtherIndexType, size_t N>
constexpr explicit(N != rank_dynamic())
mdspan(convertible-to-non-slicing<data_handle_type> auto p, const array<OtherIndexType, N>& exts);
constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const extents_type& ext);
constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m);
constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m, const accessor_type& a);
[…]
};
[…]
}
Modify 23.7.3.6.2 [mdspan.mdspan.cons] as indicated:
template<class... OtherIndexTypes> constexpr explicit mdspan(convertible-to-non-slicing<data_handle_type> auto p, OtherIndexTypes... exts);-4- Let
-5- Constraints: […] […]Nbesizeof...(OtherIndexTypes).template<class OtherIndexType, size_t N> constexpr explicit(N != rank_dynamic()) mdspan(convertible-to-non-slicing<data_handle_type> auto p, span<OtherIndexType, N> exts); template<class OtherIndexType, size_t N> constexpr explicit(N != rank_dynamic()) mdspan(convertible-to-non-slicing<data_handle_type> auto p, const array<OtherIndexType, N>& exts);-8- Constraints: […]
[…]constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const extents_type& ext);-11- Constraints: […]
[…]constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m);-14- Constraints: […]
[…]constexpr mdspan(convertible-to-non-slicing<data_handle_type> auto p, const mapping_type& m, const accessor_type& a);-17- Preconditions: […]
[…]
optional::value_or return statement is inconsistent with MandatesSection: 22.5.3.7 [optional.observe], 22.5.4.6 [optional.ref.observe], 22.8.6.6 [expected.object.obs] Status: New Submitter: Hewill Kang Opened: 2025-09-06 Last modified: 2025-10-16
Priority: 3
View other active issues in [optional.observe].
View all other issues in [optional.observe].
View all issues with New status.
Discussion:
optional<T>::value_or(U&&) requires is_convertible_v<U&&, T>
to ensure that T can be convert from U when optional has no value.
T by static_cast, which is not checked by
is_convertible_v since it only checks for implicit conversions.
This results in rare cases where Mandates may not be violated, but value_or is ill-formed
(demo):
struct S {
operator int() const;
explicit operator int() = delete;
};
int main() {
std::optional<int>{}.value_or(S{}); // fire
}
It is reasonable to create objects that stick to Mandates. The same goes for expected::value_or.
[2025-10-16; Reflector poll]
Set priority to 3 after reflector poll.
This would need to use val instead of **this if LWG 4015(i)
is accepted.
Proposed resolution:
This wording is relative to N5014.
Modify 22.5.3.7 [optional.observe] as indicated:
template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) const &;-15- Mandates:
-16- Effects: Equivalent to:is_copy_constructible_v<T> && is_convertible_v<U&&, T>istrue.return has_value() ? **this : static_cast<T>(std::forward<U>(v));if (has_value()) return **this; return std::forward<U>(v);template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) &&;-17- Mandates:
-18- Effects: Equivalent to:is_move_constructible_v<T> && is_convertible_v<U&&, T>istrue.return has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v));if (has_value()) return std::move(**this); return std::forward<U>(v);
Modify 22.5.4.6 [optional.ref.observe] as indicated:
template<class U = remove_cv_t<T>> constexpr remove_cv_t<T> value_or(U&& u) const;-8- Let
-9- Mandates:Xberemove_cv_t<T>.is_constructible_v<X, T&> && is_convertible_v<U, X>istrue. -10- Effects: Equivalent to:return has_value() ? *val : static_cast<X>(std::forward<U>(u));if (has_value()) return *val; return std::forward<U>(u);
Modify 22.8.6.6 [expected.object.obs] as indicated:
template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) const &;-18- Mandates:
is_copy_constructible_v<T>istrueandis_convertible_v<U, T>istrue.-19- Returns:-?- Effects: Equivalent to:has_value() ? **this : static_cast<T>(std::forward<U>(v)).if (has_value()) return **this; return std::forward<U>(v);template<class U = remove_cv_t<T>> constexpr T value_or(U&& v) &&;-20- Mandates:
is_move_constructible_v<T>istrueandis_convertible_v<U, T>istrue.-21- Returns:-?- Effects: Equivalent to:has_value() ? std::move(**this) : static_cast<T>(std::forward<U>(v)).if (has_value()) return std::move(**this); return std::forward<U>(v);
simd::vec::operator[]Section: 29.10.7.3 [simd.subscr], 29.10.9.3 [simd.mask.subscr] Status: LEWG Submitter: Hewill Kang Opened: 2025-10-07 Last modified: 2025-10-22
Priority: 3
View all issues with LEWG status.
Discussion:
simd::vec::operator[] satisfies the criteria in P3471R4 and P3697R1 :
"violating the precondition results in a memory safety issue", which means that hardening is reasonable.
[2025-10-22; Reflector poll. Status changed: New → LEWG and P3.]
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.7.3 [simd.subscr] as indicated:
constexpr value_type operator[](simd-size-type i) const;-1- Hardened
Ppreconditions:i >= 0 && i < size()istrue.
Modify 29.10.9.3 [simd.mask.subscr] as indicated:
constexpr value_type operator[](simd-size-type i) const;-1- Hardened
Ppreconditions:i >= 0 && i < size()istrue.
ranges::size(r) Constraints and Mandates in [simd]Section: 29.10 [simd] Status: New Submitter: Lénárd Szolnoki Opened: 2025-10-10 Last modified: 2025-10-22
Priority: 3
View other active issues in [simd].
View all other issues in [simd].
View all issues with New status.
Discussion:
Various Constraints and Mandates in 29.10 [simd] are conditioned on ranges::size(r),
assuming that subsequent operations on the result are also constant expression. This is not a given,
since ranges::size(r) is allowed to return an integer-class type, and it is not mandated that operations
on integer-class types are usable in constant expressions.
29.10.7.2 [simd.ctor] p13:
Constraints:
Rmodelsranges::contiguous_rangeandranges::sized_range,
ranges::size(r)is a constant expression, and
ranges::size(r)is equal tosize().
Here ranges::size(r) == size() might not be a constant expression, even when ranges::size(r)
is. Operations that are not accounted for:
Possible conversion from range::size(r) to simd-size-type.
Possible operator overloading on ==.
29.10.7.2 [simd.ctor] p17+18:
Constraints:
Rmodelsranges::contiguous_rangeandranges::sized_range, and
ranges::size(r)is a constant expressionRemarks: The deduced type is equivalent to
vec<ranges::range_value_t<R>, ranges::size(r)>.
Here the second constraint is simply redundant, if failure to form the type is in the immediate context.
If we want to type it out in the constraints then we should account for conversion to simd-size-type
and narrowing.
29.10.8.7 [simd.loadstore] p2:
Mandates: If
ranges::size(r)is a constant expression thenranges::size(r)≥V::size().
Here ranges::size(r) >= V::size() might not be a constant expression, even when
ranges::size(r) is. It is unclear why a mathematical operator symbol is used here (and further
down in the preconditions).
[2025-10-22; Reflector poll.]
Set priority to P3 after reflector poll.
General preference on considering this NAD, and instead clarifying that operations on integer-class should produce constant expressions.
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.7.2 [simd.ctor] as indicated:
template<class R, class... Flags> constexpr basic_vec(R&& r, flags<Flags...> = {}); template<class R, class... Flags> constexpr basic_vec(R&& r, const mask_type& mask, flags<Flags...> = {});-12- Let
-13- Constraints:maskbemask_type(true)for the overload with nomaskparameter.[…]
(13.1) —
Rmodelsranges::contiguous_rangeandranges::sized_range, and(13.2) —
ranges::size(r) == size()is a constant expression, and evaluates totrue.
(13.3) —ranges::size(r)is equal tosize().template<class R, class... Ts> basic_vec(R&& r, Ts...) -> see below;-17- Constraints:
(17.1) —Rmodelsranges::contiguous_rangeandranges::sized_range, and.
(17.2) —ranges::size(r)is a constant expression.-18- Remarks: The deduced type is equivalent to
vec<ranges::range_value_t<R>, ranges::size(r)>.
Modify 29.10.8.7 [simd.loadstore] as indicated:
template<class V = see below, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R> constexpr V unchecked_load(R&& r, flags<Flags...> f = {}); […] template<class V = see below, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags> constexpr V unchecked_load(I first, S last, const typename V::mask_type& mask, flags<Flags...> f = {});-1- Let […]
-2- Mandates: Ifranges::size(r) >= V::size()is a constant expression thenit evaluates toranges::size(r)≥V::size()true.
basic_string_view(It begin, End end) is underconstrainedSection: 27.3.3.2 [string.view.cons] Status: New Submitter: Hewill Kang Opened: 2025-10-12 Last modified: 2025-10-17
Priority: 4
View other active issues in [string.view.cons].
View all other issues in [string.view.cons].
View all issues with New status.
Discussion:
The following lead to a hard error in the string_view constructor
(demo):
volatile char* p;
std::string_view sv(p, p); // error: invalid conversion from 'volatile char*' to 'const char*'
Because the constructor currently only requires that the value type of the contiguous iterator be
char, which does not reject volatile char*, which fires when further initializing const char*.
[2025-10-17; Reflector poll.]
Set priority to 4 after reflector poll.
"NAD, don't care about volatile."
"Maybe make it Mandates: instead of Constraints:."
"We already reject span<const char> s(p,p)
so should also reject string_view sv(p,p)."
Proposed resolution:
This wording is relative to N5014.
Modify 27.3.3.2 [string.view.cons] as indicated:
template<class It, class End> constexpr basic_string_view(It begin, End end);-7- Constraints:
[…]
(7.1) —
Itsatisfiescontiguous_iterator.(7.2) —
Endsatisfiessized_sentinel_for<It>.(7.3) —
is_same_v<iter_value_t<It>, charT>istrue.(7.?) —
is_convertible_v<add_pointer_t<iter_reference_t<It>>, const_pointer>istrue.(7.4) —
is_convertible_v<End, size_type>isfalse.template<class R> constexpr explicit basic_string_view(R&& r);-11- Let
-12- Constraints:dbe an lvalue of typeremove_cvref_t<R>.
(12.1) —
remove_cvref_t<R>is not the same type asbasic_string_view,(12.2) —
Rmodelsranges::contiguous_rangeandranges::sized_range,(12.3) —
is_same_v<ranges::range_value_t<R>, charT>istrue,(12.?) —
is_convertible_v<add_pointer_t<ranges::range_reference_t<R>>, const_pointer>istrue,(12.4) —
is_convertible_v<R, const charT*>isfalse, and(12.5) —
d.operator ::std::basic_string_view<charT, traits>()is not a valid expression.
Section: 17.3.2 [version.syn] Status: New Submitter: Jonathan Wakely Opened: 2025-10-14 Last modified: 2025-10-22
Priority: 2
View other active issues in [version.syn].
View all other issues in [version.syn].
View all issues with New status.
Discussion:
During the review of LWG 4286(i) it was noticed that the
proposed resolution did not include a change to __cpp_lib_ranges_indices,
which was added in Sofia by P3060R3
(which was after the issue was opened).
It also doesn't change __cpp_lib_constexpr_utility, and probably should do.
It doesn't change __cpp_lib_tuple_like,
but that's less clear whether it should do.
The relevant changes in
<tuple> and <pair>
are obviously available in a freestanding implementation,
but the changes in
<map> and <unordered_map>
are not.
[2025-10-22; Reflector poll.]
Set priority to 2 after reflector poll.
Proposed resolution:
rebind and resize reference a type member that doesn't existSection: 29.10.4 [simd.traits] Status: New Submitter: Matthias Kretz Opened: 2025-10-15 Last modified: 2025-10-20
Priority: Not Prioritized
View other active issues in [simd.traits].
View all other issues in [simd.traits].
View all issues with New status.
Discussion:
In 29.10.4 [simd.traits] rebind and resize say "deduce-abi-t<T, V::size()>
has a member type type". But deduce-abi-t is specified in
29.10.2.2 [simd.expos.abi] p4-5 as an alias that is either defined or not.
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.4 [simd.traits] as indicated:
template<class T, class V> struct rebind { using type = see below; };-4- The member
typeis present if and only if[…]
(4.1) —
Vis a data-parallel type,(4.2) —
Tis a vectorizable type, and(4.3) —
deduce-abi-t<T, V::size()>has a member typeis defined.typetemplate<simd-size-type N, class V> struct resize { using type = see below; };-7- Let
-8- The memberTdenote […]typeis present if and only if
(8.1) —
Vis a data-parallel type, and(8.2) —
deduce-abi-t<T, N>has a member typeis defined.type
views::indices is underconstrainedSection: 25.6.4.1 [range.iota.overview] Status: New Submitter: Hewill Kang Opened: 2025-10-15 Last modified: 2025-10-19
Priority: Not Prioritized
View all other issues in [range.iota.overview].
View all issues with New status.
Discussion:
Whether an integer-class type satisfies weakly_incrementable is unspecified according to
25.6.4.2 [range.iota.view]. For example, the library may provide a member
type alias difference_type for the integer-class type to make it weakly_incrementable, or not.
views::iota(integer-class-type(0)) is always ill-formed because
iota_view<W, Bound> requires W to be weakly_incrementable.
However, unlike views::iota, views::indices unconditionally accepts integer-class types,
which will lead to a hard error in the function body if the integer-class type is not
weakly_incrementable, which is true for both libstdc++ and MSVC-STL.
Proposed resolution:
This wording is relative to N5014.
Modify 25.6.4.1 [range.iota.overview] as indicated:
-4- The name
views::indicesdenotes a customization point object (16.3.3.3.5 [customization.point.object]). Given subexpressionE, letTberemove_cvref_t<decltype((E))>.views::indices(E)is expression-equivalent toviews::iota(T(0), E)ifis-integer-like<T>istrueandTmodelsweakly_incrementable, and ill-formed otherwise.
co_yield elements_of(vector<int>()) does not compileSection: 25.8.5 [coro.generator.promise] Status: New Submitter: Mathias Stearn Opened: 2025-10-16 Last modified: 2025-10-23
Priority: 2
View all other issues in [coro.generator.promise].
View all issues with New status.
Discussion:
The std::generator proposal (P2502) explicitly says that the following example is supposed to work, but it does not compile:
For convenience, we further propose that
co_yield elements_of(x)be extended to support yielding the values of arbitrary ranges beyond generators, iestd::generator<int> f() { std::vector<int> v = /*... */; co_yield std::ranges::elements_of(v); }
This doesn't compile because the overload listed in 25.8.5 [coro.generator.promise] p13
requires convertible_to<ranges::range_reference_t<R>, yielded> (i.e.
convertible_to<int&, int&&>) which isn't satisfied.
co_yield elements_of(rng) should be
semantically similar to the following code, but require a specific optimization in the
case where rng is the same type of generator as the current coroutine:
for (auto&& x : rng) {
co_yield std::forward<decltype(x)>(x);
}
Note that that code does compile correctly where rng is a vector<int>, so
I think the original code should as well.
generator<T>
in P2502R0 was basically the same as generator<const T&> where
co_yield elements_of(vector<int>()) does work. After P2529 (which was a
design paper w/o wording) was approved by LEWG, P2502R1 was updated to change the
reference type to T&&. However, when making the change, I assume that the impact
it had on the elements_of-non-generator overload of yield_value was not considered, so it was
unmodified.
[2025-10-23; Reflector poll.]
Set priority to 2 after reflector poll.
The current resolution is incorrect, and breaks following example:
std::generator<std::vector<int>> g()
{
std::vector<int> v = {1, 2, 3};
co_yield std::ranges::elements_of(v);
}
The constraints are equivalent to checking if yield_value(*it) is well-formed,
and maybe we could express is directly as:
requires requires (promise_type p, ranges::iterator_t<R> it) {
p.yield_value(*it);
}
Proposed resolution:
This wording is relative to N5014.
Modify 25.8.5 [coro.generator.promise] as indicated:
[Drafting note: The following changes would check that
co_yield *iwould match one of the overloads ofyield_value()described in paragraphs 4 or 6. Alternatively this could be expressed more directly as a helper comment or in prose in a Mandates: element.
[…]namespace std { template<class Ref, class Val, class Allocator> class generator<Ref, Val, Allocator>::promise_type { public: […] template<ranges::input_range R, class Alloc> requires convertible_to<ranges::range_reference_t<R>, yielded> || (is_rvalue_reference_v<yielded> && constructible_from<remove_cvref_t<yielded>, const remove_reference_t<yielded>&>) auto yield_value(ranges::elements_of<R, Alloc> r); […] }; }template<ranges::input_range R, class Alloc> requires convertible_to<ranges::range_reference_t<R>, yielded> || (is_rvalue_reference_v<yielded> && constructible_from<remove_cvref_t<yielded>, const remove_reference_t<yielded>&>) auto yield_value(ranges::elements_of<R, Alloc> r);-13- Effects: Equivalent to:
auto nested = [](allocator_arg_t, Alloc, ranges::iterator_t<R> i, ranges::sentinel_t<R> s) -> generator<yielded, void, Alloc> { for (; i != s; ++i) { co_yieldstatic_cast<yielded>(*i); } }; return yield_value(ranges::elements_of(nested( allocator_arg, r.allocator, ranges::begin(r.range), ranges::end(r.range))));
let_value/error/stopped should specify the attributes of their sendersSection: 33.9.12.10 [exec.let] Status: New Submitter: Eric Niebler Opened: 2025-10-16 Last modified: 2025-10-23
Priority: 1
View all other issues in [exec.let].
View all issues with New status.
Discussion:
Imported from https://github.com/cplusplus/sender-receiver/issues/346.
let_value(sndr, fn) returns make-sender(let-tag, fn, sndr),
which means that the let_value sender has one child. 33.9.12.1 [exec.adapt.general] bullet (3.2) says:
A parent sender with a single child sender
sndrhas an associated attribute object equal toFWD-ENV(get_env(sndr)).
However, let_value(sndr, fn) does not know where it will be started or where it will complete in the general case.
The function fn could return schedule(some-scheduler), making the let_value operation complete
on some-scheduler. It would be wrong for the let_value(sndr, fn) sender to have sndr's completion
schedulers or domains.
let_value/error/stopped must define impls-for<let_xxx_t>::get-attrs to return
FWD-ENV(get_env(sndr)) except that it handles the get_completion_scheduler_t<Tag>
(for any completion tag Tag) and get_domain_t queries specially.
[2025-10-23; Reflector poll.]
Set priority to 1 after reflector poll.
"Looks like a special case of the problem that P3826 addresses."
Proposed resolution:
<stdfloat> typesSection: 29.10 [simd] Status: New Submitter: Matthias Kretz Opened: 2025-10-15 Last modified: 2025-10-23
Priority: 1
View other active issues in [simd].
View all other issues in [simd].
View all issues with New status.
Discussion:
Addresses DE-288 and DE-285
29.10.8.7 [simd.loadstore]unchecked_store and partial_store are constrained with
indirectly_writable in such a way that basic_vec's value_type must satisfy
convertible_to<range value-type>. But that is not the case,
e.g. for float → float16_t or double → float32_t. However,
if simd::flag_convert is passed, these conversions were intended to work. The
implementation thus must static_cast the basic_vec values to the range's value-type
before storing to the range.
unchecked_store(vec<float>, span<complex<float16_t>>, flag_convert)
does not work for a different reason. The complex(const float16_t&, const float16_t&)
constructor simply does not allow construction from float, irrespective of
using implicit or explicit conversion. The only valid conversion from float →
complex<float16_t> is via an extra step through complex<float16_t>::value_type.
This issue considers it a defect of complex that an explicit conversion from
float → complex<float16_t> is ill-formed and therefore no workaround/special
case is introduced.
Conversely, the conversion constructor in 29.10.7.2 [simd.ctor] does not reject
conversion from vec<complex<float>, 4> to vec<float, 4>.
I.e. convertible_to<vec<complex<float>, 4>, vec<float, 4>>
is true, which is a lie. This is NB comment DE-288. However, the NB comment's proposed
resolution is too strict, in that it would disallow conversion from float to float16_t.
The conversion/load from static-sized range constructor in 29.10.7.2 [simd.ctor] has a
similar problem:
convertible_to<array<std::string, 4>, vec<int, 4>>istrue
but when fixing this
vec<float16_t, 4>(array<float, 4>, flag_convert)
must continue to be valid.
unchecked_load and partial_load in 29.10.8.7 [simd.loadstore] currently Mandate
the range's value-type to be vectorizable, but converting loads from complex<float>
to float are not covered. It is unclear what a conversion from complex<float>
to float should do, so it needs to be added (again without breaking float → float16_t).
29.10.8.11 [simd.permute.memory] is analogous to 29.10.8.7 [simd.loadstore] and needs
equivalent constraints.
29.10.7.2 [simd.ctor] p2 requires constructible_from<U>, which makes explicit
construction of vec<float16_t> from float ill-formed. For consistency this
should also be constrained with explicitly-convertible-to.
[2025-10-22; Reflector poll.]
Set priority to 1 after reflector poll.
We also need to update Effects. There are more places in 29.10 [simd]
where float to float16_t and similar conversion are not supported.
It was pointed out that similar issues happen for complex<float16_t>.
There seem to be mismatch between language initialization rules and the intended
usage based on library API.
Previous resolution [SUPERSEDED]:
This wording is relative to N5014.
In 29.10.3 [simd.syn] and 29.10.8.7 [simd.loadstore] replace all occurrences of
indirectly_writable<ranges::iterator_t<R>, T>with
indirectly_writable<ranges::iterator_t<R>,Tranges::range_value_t<R>>and all occurrences of
indirectly_writable<I, T>with
indirectly_writable<I,Titer_value_t<I>>Modify 29.10.8.7 [simd.loadstore] as indicated:
template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R> && indirectly_writable<ranges::iterator_t<R>, T> constexpr void unchecked_store(const basic_vec<T, Abi>& v, R&& r, flags<Flags...> f = {}); […] template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags> requires indirectly_writable<I, T> constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, S last, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {});-11- Let […]
-?- Constraints: The expressionstatic_cast<ranges::range_value_t<R>>(x)wherexis an object of typeTis well-formed. -12- Mandates: Ifranges::size(r)is a constant expression thenranges::size(r) ≥ simd-size-v<T, Abi>. […]template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R> && indirectly_writable<ranges::iterator_t<R>, T> constexpr void partial_store(const basic_vec<T, Abi>& v, R&& r, flags<Flags...> f = {}); […] template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags> requires indirectly_writable<I, T> constexpr void partial_store(const basic_vec<T, Abi>& v, I first, S last, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {});-15- Let […]
-?- Constraints: The expressionstatic_cast<iter_value_t<I>>(x)wherexis an object of typeTis well-formed. -16- Mandates: […]
[2025-10-22; Matthias Kretz improves discussion and provides new wording]
Proposed resolution:
This wording is relative to N5014.
Modify 29.10.2 [simd.expos] as indicated:
[…]
template<class T>
concept constexpr-wrapper-like = // exposition only
[…]
bool_constant<static_cast<decltype(T::value)>(T()) == T::value>::value;
template<class From, class To>
concept explicitly-convertible-to = // exposition-only
requires {
static_cast<To>(declval<From>());
};
template<class T> using deduced-vec-t = see below; // exposition only
[…]
Modify 29.10.3 [simd.syn] as indicated:
[…] template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R>&& indirectly_writable<ranges::iterator_t<R>, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, R&& r, flags<Flags...> f = {}); template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R>&& indirectly_writable<ranges::iterator_t<R>, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, R&& r, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, class... Flags>requires indirectly_writable<I, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, iter_difference_t<I> n, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, class... Flags>requires indirectly_writable<I, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, iter_difference_t<I> n, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags>requires indirectly_writable<I, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, S last, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags>requires indirectly_writable<I, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, S last, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R>&& indirectly_writable<ranges::iterator_t<R>, T>constexpr void partial_store(const basic_vec<T, Abi>& v, R&& r, flags<Flags...> f = {}); template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R>&& indirectly_writable<ranges::iterator_t<R>, T>constexpr void partial_store(const basic_vec<T, Abi>& v, R&& r, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, class... Flags>requires indirectly_writable<I, T>constexpr void partial_store( const basic_vec<T, Abi>& v, I first, iter_difference_t<I> n, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, class... Flags>requires indirectly_writable<I, T>constexpr void partial_store( const basic_vec<T, Abi>& v, I first, iter_difference_t<I> n, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags>requires indirectly_writable<I, T>constexpr void partial_store(const basic_vec<T, Abi>& v, I first, S last, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags>requires indirectly_writable<I, T>constexpr void partial_store(const basic_vec<T, Abi>& v, I first, S last, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); […]
Modify 29.10.7.2 [simd.ctor] as indicated:
template<class U> constexpr explicit(see below) basic_vec(U&& value) noexcept;-1- Let
-2- Constraints:Fromdenote the typeremove_cvref_t<U>.satisfiesvalue_typeU. […]constructible_from<U>explicitly-convertible-to<value_type>template<class U, class UAbi> constexpr explicit(see below) basic_vec(const basic_vec<U, UAbi>& x) noexcept;-5- Constraints:
[…]
(5.1) —
simd-size-v<U, UAbi> == size()istrue, and(5.2) —
Usatisfiesexplicitly-convertible-to<T>.template<class R, class... Flags> constexpr basic_vec(R&& r, flags<Flags...> = {}); template<class R, class... Flags> constexpr basic_vec(R&& r, const mask_type& mask, flags<Flags...> = {});-12- Let
-13- Constraints:maskbemask_type(true)for the overload with nomaskparameter.
(13.1) —
Rmodelsranges::contiguous_rangeandranges::sized_range,(13.2) —
ranges::size(r)is a constant expression,and(13.3) —
ranges::size(r)is equal tosize(), and(13.?) —
ranges::range_value_t<R>is a vectorizable type and satisfiesexplicitly-convertible-to<T>.-14- Mandates:
(14.1) —ranges::range_value_t<R>is a vectorizable type, and
(14.2) — ifIf the template parameter packFlagsdoes not containconvert-flag, then the conversion fromranges::range_value_t<R>tovalue_typeis value-preserving.
Modify 29.10.8.7 [simd.loadstore] as indicated:
template<class V = see below , ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R> constexpr V partial_load(R&& r, flags<Flags...> f = {}); template<class V = see below , ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R> constexpr V partial_load(R&& r, const typename V::mask_type& mask, flags<Flags...> f = {}); template<class V = see below , contiguous_iterator I, class... Flags> constexpr V partial_load(I first, iter_difference_t<I> n, flags<Flags...> f = {}); template<class V = see below , contiguous_iterator I, class... Flags> constexpr V partial_load(I first, iter_difference_t<I> n, const typename V::mask_type& mask, flags<Flags...> f = {}); template<class V = see below , contiguous_iterator I, sized_sentinel_for<I> S, class... Flags> constexpr V partial_load(I first, S last, flags<Flags...> f = {}); template<class V = see below , contiguous_iterator I, sized_sentinel_for<I> S, class... Flags> constexpr V partial_load(I first, S last, const typename V::mask_type& mask, flags<Flags...> f = {});-6- Let […]
[…] -7- Mandates:
(7.1) —
ranges::range_value_t<R>is a vectorizable type and satisfiesexplicitly-convertible-to<T>,(7.2) —
same_as<remove_cvref_t<V>, V>istrue,(7.3) —
Vis an enabled specialization ofbasic_vec, and(7.4) — if the template parameter pack
Flagsdoes not containconvert-flag, then the conversion fromranges::range_value_t<R>toV::value_typeis value-preserving.template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R>&& indirectly_writable<ranges::iterator_t<R>, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, R&& r, flags<Flags...> f = {}); template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R>&& indirectly_writable<ranges::iterator_t<R>, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, R&& r, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, class... Flags>requires indirectly_writable<I, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, iter_difference_t<I> n, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, class... Flags>requires indirectly_writable<I, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, iter_difference_t<I> n, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags>requires indirectly_writable<I, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, S last, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags>requires indirectly_writable<I, T>constexpr void unchecked_store(const basic_vec<T, Abi>& v, I first, S last, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {});-11- Let […]
[…]template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R>&& indirectly_writable<ranges::iterator_t<R>, T>constexpr void partial_store(const basic_vec<T, Abi>& v, R&& r, flags<Flags...> f = {}); template<class T, class Abi, ranges::contiguous_range R, class... Flags> requires ranges::sized_range<R>&& indirectly_writable<ranges::iterator_t<R>, T>constexpr void partial_store(const basic_vec<T, Abi>& v, R&& r, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, class... Flags>requires indirectly_writable<I, T>constexpr void partial_store( const basic_vec<T, Abi>& v, I first, iter_difference_t<I> n, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, class... Flags>requires indirectly_writable<I, T>constexpr void partial_store( const basic_vec<T, Abi>& v, I first, iter_difference_t<I> n, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags>requires indirectly_writable<I, T>constexpr void partial_store(const basic_vec<T, Abi>& v, I first, S last, flags<Flags...> f = {}); template<class T, class Abi, contiguous_iterator I, sized_sentinel_for<I> S, class... Flags>requires indirectly_writable<I, T>constexpr void partial_store(const basic_vec<T, Abi>& v, I first, S last, const typename basic_vec<T, Abi>::mask_type& mask, flags<Flags...> f = {});-15- Let […]
-?- Constraints:
(?.1) —
ranges::iterator_t<R>satisfiesindirectly_writable<ranges::range_value_t<R>>, and(?.2) —
Tsatisfiesexplicitly-convertible-to<ranges::range_value_t<R>>-16- Mandates: […]
-17- Preconditions: […] -18- Effects: For alliin the range of[0, basic_vec<T, Abi>::size()), ifmask[i] && i < ranges::size(r)istrue, evaluatesranges::data(r)[i] = static_cast<ranges::range_value_t<R>>(v[i]).
Modify 29.10.8.11 [simd.permute.memory] as indicated:
template<class V = see below, ranges::contiguous_range R, simd-integral I, class... Flags> requires ranges::sized_range<R> constexpr V partial_gather_from(R&& in, const I& indices, flags<Flags...> f = {}); template<class V = see below, ranges::contiguous_range R, simd-integral I, class... Flags> requires ranges::sized_range<R> constexpr V partial_gather_from(R&& in, const typename I::mask_type& mask, const I& indices, flags<Flags...> f = {});-5- Let: […]
-?- Constraints:ranges::range_value_t<R>is a vectorizable type and satisfiesexplicitly-convertible-to<T>.-6- Mandates: […]
[…]template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags> requires ranges::sized_range<R> constexpr void partial_scatter_to(const V& v, R&& out, const I& indices, flags<Flags...> f = {}); template<simd-vec-type V, ranges::contiguous_range R, simd-integral I, class... Flags> requires ranges::sized_range<R> constexpr void partial_scatter_to(const V& v, R&& out, const typename I::mask_type& mask, const I& indices, flags<Flags...> f = {});-13- Let
-14- Constraints:maskbeI::mask_type(true)for the overload with nomaskparameter.
(14.1) —
V::size() == I::size()istrue,(14.2) —
ranges::iterator_t<R>satisfiesindirectly_writable<ranges::range_value_t<R>>, and(14.3) —
typename V::value_typesatisfiesexplicitly-convertible-to<ranges::range_value_t<R>>.
to_chars for integersSection: 28.2.2 [charconv.to.chars] Status: New Submitter: Jan Schultke Opened: 2025-10-17 Last modified: 2025-10-23
Priority: 3
View all issues with New status.
Discussion:
The integer overload of to_chars is specified "manually" rather than in terms of printf,
and it is not clearly stated what encoding the output characters have. Given the existing
use of the character literal '-' in the Effects element, the intent is presumably
to output in the ordinary literal encoding.
[2025-10-23; Reflector poll.]
Set priority to 3 after reflector poll.
Concerns with meaning of 'a'..'z' for EBCDIC.
Suggestion to use U+0061..U+007A like in [tab:lex.charset.basic],
as alternative.
Proposed resolution:
This wording is relative to N5014.
Modify 28.2.2 [charconv.to.chars] as indicated:
constexpr to_chars_result to_chars(char* first, char* last, integer-type value, int base = 10);-4- Preconditions:
-5- Effects: The value ofbasehas a value between 2 and 36 (inclusive).valueis converted to a string of digits in the given base (with no redundant leading zeroes) in the ordinary literal encoding (5.3.1 [lex.charset]). Digits in the range 10..35 (inclusive) are represented as lowercase characters'a'..'z'. Ifvalueis less than zero, the representation starts with'-'.
meta::access_context should be a consteval-only typeSection: 21.4.8 [meta.reflection.access.context] Status: New Submitter: Jakub Jelinek Opened: 2025-10-20 Last modified: 2025-10-23
Priority: 2
View all issues with New status.
Discussion:
The meta::access_context type is expected to contain some meta::info
objects, which would make it a consteval-only type. But we don't actually
specify any members, so nothing in the current specification says you can't
persist one until runtime.
[2025-10-23; Reflector poll. Adjust proposed wording.]
Set priority to 2 after reflector poll.
Reflector discussion requested that 'non-aggregate' and 'consteval-only' both be put in paragraph 3, adjacent to 'structural'. Also added a drive-by editorial change to paragraph 1.
Proposed resolution:
This wording is relative to N5014.
Modify 21.4.8 [meta.reflection.access.context] as indicated:
-1- The class
access_contextclass is a non-aggregate type thatrepresents a namespace, class, or function from which queries pertaining to access rules may be performed, as well as the designating class (11.8.3 [class.access.base]), if any.-2- An
access_contexthas an associated scope and designating class.namespace std::meta { struct access_context { access_context() = delete; consteval info scope() const; consteval info designating_class() const; static consteval access_context current() noexcept; static consteval access_context unprivileged() noexcept; static consteval access_context unchecked() noexcept; consteval access_context via(info cls) const; }; }-3-
access_contextis a structural, consteval-only, non-aggregate type. Two valuesac1andac2of typeaccess_contextare template-argument-equivalent (13.6 [temp.type]) ifac1.scope()andac2.scope()are template-argument-equivalent andac1.designating_class()andac2.designating_class()are template-argument-equivalent.
meta::data_member_spec allows negative bit-field widthsSection: 21.4.16 [meta.reflection.define.aggregate] Status: New Submitter: Jakub Jelinek Opened: 2025-10-20 Last modified: 2025-10-20
Priority: Not Prioritized
View other active issues in [meta.reflection.define.aggregate].
View all other issues in [meta.reflection.define.aggregate].
View all issues with New status.
Discussion:
The meta::data_member_spec function doesn't restrict options.bit_width
to be non-negative.
Proposed resolution:
This wording is relative to N5014.
Modify 21.4.16 [meta.reflection.define.aggregate] as indicated:
- (5.3) — if
options.namedoes not contain a value, thenoptions.bit_widthcontains a value;- (5.4) — if
options.bit_widthcontains a value V, then
- (5.4.1) —
is_integral_type(type) || is_enum_type(type)istrue,- (5.4.2) —
options.alignmentdoes not contain a value,- (5.4.3) —
options.no_unique_addressisfalse,and- (5.4.?) — V is not negative, and
- (5.4.4) — if V equals
0, thenoptions.namedoes not contain a value; and- (5.5) — if
options.alignmentcontains a value, it is an alignment value (6.8.3 [basic.align]) not less thanalignment_of(type).
meta::define_aggregate should require a class typeSection: 21.4.16 [meta.reflection.define.aggregate] Status: New Submitter: Jakub Jelinek Opened: 2025-10-20 Last modified: 2025-10-20
Priority: Not Prioritized
View other active issues in [meta.reflection.define.aggregate].
View all other issues in [meta.reflection.define.aggregate].
View all issues with New status.
Discussion:
The meta::define_aggregate function doesn't say what happens if C
does not represent a class type.
It's also unclear whether it should work with aliases to class types, e.g.
struct S; using A = S; ... meta::define_aggregate(^^A, {});
And what happens if you try to define a cv-qualified type:
struct S; meta::define_aggregate(^^const S, {});
Should this be an error, or inject a definition of the unqualified type?
Proposed resolution:
This wording is relative to N5014.
Modify 21.4.16 [meta.reflection.define.aggregate] as indicated:
-7- Let C be the class represented by
dealias(class_type)and rK be the Kth reflection value inmdescrs. For every rK inmdescrs, let (TK, NK, AK, WK, NUAK) be the corresponding data member description represented by rK.-8- Constant when:
- (8.?) —
dealias(class_type)represents a class type;- (8.1) — C is incomplete from every point in the evaluation context;
meta::reflect_constant_string considers a string literalSection: 21.4.15 [meta.reflection.array] Status: New Submitter: Jakub Jelinek Opened: 2025-10-21 Last modified: 2025-10-21
Priority: Not Prioritized
View all issues with New status.
Discussion:
meta::reflect_constant_string says:
Let V be the pack of values of typeIt's unclear how the implementation should decide whetherCharTwhose elements are the corresponding elements ofr, except that ifrrefers to a string literal object, then V does not include the trailing null terminator ofr.
r refers to
a string literal. If R models contiguous_iterator, should it use
meta::is_string_literal(ranges::data(r))?
Should it omit the '\0' from string_view("abc", 3)?
Also, "null terminator" is only defined in 27.4.3.1 [basic.string.general] and not used for string literal objects (5.13.5 [lex.string]).
Proposed resolution:
This wording is relative to N5014.
Modify 21.4.15 [meta.reflection.array] as indicated:
template<ranges::input_range R> consteval info reflect_constant_string(R&& r);-2- Let
CharTberanges::range_value_t<R>.-3- Mandates:
CharTis one ofchar,wchar_t,char8_t,char16_t,char32_t.Let V be the pack of values of type
CharTwhose elements are the corresponding elements ofr, except that ifrrefersis a reference to a string literal object, then V does not include thetrailing null terminatorterminating u+0000 null character ofr.