| Doc. no. | D???? |
| Date: | 2026-08-15 |
| Project: | Programming Language C++ |
| Reply to: | Jonathan Wakely <lwgchair@gmail.com> |
Revised 2026-08-15 at 14:46:44 UTC
Reference ISO/IEC IS 14882:2024(E)
Also see:
The purpose of this document is to record the status of issues which have come before the Library Working Group (LWG) of the INCITS PL22.16 and ISO WG21 C++ Standards Committee. Issues represent potential defects in the ISO/IEC IS 14882:2024(E) document.
This document contains only library issues which are actively being considered by the Library Working Group, i.e., issues which have a status of New, Open, Ready, or Review. See Library Defect Reports and Accepted Issues for issues considered defects and Library Closed Issues List for issues considered closed.
The issues in these lists are not necessarily formal ISO Defect Reports (DR's). While some issues will eventually be elevated to official Defect Report status, other issues will be disposed of in other ways. See Issue Status.
Prior to Revision 14, library issues lists existed in two slightly different versions; a Committee Version and a Public Version. Beginning with Revision 14 the two versions were combined into a single version.
This document includes [bracketed italicized notes] as a reminder to the LWG of current progress on issues. Such notes are strictly unofficial and should be read with caution as they may be incomplete or incorrect. Be aware that LWG support for a particular resolution can quickly change if new viewpoints or killer examples are presented in subsequent discussions.
For the most current official version of this document see http://www.open-std.org/jtc1/sc22/wg21/.
Public information as to how to obtain a copy of the C++ Standard, join the standards committee, submit an issue, or comment on an issue can be found at isocpp.org.
Issues reported to the LWG transition through a variety of statuses, indicating their progress towards a resolution. Typically, most issues will flow through the following stages.
New - The issue has not yet been reviewed by the LWG. Any Proposed Resolution is purely a suggestion from the issue submitter, and should not be construed as the view of LWG.
Open - The LWG has discussed the issue but is not yet ready to move the issue forward. There are several possible reasons for open status:
A Proposed Resolution for an open issue is still not be construed as the view of LWG. Comments on the current state of discussions are often given at the end of open issues in an italic font. Such comments are for information only and should not be given undue importance.
Review - Exact wording of a Proposed Resolution is now available for review on an issue for which the LWG previously reached informal consensus.
Ready - The LWG has reached consensus that the issue is a defect in the Standard, the Proposed Resolution is correct, and the issue is ready to forward to the full committee for further action as a Defect Report (DR).
Typically, an issue must have a proposed resolution in the currently published issues list, whose wording does not change during LWG review, to move to the Ready status.
Voting - This status should not be seen in a published issues list, but is a marker for use during meetings to indicate an issues was Ready in the pre-meeting mailing, the Proposed Resolution is correct, and the issue will be offered to the working group at the end of the current meeting to apply to the current working paper (WP) or to close in some other appropriate manner. This easily distinguishes such issues from those moving to Ready status during the meeting itself, that should not be forwarded until the next meeting. If the issue does not move forward, it should fall back to one of the other open states before the next list is published.
Immediate - This status should not be seen in a published issues list, but is a marker for use during meetings to indicate an issues was not Ready in the pre-meeting mailing, but the Proposed Resolution is correct, and the issue will be offered to the working group at the end of the current meeting to apply to the current working paper (WP) or to close in some other appropriate manner. This status is used only rarely, typically for fixes that are both small and obvious, and usually within a meeting of the expected publication of a revised standard. If the issue does not move forward, it should fall back to one of the other open states before the next list is published.
In addition, there are a few ways to categorise and issue that remains open to a resolution within the library, but is not actively being worked on.
Deferred - The LWG has discussed the issue, is not yet ready to move the issue forward, but neither does it deem the issue significant enough to delay publishing a standard or Technical Report. A typical deferred issue would be seeking to clarify wording that might be technically correct, but easily mis-read.
A Proposed Resolution for a deferred issue is still not be construed as the view of LWG. Comments on the current state of discussions are often given at the end of open issues in an italic font. Such comments are for information only and should not be given undue importance.
Core - The LWG has discussed the issue, and feels that some key part of resolving the issue is better handled by a cleanup of the language in the Core part of the standard. The issue is passed to the Core Working Group, which should ideally open a corresponding issue that can be linked from the library issue. Such issues will be revisitted after Core have made (or declined to make) any changes.
EWG - The LWG has discussed the issue, and wonder that some key part of resolving the issue is better handled by some (hopefully small) extension to the language. The issue is passed to the Evolution Working Group, which should ideally open a corresponding issue that can be linked from the library issue. Such issues will be revisitted after Evoltion have made (or declined to make) any recommendations. Positive recommendations from EWG will often mean the issue transition to Core status while we wait for some proposed new feature to land in the working paper.
LEWG - The LWG has discussed the issue, and deemd the issue is either an extension, however small, or changes the library design in some fundamental way, and so has delegated the initial work to the Library Evolution Working Group.
Ultimately, all issues should reach closure with one of the following statuses.
DR - (Defect Report) - The full WG21/PL22.16 committee has voted to forward the issue to the Project Editor to be processed as a Potential Defect Report. The Project Editor reviews the issue, and then forwards it to the WG21 Convenor, who returns it to the full committee for final disposition. This issues list accords the status of DR to all these Defect Reports regardless of where they are in that process.
WP - (Working Paper) - The proposed resolution has not been accepted as a Technical Corrigendum, but the full WG21/PL22.16 committee has voted to apply the Defect Report's Proposed Resolution to the working paper.
C++23 - (C++ Standard, as revised for 2023) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2023 revision to the C++ standard, ISO/IEC IS 14882:2024(E).
C++20 - (C++ Standard, as revised for 2020) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2020 revision to the C++ standard, ISO/IEC IS 14882:2020(E).
C++17 - (C++ Standard, as revised for 2017) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2017 revision to the C++ standard, ISO/IEC IS 14882:2017(E).
C++14 - (C++ Standard, as revised for 2014) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2014 revision to the C++ standard, ISO/IEC IS 14882:2014(E).
C++11 - (C++ Standard, as revised for 2011) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the published 2011 revision to the C++ standard, ISO/IEC IS 14882:2011(E).
CD1 - (Committee Draft 2008) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the Fall 2008 Committee Draft.
TC1 - (Technical Corrigenda 1) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution as a Technical Corrigenda. Action on this issue is thus complete and no further action is possible under ISO rules.
TRDec - (Decimal TR defect) - The LWG has voted to accept the Defect Report's Proposed Resolution into the Decimal TR. Action on this issue is thus complete and no further action is expected.
TS - (TS - various) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into a published Technical Specification.
Resolved - The LWG has reached consensus that the issue is a defect in the Standard, but the resolution adopted to resolve the issue came via some other mechanism than this issue in the list - typically by applying a formal paper, occasionally as a side effect of consolidating several interacting issue resolutions into a single issue.
Dup - The LWG has reached consensus that the issue is a duplicate of another issue, and will not be further dealt with. A Rationale identifies the duplicated issue's issue number.
NAD - The LWG has reached consensus that the issue is not a defect in the Standard.
NAD Editorial - The LWG has reached consensus that the issue can either be handled editorially, or is handled by a paper (usually linked to in the rationale).
Tentatively - This is a status qualifier. The issue has been reviewed online, or at an unofficial meeting, but not in an official meeting, and some support has been formed for the qualified status. Tentatively qualified issues may be moved to the unqualified status and forwarded to full committee (if Ready) within the same meeting. Unlike Ready issues, Tentatively Ready issues will be reviewed in subcommittee prior to forwarding to full committee. When a status is qualified with Tentatively, the issue is still considered active.
Pending - This is a status qualifier. When prepended to a status this indicates the issue has been processed by the committee, and a decision has been made to move the issue to the associated unqualified status. However for logistical reasons the indicated outcome of the issue has not yet appeared in the latest working paper.
The following statuses have been retired, but may show up on older issues lists.
NAD Future - In addition to the regular status, the LWG believes that this issue should be revisited at the next revision of the standard. That is now an ongoing task managed by the Library Evolution Working Group, and most issues in this status were reopended with the status LEWG.
NAD Concepts - This status reflects an evolution of the language during the development of C++11, where a new feature entered the language, called concepts, that fundamentally changed the way templates would be specified and written. While this language feature was removed towards the end of the C++11 project, there is a clear intent to revisit this part of the language design. During that development, a number of issues were opened against the updated library related to use of that feature, or requesting fixes that would require explicit use of the concepts feature. All such issues have been closed with this status, and may be revisitted should this or a similar language feature return for a future standard.
NAD Arrays - This status reflects an evolution of the language during the development of C++14/17, where work on a Technical Specification, called the Arrays TS was begun. In early 2016, this work was abandoned, and the work item was officially withdrawn. During development of the TS, a number of issues were opened the features in the TS. All such issues have been closed with this status, and may be revisitted should this or a similar language feature return for a future standard.
Issues are always given the status of New when they first appear on the issues list. They may progress to Open or Review while the LWG is actively working on them. When the LWG has reached consensus on the disposition of an issue, the status will then change to Dup, NAD, or Ready as appropriate. Once the full PL22.16 committee votes to forward Ready issues to the Project Editor, they are given the status of Defect Report (DR). These in turn may become the basis for Technical Corrigenda (TC1), an updated standard (C++11, C++14), or are closed without action other than a Record of Response (Resolved) where the desired effect has already been achieved by some other process. The intent of this LWG process is that only issues which are truly defects in the Standard move to the formal ISO DR status.
streamsize in iostreamsSection: 31 [input.output] Status: Open Submitter: Martin Sebor Opened: 2003-09-18 Last modified: 2018-12-09
Priority: 3
View other active issues in [input.output].
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]
[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 to bool" 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:
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 your n 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
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.-?- Ensures:
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
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.-?- Ensures:
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
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.-?- Ensures:
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
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.-?- Ensures:
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
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.-?- Ensures:
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
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.-?- Ensures:
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
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.-?- Ensures:
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
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.-?- Ensures:
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 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.
However, in the short term, I'm still inclined to declare this NAD, for two separate reasons:
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.
An alternative resolution would be to simply delete our flakey attempt at preventing out-of-thin-air reads, by removing 32.5.4 [atomics.order] p9-11, possibly adding a note that explains that we technically allow, but strongly discourage them. If we were starting this from scratch now, that would probably be my preference. But it seems like too drastic a resolution at this stage.
[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: 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 iterator i, 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.
In regard to the second point: To make this point clearer, I suggest to
add a similar additional wording as we already have for input iterator to the
"Assertion/note" column of the expression ++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.5.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.
IMHO, if that is the intended requirement, it should be stated explicitly.
In any event, there is a tiny editorial cleanup that could be made:
In C++11, 29.6.8.1 [template.mask.array.overview] para 2 mentions
"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
mask_arrayassignment hence target the C++03 26.3.8 paragraph. Correct?[Plum Hall:]
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 simple
valarrayassignment 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
b".That is why I reported the test t263.dir/_26322Y31.cpp having an undefined behaviour.
[Plum Hall:]
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.5.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.
Daniel:
At the time of the specification this was intended and the solution is
not done by removing the destruction semantics of 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
str.setf(ios_base::showgrouping).-?- Returns:
str.ios_base& noshowgrouping(ios_base& str);-?- Effects: Calls
str.unsetf(ios_base::showgrouping).-?- Returns:
str.
Proposed resolution:
Section: 16.3.2 [structure] Status: Ready Submitter: Jens Maurer Opened: 2012-03-08 Last modified: 2026-06-09
Priority: 3
View all issues with Ready 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]
[Brno 2026-06-09; LWG issue processing]
23.6.8.7 [flat.map.modifiers] has a Postconditions: for extract
which says "*this is emptied , even if the function exits via an exception".
Similarly for flat_set::extract in 23.6.11.5 [flat.set.modifiers]
and the flat_multifoo::extract functions.
Christian suggests new wording with "unless otherwise specified" so that the flat adaptor cases are not invalidated.
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.
[Brno 2026-06-09; Christian provides new wording]
[Brno 2026-06-09; Change status Open → Ready]
Proposed resolution:
This wording is relative to N5046.
Change 16.3.2.4 [structure.specifications] as indicated:
(3.4) — Postconditions: the conditions (sometimes termed observable results) established by the function. Unless otherwise specified, postconditions only apply when the function returns normally.
Section: 28.6.7.3 [re.regex.assign] Status: Ready Submitter: Jonathan Wakely Opened: 2012-03-08 Last modified: 2026-06-09
Priority: 3
View all other issues in [re.regex.assign].
View all issues with Ready 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").
[Brno 2026-06-09; Change status Open → Ready.]
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 string
s, 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 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?
As far as I understand, we can explicitly specify template arguments for 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 of std::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.
Let T 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.
The overload of the non-member 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 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.
Conclusion: Update wording, revisit later.
[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-
arrayshall provide support for the special caseN == 0.-?- The unspecified internal structure of
arrayfor this case shall allow initializations like:array<T, 0> a = { };and said initializations must be valid even when
Tis not default-constructible.-2- In the case that
N == 0,begin() == end() ==unique value. The return value ofdata()is unspecified.-3- The effect of calling
front()orback()for a zero-sized array is undefined.-4- Member function
swap()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-
arrayshallprovides support for the special case of a zero-sizedarraythat is always empty, i.e.N == 0, with the properties described in this subclause.-?- A zero-sized
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 calling
front()orback()for a zero-sized array is undefined.-4- Member function
swap()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-
arrayshallprovides support for the special case of a zero-sizedarraythat is always empty, i.e.N == 0, with the properties described in this subclause.-?- A zero-sized
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 calling
front()orback()for a zero-sized array is undefined.-4- Member function
swap()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-
arrayshallprovides support for the special case of a zero-sizedarraythat is always empty, i.e.N == 0, with the properties described in this subclause.-?- A zero-sized
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 calling
front()orback()for a zero-sized array is undefined.-4- Member function
swap()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.
See also message c++std-lib-32908.
[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 on n. 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).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."
This issue is related to LWG 2199(i).
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 |
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].
This may just be an editorial issue because the actual class synopses declare the functions
to take 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: 2026-06-09
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.
This problem has already affected publicly available implementations.
For example, Microsoft Visual C++ STL introduces a memory leak if
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.
To my mind, the described modification is sufficient to resolve the issue. But to
emphasize the possibility to call 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 that init 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).
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.
[Brno 2026-06-09; LWG issue processing]
Review history. Would like to accept this.
MSVC still requires exactly one call to init.
Tim to contact implementers to see if changing this would be acceptable.
Jonathan would like to make the default constructor set badbit and not
leave it so that touching the stream is undefined. That would make it safe
to attempt I/O operations (they'd just fail) but e.g. getloc() could
not succeed for MSVC which doesn't allocate a locale until init is called.
Changing that would need a paper.
[Brno 2026-06-09; Jonathan provides new wording]
Fix grammar.
Proposed resolution:
This wording is relative to N5046.
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 142.
-?- Remarks: The function can be called multiple times during the object lifetime. Each subsequent call reinitializes the object to the state described in Table 142.
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: Ready Submitter: Pete Becker Opened: 2012-11-21 Last modified: 2026-06-09
Priority: 3
View all issues with Ready 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.
The problem is that for iterator values prior to the suffix iterator, equality means the same regular expression and the same matched sequence (both uses of "same" refer to identity, not equality); for the suffix iterator, equality means that the matched sequences compare equal.
[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.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]
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]
[Brno 2026-06-09; Change status Open → Ready.]
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.
The simple solution is to correct that statement in Table 102, but I believe this is part of a larger problem
of underspecified behavior: The new standard has made an effort in regards to allocators and now fully
specifies what happens to stateful allocator objects. It has even specified what happens to stateful 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 in std::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.
We have wording from C++98 for associative containers (but not for unordered containers!) that was never adjusted to C++11 move-semantics in 23.2.7 [associative.reqmts] p12:
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 the CopyConstructible 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:
<cuchar> macrosSection: 27.5 [c.strings] Status: Core Submitter: Jason Merrill Opened: 2013-01-29 Last modified: 2026-06-10
Priority: 4
View other active issues in [c.strings].
View all other issues in [c.strings].
View all issues with Core 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?
[Brno 2026-06-09; Change status New → Core]
In C++14 these macros were defined in a table showing them as macros
in <cuchar>, and referring to the C Unicode TR,
ISO/IEC TR 19769:2004, where the macros were defined in the header.
But in the final C11 standard, the macros were predefined by the compiler
not in the header. So when C++17 rebased on C11 and referred to the uchar.h
from the C standard, we lost any reference to those macros.
An editorial change
for C++17 removed the table mentioning those macros,
following an arguably incorrect
editorial change
to "remove an outdated reference to the C Unicode TR".
Although uchar.h is in C11, we lost any mention of the macros by
saying that our uchar.h is the same as C uchar.h.
So those macros now have no existence at all in C++.
They're not inherited from the C version of uchar.h
and they're not predefined by a C++ compiler.
If we want to copy those macros from C, they should be in 15.12 [cpp.predefined], so Core should decide on that.
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:
unique_ptr<T>::get_deleter()(p) to be able to destroy the unique_ptrSection: 20.4.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.4.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]
[para 5]: Postconditions: If the call get_deleter()(old_p) destroyed *this, none. Otherwise,
get() == p. [Note: The postcondition does not hold if the call to
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.4.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.4.1.3 [unique.ptr.single]/1 was insufficient, because
d might be a reference to a deleter functor that's destroyed during self-destruction.
We believed that 20.4.1.3.6 [unique.ptr.single.modifiers]/4 was already sufficiently clear. The Standard occasionally prevents
implementations of 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.4.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.4.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.4.1.3.6 [unique.ptr.single.modifiers] p4+5 as indicated:
void reset(pointer p = pointer()) noexcept;-3- Requires: The expression
get_deleter()(get())shall be well formed, shall have well-defined behavior, and shall not throw exceptions.-4- Effects: assigns
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 toget_deleter()may destroy*this. — end note]-5- Postconditions: If the call
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.4.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.
Suggested alternative to the current proposed wording:
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.4.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 edge x->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.
Presumably the input type must be convertible to the output type. What's less clear is what the requirements are on the comparison operator. Does the algorithm only perform comparisons on two values of the output type, or does it also perform comparisons on values of the input type, or might it even perform heterogeneous comparisons?
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.
The semantics of 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.
Consider the following:
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.
Saying that the initialized part of the string is used already describes what needs to happen here.
[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
traits::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.-?- The function can make a read position available only if
(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: 2026-06-11
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.
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.
This wording is necessary to limit speculative compilations needed to implement these traits, but it is also lengthy and repetitive.
[2014-05-19, Daniel suggests a descriptive term]
constrictedly well-formed expression:
An expression e depending on a set of types A1, ..., 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".
I would like to point out that given the new definition of "valid in a contrived argument deduction context", there are several other
places of the Library specification that could take advantage of this wording to improve the existing specification, such as
22.10.17.3 [func.wrap.func] p2, most functions in 20.2.9.3 [allocator.traits.members], and the **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 types A1, ..., 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.5.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.7 [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 of std::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.
Technically a library can solve these issues: For special member functions by defining them in some base class, for others
by transforming them effectively into a function template due to the great feature of default template arguments for
function templates (At the very moment the validity of the latter approach depends on a resolution of core language issue
CWG 1635, though). E.g. the here mentioned
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 example std::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.
VV: The issue says that STL was going to propose new wording. Has he done that? AM: I believe we're looking at that. GR: The request touches on multiple paragraphs, and this PR has only one new paragraph, so this looks like it's not up-to-date. MC: This was last updated a year ago in Issaquah.
Conclusion: Skip, not up to date.
[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).
So I guess the question is do we gain anything by requiring all character-comparisons to go through the locale when this bit is set? Certainly it adds a great deal to the implementation effort (it's not what Boost.Regex has ever done). I guess the question is are differing code-points that collate identically an important use case? I guess there might be a few Unicode code points that do that, but I don't know how to go about verifying that.
STL:
If this was unintentional, then 28.6.4.2 [re.synopt]/1's table should be left alone, while 28.6.12 [re.grammar]/14 should be changed instead.
Jeffrey Yasskin:
This page mentions that [V] in Swedish should match "W" in a perfect world.
However, the most recent version of TR18 retracts
both language-specific loose matches and language-specific ranges
because "for most full-featured regular expression engines, it is
quite difficult to match under code point equivalences that are not
1:1" and "tailored ranges can be quite difficult to implement
properly, and can have very unexpected results in practice. For
example, languages may also vary whether they consider lowercase below
uppercase or the reverse. This can have some surprising results: [a-Z]
may not match anything if 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.".
Taken literally, 28.6.6 [re.traits]/7 appears to imply that named locales are required to hold their collate facets with
dynamic type 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.
Demonstration:
#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
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)-2- Returns:
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:
sshall not be a null pointer.-4- Effects: Behaves like a formatted inserter (as described in 31.7.6.3.1 [ostream.formatted.reqmts]) of
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
seqas described in 31.7.6.3.1 [ostream.formatted.reqmts]. Insertsseqintoout. Callsout.width(0).-5- Returns:
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 all other issues in [template.bitset].
View all issues with Open status.
Discussion:
Example: char16_t('1') != u'1' is possible.
The numeric value of 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
zeroandonecompare equal to the character literals0and1of typecharT, respectively.-3- Requires::
pos <= str.size().[…]
is_empty type traitSection: 21.3.5.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.
I'm not sure what the purpose of 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).
N3873 includes
a partial solution to this in the form of a new 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).
24.6.4 [istreambuf.iterator] says that 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: 2026-06-16
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.
Overflow could be detected if the duration integer literals were literal operator templates, otherwise overflow can either be undefined or a run-time error, not ill-formed.
[Urbana 2014-11-07: Move to Open]
[2026-06-16; Jonathan adds wording.]
The library has no way to distinguish the case where a suffix is
"applied to an integer literal", as opposed to using function call notation
like operator""s(123).
There is never overflow, because converting unsigned long long to a rep
such as signed long long is always well-formed, just not value-preserving.
Unless it is used as a constant subexpression, a user-defined-literal such as
123s is not constant evaluated, so in general checking the value at
compile-time is unimplementable.
If we made these operators consteval then using them as the suffix of a
user-defined-literal would always be a constant expression.
It would make it ill-formed to call operator""s(some_variable)
but I consider that perfectly acceptable. Such uses are absurd,
and the existing wording is all specified in terms of suffixes anyway,
not in terms of function calls to those operators.
P3931R0 proposes making these (and other literal operators)
consteval, which would resolve this issue by making it implementable.
Alternatively, we could permit the integral literal operators to be implemented
as literal operator templates, i.e. template<char... Digits>,
as this makes it possible to check for overflow. This is what libstdc++ has
done since the duration literals were first implemented.
Alternatively, we could just remove the rule saying that overflow is detected. Neither libc++ nor MSVC implements the overflow check, so if it was a problem that affects many users we'd have heard about it.
Proposed resolution:
This wording is relative to N5046.
Three options are presented, choose only one.Option A
Modify 30.5.9 [time.duration.literals] as indicated:
-1- This subclause describes literal suffixes for constructing duration literals. The suffixes
h,min,s,ms,us,nsdenote duration values of the corresponding typeshours,minutes,seconds,milliseconds,microseconds, andnanosecondsrespectively if they are applied to integer-literals.-2- If any of these suffixes
areis applied to a floating-point-literal the result is achrono::durationliteral with an unspecified floating-point representation.-3- If any of these suffixes
areis applied to an integer-literal and the parameter cannot be represented in thereptype of the return type,the resultingthe program is ill-formed.chrono::durationvalue cannot be represented in the result type because of overflow,-4- [Example 1: The following code shows some duration literals.
— end example]using namespace std::chrono_literals; auto constexpr aday=24h; auto constexpr lesson=45min; auto constexpr halfanhour=0.5h;constexprconsteval chrono::hours operator""h(unsigned long long hours);constexprconsteval chrono::duration<unspecified, ratio<3600, 1>> operator""h(long double hours);-5- Returns: A
durationliteralvalue representinghourshours.constexprconsteval chrono::minutes operator""min(unsigned long long minutes);constexprconsteval chrono::duration<unspecified, ratio<60, 1>> operator""min(long double minutes);-6- Returns: A
durationliteralvalue representingminutesminutes.constexprconsteval chrono::seconds operator""s(unsigned long long seconds);constexprconsteval chrono::duration<unspecified> operator""s(long double seconds);-7- Returns: A
durationliteralvalue representingsecondsseconds.-8- [Note 1: The same suffix
sis used forbasic_stringbut there is no conflict, since duration suffixes apply to numbers and string literal suffixes apply to character array literals. — end note]constexprconsteval chrono::milliseconds operator""ms(unsigned long long msec);constexprconsteval chrono::duration<unspecified, milli> operator""ms(long double msec);-9- Returns: A
durationliteralvalue representingmsecmilliseconds.constexprconsteval chrono::microseconds operator""us(unsigned long long usec);constexprconsteval chrono::duration<unspecified, micro> operator""us(long double usec);-10- Returns: A
durationliteralvalue representingusecmicroseconds.constexprconsteval chrono::nanoseconds operator""ns(unsigned long long nsec);constexprconsteval chrono::duration<unspecified, nano> operator""ns(long double nsec);-11- Returns: A
durationliteralvalue representingnsecnanoseconds.
Add [diff.cpp26.duration] to Annex C:
C.1.? Clause 30 [time]: Time library
Affected subclause: 30.5.9 [time.duration.literals]
Change: Literal operators for
durationliterals are immediate functions.Rationale: Allow checking for overflow.
Effect on original feature: Valid C++ 2026 code that used function call syntax and non-constant arguments to create duration literals is ill-formed in this document.
[Example ?:
— end example]int n = 60; auto t = std::chrono::literals::operator""s(n); // ill-formed; previously well-formed
Modify 30.5.9 [time.duration.literals] as indicated:
-1- This subclause describes literal suffixes for constructing duration literals. The suffixes
h,min,s,ms,us,nsdenote duration values of the corresponding typeshours,minutes,seconds,milliseconds,microseconds, andnanosecondsrespectively if they are applied to integer-literals.-2- If any of these suffixes
areis applied to a floating-point-literal the result is achrono::durationliteral with an unspecified floating-point representation.-3- If any of these suffixes
areis applied to an integer-literal and the parameter cannot be represented in thereptype of the return type,the resultingthe program is ill-formed.chrono::durationvalue cannot be represented in the result type because of overflow,-?- It is unspecified whether the literal operators below are usable only as the suffix of a user-defined-literal (5.13.9 [lex.ext]), or whether they can also be called using function call notation. If they can be called using function call notation, using
unsigned long longparameters that cannot be represented in thereptype of the return type is ill-formed, no diagnostic required.
Option B should also do the "durationliteralvalue" drive-by fix, and probably update Annex C as well.
Modify 30.5.9 [time.duration.literals] as indicated:
-1- This subclause describes literal suffixes for constructing duration literals. The suffixes
h,min,s,ms,us,nsdenote duration values of the corresponding typeshours,minutes,seconds,milliseconds,microseconds, andnanosecondsrespectively if they are applied to integer-literals.-2- If any of these suffixes
areis applied to a floating-point-literal the result is achrono::durationliteral with an unspecified floating-point representation.
-3- If any of these suffixes are applied to an integer-literal and the resultingchrono::durationvalue cannot be represented in the result type because of overflow, the program is ill-formed.
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.
Making this implementation-defined wouldn't affect users (who can observe this only if they're specifically looking for it) and
wouldn't affect implementations who need 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.
These are probably rare things, but I can imagine at least some forms of the latter being used in SFINAE tricks.
[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.
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 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.)
The case of 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.
The 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.
In the following program, there appears to be conflicting definitions of a specialization of
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.
The current wording in N4296 relevant here is from Table 28 — "Allocator requirements" in 16.4.4.6 [allocator.requirements]:
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.
Similarly, the 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).
Even though the value type has an accessible and 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.
Requiring 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 via
is_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 keeping is_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.
In addition the library resolution proposed in the LWG issues 2089(i) and
N4462,
will break the relation between the 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.
I believe all three of Dinkumware, libc++ and libstdc++ implement
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:
<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.
So, I think that we can specify that the implementation-defined strict total order (3.28 [defns.order.ptr]) generates a core constant expression if and only if the corresponding underlying comparison expression comparing pointer values is a core constant expression. No any other case should be a core constant expression, otherwise we should also make the underlying comparison expression a core constant expression.
IMO the proposed resolution is already implemented in libc++, libstdc++, and MSVC STL, and implementable on compilers
that either support flat address spaces only or have implemented intrinsics needed for transparent comparison operators
and 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.4.1 [unique.ptr]/5) and
std::enable_shared_from_this (20.4.2.7 [util.smartptr.enab]/2)).
A resolution would be to copy-paste the relevant text from such a paragraph.
Proposed resolution:
Section: 21.3.5.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.5.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).
Instantiating 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 synopsis basic_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:
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 other active issues in [new.delete.dataraces].
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).
28.3.4.6.2.3 [locale.time.get.virtuals] makes it implementation defined whether two-digit years are accepted, etc., but does not say anything specifically about three-digit years.
The implementation freedom to not report errors in 28.3.4.6.2 [locale.time.get] paragraph 1 also seems to be too broad.
See also the discussion following c++std-lib-38042.
[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.
However for a 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.
Similar problems exist with POD types with a deleted default constructor.
I believe that 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.
Consider the following program:
#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.
A std::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.
Consider the following snippet:
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.
Straw polls:
SF | F | N | A | SA
For the default behaviour:
"1. Run on completed task or new execution agent"
0 | 7 | 5 | 1 | 0
"2. Run on completed task or .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.3 [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.3 [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: Ready Submitter: Andy Giese Opened: 2016-02-05 Last modified: 2026-06-09
Priority: 4
View all other issues in [time.syn].
View all issues with Ready 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.
The standard should specify that implementations guarantee that a 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.
I propose to change the definitions of the convenience typedefs as follows:
// 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.
Consider seconds, 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.
[Brno 2026-06-09; Change status New → Ready.]
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.4.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.4.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.4.2.2.2 [util.smartptr.shared.const] p8 says about the effect of
shared_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.
Since 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.4.2.2 [util.smartptr.shared] p1.
These cause a contradiction:
20.4.2.2.2 [util.smartptr.shared.const] p9 sets a postcondition
use_count() == 1 on shared_ptr(nullptr, d). But
20.4.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.4.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.4.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.4.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: Ready Submitter: Robert Haberlach Opened: 2016-02-28 Last modified: 2026-06-09
Priority: 3
View all other issues in [reverse.iterator].
View all issues with Ready 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).
Suggested resolution: Adjust 24.5.1.2 [reverse.iterator]'s synopsis and 24.5.1.6 [reverse.iter.elem] to
use reference instead of unspecified.
[2021-06-06 Tim syncs wording to current working draft]
[Brno 2026-06-09; Change status New → Ready.]
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.4.1 [unique.ptr], 20.4.1.2.1 [unique.ptr.dltr.general], 20.4.2.2 [util.smartptr.shared], 20.4.2.3 [util.smartptr.weak], 20.4.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)
When formatting for output (28.3.4.7.3.3 [locale.money.put.virtuals]), it is not clear that
"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".
I believe that a strict interpretation of the current wording supports U+0020; however, 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].
There is implementation divergence:
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.
With this description:
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.
I think that applying padding and alignment is more appropriate.
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 on n > capacity() case,
see here.
libstdc++ doesn't rely on the precondition.
It creates temporary vector(n, t) and swap()
on n > capacity() case, see
here.
MSVC relies on the precondition.
It unconditionally does clear() 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.
I also found that 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
assign(basic_string(n, c)).Returns:
*this.
This can be seen as another gap.
Looking back on the history, I found that the definition of assign(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
Move to open and note that Jonathan is reviewing and making recommendations.
[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: Ready Submitter: Billy Robert O'Neal III Opened: 2016-05-20 Last modified: 2026-06-12
Priority: 3
View all other issues in [unord].
View all issues with Ready status.
Discussion:
The resolution of LWG 2210(i) missed constructors accepting a range or initializer list and allocator.
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.
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).
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.
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.
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.
[Brno 2026-06-11; Jonathan provides new wording]
Needs constexpr sprinkled everywhere, rebase on current working draft.
[Brno 2026-06-11; Change status: New → Ready.]
Proposed resolution:
This resolution is relative to N5046.
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:
[…]
constexpr unordered_map(size_type n, const hasher& hf, const allocator_type& a)
: unordered_map(n, hf, key_equal(), a) { }
template<class InputIterator>
constexpr unordered_map(InputIterator f, InputIterator l, const allocator_type& a);
template<class InputIterator>
constexpr 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>
constexpr 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>
constexpr unordered_map(from_range_t, R&& rg, const allocator_type& a);
template<container-compatible-range<value_type> R>
constexpr 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>
constexpr 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) { }
constexpr unordered_map(initializer_list<value_type> il, const allocator_type& a);
constexpr 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> constexpr 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> constexpr 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) { } constexpr 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:
[…]
constexpr unordered_multimap(size_type n, const hasher& hf, const allocator_type& a)
: unordered_multimap(n, hf, key_equal(), a) { }
template<class InputIterator>
constexpr unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a);
template<class InputIterator>
constexpr 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>
constexpr 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>
constexpr unordered_multimap(from_range_t, R&& rg, const allocator_type& a);
template<container-compatible-range<value_type> R>
constexpr 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) { }
[…]
constexpr unordered_multimap(initializer_list<value_type> il, const allocator_type& a);
constexpr 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> constexpr 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> constexpr 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) { } constexpr 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:
[…]
constexpr unordered_set(size_type n, const hasher& hf, const allocator_type& a)
: unordered_set(n, hf, key_equal(), a) { }
template<class InputIterator>
constexpr unordered_set(InputIterator f, InputIterator l, const allocator_type& a);
template<class InputIterator>
constexpr 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>
constexpr 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) { }
constexpr unordered_set(initializer_list<value_type> il, const allocator_type& a);
constexpr 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>
constexpr unordered_set(from_range_t, R&& rg, const allocator_type& a);
template<container-compatible-range<value_type> R>
constexpr 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> constexpr 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> constexpr 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) { } constexpr 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:
[…]
constexpr unordered_multiset(size_type n, const hasher& hf, const allocator_type& a)
: unordered_multiset(n, hf, key_equal(), a) { }
template<class InputIterator>
constexpr unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a);
template<class InputIterator>
constexpr 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>
constexpr 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>
constexpr unordered_multiset(from_range_t, R&& rg, const allocator_type& a);
template<container-compatible-range<value_type> R>
constexpr 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) { }
[…]
constexpr unordered_multiset(initializer_list<value_type> il, const allocator_type& a);
constexpr 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> constexpr 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> constexpr 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) { } constexpr 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: Ready Submitter: Tim Song Opened: 2016-05-23 Last modified: 2026-06-09
Priority: 3
View all other issues in [complex.ops].
View all issues with Ready 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
xof the form:u,(u), or(u,v), whereuis the real part andvis the imaginary part (31.7.5.3 [istream.formatted]).Requires: The input values shall be convertible to
T.If bad input is encountered, calls
is.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.)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, calls
is.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.]
[Brno 2026-06-09 Status changed: New → Ready.]
The relevant paragraphs are 9-13 now.
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, assigns
complex<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 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
numeric_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.I know that
__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 being
0andfalse? 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.
Along the same lines, one might wonder why the members that only make sense for floating-point types are required to be defined to nonsense values for integer types.
[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:
shared_ptr deleter not specified to observe expired weak_ptr instancesSection: 20.4.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.
Suggested fix: 20.4.2.2.3 [util.smartptr.shared.dest] should specify that the decrease in
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.4.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 of pair, tuple, unique_ptr, array, queue,
priority_queue, stack, and should eventually also apply to optional and variant.
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
x.swap(y).-8- Remarks: This function shall
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.4.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
not participate in overload resolutionbe defined as deleted unlessis_swappable_v<D>istrue.-2- Effects: Calls
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
not participate in overload resolutionbe defined as deleted unlessN == 0oris_swappable_v<T>istrue.-2- Effects: As if by
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
not participate in overload resolutionbe defined as deleted unlessis_swappable_v<Container>istrue.-2- Effects: As if by
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-
Remarks:This function shallnot participate in overload resolutionbe defined as deleted unlessis_swappable_v<Container>istrueandis_swappable_v<Compare>istrue.-2- Effects: As if by
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
not participate in overload resolutionbe defined as deleted unlessis_swappable_v<Container>istrue.-2- Effects: As if by
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
x.swap(y).-2- Remarks: This function shall
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
v.swap(w).-2- Remarks: This function shall
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
x.swap(y).-8- Remarks: This function
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
x.swap(y).-2- Remarks: This function
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
v.swap(w).-2- Remarks: This function
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.4.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-
Constraints:N == 0oris_swappable_v<T>istrue.-2- Effects: As if by
x.swap(y).-3- Complexity: Linear in
N.-?- Remarks: This function is defined as deleted unless
N == 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: Ready Submitter: Tim Song Opened: 2016-10-29 Last modified: 2026-07-01
Priority: 3
View all other issues in [optional.ctor].
View all issues with Ready 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
T.-26- Remarks: If
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.
The wording should probably be recast to refer to the entire initialization.
[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.
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 the
selected constructor ofinitialization of the contained value.Tconstexpr optional(optional&& rhs) noexcept(see below);-7- […]
[…]
-10- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.Ttemplate<class... Args> constexpr explicit optional(in_place_t, Args&&... args);-12- […]
[…]
-15- Throws: Any exception thrown by the
selected 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 the
selected constructor ofinitialization of the contained value.Ttemplate<class U = T> constexpr explicit(see below) optional(U&& v);-22- […]
[…]
-25- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.Ttemplate<class U> explicit(see below) optional(const optional<U>& rhs);-27- […]
[…]
-30- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.Ttemplate<class U> explicit(see below) optional(optional<U>&& rhs);-32- […]
[…]
-35- Throws: Any exception thrown by the
selected 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 by
calling 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 by
calling 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 by
calling 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 exceptions
arising 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 the
selected 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 the
selected 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 the
selected 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 the
selected constructor ofinitialization of the contained value.VT-8- […]
template<class T, class U, class... Args> decay_t<T>& emplace(initializer_list<U> il, Args&&... args);-9- […]
[…]
-15- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.VT
[2026-06-10; Tim rebases and provides new wording]
The variant part has been fixed by LWG 4460(i).
[Brno 2026-06-10; Change status: New → Ready.]
Proposed resolution:
This wording is relative to N5050.
Modify 22.5.3.2 [optional.ctor] as indicated:
constexpr optional(const optional& rhs);[…]
-6- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.Tconstexpr optional(optional&& rhs) noexcept(see below);[…]
-11- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.Ttemplate<class... Args> constexpr explicit optional(in_place_t, Args&&... args);[…]
-16- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.Ttemplate<class U, class... Args> constexpr explicit optional(in_place_t, initializer_list<U> il, Args&&... args);[…]
-21- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.Ttemplate<class U = T> constexpr explicit(see below) optional(U&& v);[…]
-26- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.Ttemplate<class U> explicit(see below) optional(const optional<U>& rhs);[…]
-31- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.Ttemplate<class U> explicit(see below) optional(optional<U>&& rhs);[…]
-36- Throws: Any exception thrown by the
selected constructor ofinitialization of the contained value.T
Modify 22.7.4.2 [any.cons] as indicated:
any(const any& other);[…]-2- Effects: […]
-3- Throws: Any exceptions
arising 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 the
selected 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 the
selected 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 the
selected 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 the
selected constructor ofinitialization of the contained value.VT-8- […]
template<class T, class U, class... Args> decay_t<T>& emplace(initializer_list<U> il, Args&&... args);-9- […]
[…]
-15- Throws: Any exception thrown by the
selected 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.
Our reasoning is that functions registered with 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
at_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]-?- 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]
Section: 32.2.5 [thread.req.lockable], 32.6.4 [thread.mutex.requirements] Status: Ready Submitter: Agustín K-ballo Bergé Opened: 2016-11-12 Last modified: 2026-06-10
Priority: 3
View all issues with Ready 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.]
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.
[Brno 2026-06-10; Change status: New → Ready.]
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.5.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.5.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.
This is interpreted by implementations to include the destructor call,
presumably for consistency with 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:
*thiscontains a value.-2- Returns:
val.-3- Throws: Nothing.
-4- Remarks: These functions shall be
constexprfunctions.
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: 2026-06-11
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").
[variant.ctor]/1 contains this, which should also be fixed:
"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.
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).
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
constexpr(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.-?- Let
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 this superseded resolution is the same as the next resolution below this one …]
[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.
[Brno 2026-06-11; needs to be rebased, especially if P3367 is approved.]
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
constexpr(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.-?- Let
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 inside
noexceptis 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 function
iscan 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?
On my implementation we got "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.
Daniel K. volunteered to provide such wording.
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.
Incidentally, with the exception of 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:
(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>.-3 Function template
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.3 [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.
An implementation like
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.6.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.6.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 value largest_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: 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:
shared_ptr from a
unique_ptrSection: 20.4.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+=.
An equivalent optimization could be allowed by adding a single-argument overload to 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.
Unfortunately, there exists some cases, where we currently overspecify imposing complete type requirements where they
are not actually required. For example, for the following situation the answer of the trait could
be given without ever needing the complete type of 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.
This wording is relative to N4981.
- In 21.3.5.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.7 [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.5.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.7 [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".
In [fs.class.directory_iterator] an "implementation-defined directory-like file type" is an implementation-defined file type (see [fs.enum.file_type] and Table 115) that is treated like a directory by the special rules that the OS has for non-regular files (see 31.12.6.2 [fs.path.generic]).
In 31.12.13.4 [fs.op.copy], an "implementation-defined file type" is exactly that, see [fs.enum.file_type] and Table 115 again. I don't see what isn't clear about that. Maybe add a cross-reference to [fs.enum.file_type].
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.
27.2.4.4 [char.traits.specializations.char16.t] says:
— 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.
I believe that 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.
With the advent of concepts as first-class citizens in the language, getting this right as soon as possible is a priority.
This issue seems to be similar to both LWG 448(i) and LWG 484(i), but not the same.
The proposed resolution stems from two considerations outlined below:
Convertibility is too strong for all algorithms
No algorithm in the standard library requires convertibility to
value_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
CopyConstructibleimplicitly 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.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, the
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 like
Iterator::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).
Really, no generic code should be doing *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.
The list of algorithms with underspecified requirements from <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.
Change description: acc, a variable of InputIterator's value type, shall be constructible
adjacent_difference
acc is not defined.
Change description: 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: 2026-06-08
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: Let
N = 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: Ready Submitter: Jonathan Wakely Opened: 2017-06-21 Last modified: 2026-06-10
Priority: 3
View all other issues in [locale.money.put.virtuals].
View all issues with Ready 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.
If we want the leading zero, we should say so.
[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.
[Brno 2026-06-10; Change status: New → Ready.]
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.
We could change it to be "otherwise the program is ill-formed", or to remove the function templates from overload
resolution when the argument is not 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.
It should be emphasized that this extension of the current wording would provide support for 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
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.-2- Requires:
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: If
is_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 expression
in >> 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 if
inis 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
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.-5- Requires:
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: If
is_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 if
outis 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.
In [re.grammar] paragraph 14:
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.
From N4660 subclause 31.13 [re.grammar], 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.
It is therefore unclear what the effect is if 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>;
[…]
};
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.
It's possible to have expressions that meet the C++ requirements for a constant subexpression, but fail to meet the C requirements, and so don't compile.
#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.
I think either 19.3.2 [cassert.syn] or 19.3.3 [assertions.assert] should repeat the requirement from C that
E has scalar type, either normatively or in a note. We should also consider whether "contextually converted to 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).
Simply relaxing the requirement to
-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).
The effect of the proposed relaxation on existing library implementations would be minimal in the extreme, and on existing user code nil. The base algorithm code remains exactly identical. The only changes needed would be to any instrumentation in a debugging version of the library, which would just need to relax its check, and to test suites that should exercise the newly tolerated input.
Users today are tempted to get the improved performance by relying on existing implementations' tacit tolerance of input that
only satisfies the proposed, relaxed requirements. In fact, the
cppreference.com page on 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".
As an example of why this is an issue, note that 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.
As noted in the LWG 2162(i) comments, we don't currently guarantee it can be called on a 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]
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:
X::size_type-51- Returns: The largest value that can meaningfully be passed to
X::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:
X::size_type-51- Returns: The largest value that can meaningfully be passed to
X::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.
There is no way to construct a 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-
reference_wrapper<T>is aCopyConstructibleandCopyAssignablewrapper around a reference to an object or function of typeT.Tshall be a referenceable type (3.46 [defns.referenceable]) that is not a reference type.-2-
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.
As concrete wording suggestion I would recommend wording that ensures that an ill-formed program is only required when a
specialization of 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.46 [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-
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.46 [defns.referenceable]) as the argument for the template parameterT, the program is ill-formed.-2-
reference_wrapper<T>is a trivially copyable type (6.9 [basic.types]).-3- The template parameter
Tofreference_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: Ready Submitter: Tim Song Opened: 2017-12-15 Last modified: 2026-07-31
Priority: 3
View all issues with Ready 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".
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:
atomic<integral>'s working foroperator++/operator--is shared withatomic<T*>. — end drafting note][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:
return reinterpret_cast<T*>(reinterpret_cast<ptrdiff_t>(fetch_key(operand)) op operand);Remarks: The result may be an undefined address, but the operations otherwise have no undefined behavior.
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]
[2026-07-30; Tim provides new wording]
[2026-07-31 LWG telecon; Status changed: New → Ready.]
Proposed resolution:
This wording is relative to N5054.
Modify 32.5.7.3 [atomics.ref.int] as indicated:
constexpr value_type operator op=(value_type operand) const noexcept;-14- Constraints:
is_const_v<integral-type>isfalse.-15- Effects: Equivalent to:
returnfetch_key(operand)op operand;-16- Returns: The value written by the call to
fetch_key.
Modify 32.5.7.4 [atomics.ref.float] as indicated:
constexpr value_type operator op=(value_type operand) const noexcept;-17- Constraints:
is_const_v<floating-point-type>isfalse.-18- Effects: Equivalent to:
return fetch_key(operand) op operand;-19- Remarks: If the result is not a representable value for its type (7.1 [expr.pre]) the result is unspecified, but the operations otherwise have no undefined behavior. The floating-point environment (29.3 [cfenv]) for atomic arithmetic operations on
floating-point-typemay be different than the calling thread's floating-point environment.
Modify 32.5.7.5 [atomics.ref.pointer] as indicated:
constexpr value_type operator op=(difference_type operand) const noexcept;-14- Constraints:
is_const_v<pointer-type>isfalse.-15- Effects: Equivalent to:
returnfetch_key(operand)op operand;-16- Returns: The value written by the call to
fetch_key.
Modify 32.5.7.6 [atomics.ref.memop] as indicated:
constexpr value_type operator++() const noexcept;-6- Constraints:
is_const_v<referred-type>isfalse.-7- Effects: Equivalent to:
returnfetch_add(1) + 1operator+=(1);constexpr value_type operator--() const noexcept;-8- Constraints:
is_const_v<referred-type>isfalse.-9- Effects: Equivalent to:
returnfetch_sub(1) + 1operator-=(1);
Modify 32.5.8.3 [atomics.types.int] as indicated:
integral-type operator op=(integral-type operand) volatile noexcept; constexpr integral-type operator op=(integral-type operand) noexcept;-14- Constraints: For the
volatileoverload of this function,is_always_lock_freeistrue.-15- Effects: Equivalent to:
returnfetch_key(operand)op operand;-16- Returns: The value written by the call to
fetch_key.
Modify 32.5.8.4 [atomics.types.float] as indicated:
floating-point-type operator op=(floating-point-type operand) volatile noexcept; constexpr floating-point-type operator op=(floating-point-type operand) noexcept;-17- Constraints: For the
volatileoverload of this function,is_always_lock_freeistrue.-18- Effects: Equivalent to:
return fetch_key(operand) op operand;-19- Remarks: If the result is not a representable value for its type (7.1 [expr.pre]) the result is unspecified, but the operations otherwise have no undefined behavior.
Atomic arithmetic operations onThe floating-point environment (29.3 [cfenv]) for atomic arithmetic operations onfloating-point-typeshould conform to thestd::numeric_limits<floating-point-type>traits associated with the floating-point type (17.3.3 [limits.syn]).floating-point-typemay be different than the calling thread's floating-point environment.
Modify 32.5.8.5 [atomics.types.pointer] as indicated:
T* operator op=(ptrdiff_t operand) volatile noexcept; constexpr T* operator op=(ptrdiff_t operand) noexcept;-15- Constraints: For the
volatileoverload of this function,is_always_lock_freeistrue.-16- Effects: Equivalent to:
returnfetch_key(operand)op operand;-17- Returns: The value written by the call to
fetch_key.
Modify 32.5.8.6 [atomics.types.memop] as indicated:
value_type operator++() volatile noexcept; constexpr value_type operator++() noexcept;-5- Constraints: For the
volatileoverload of this function,is_always_lock_freeistrue.-6- Effects: Equivalent to:
returnfetch_add(1) + 1operator+=(1);value_type operator--() volatile noexcept; constexpr value_type operator--() noexcept;-7- Constraints: For the
volatileoverload of this function,is_always_lock_freeistrue.-8- Effects: Equivalent to:
returnfetch_sub(1) + 1operator-=(1);
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 parallel generate() 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 object
fto 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);
-6- Requires:Functionshall meet the requirements ofCopyConstructible.-7- Effects: Applies the function object
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 object
fto 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);
-16- Requires:Functionshall meet the requirements ofCopyConstructible.[…]
-18- Effects: Applies the function object
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 object
fto 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);
-6- Preconditions:Functionmeets the Cpp17CopyConstructible requirements.-7- Effects: Applies the function object
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 object
fto 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.Functionmeets theCpp17CopyConstructiblerequirements.-23- Effects: Applies the function object
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: Ready Submitter: Antony Polukhin Opened: 2018-01-24 Last modified: 2026-07-01
Priority: 3
View all other issues in [syserr.errcode.overview].
View all issues with Ready 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
[Brno 2026-06-10; LWG agreed this is an improvement, need to change error_condition too.]
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;
[…]
};
[…]
}
[2026-06-10; Tim provides new wording]
[Brno 2026-06-12; Status changed: New → Ready.]
Proposed resolution:
This wording is relative to N5050.
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;
[…]
};
[…]
}
Modify 19.5.5.1 [syserr.errcondition.overview] as indicated:
namespace std {
class error_condition {
public:
// 19.5.5.2 [syserr.errcondition.constructors], constructors
[…]
error_condition(int val, const error_category& cat) noexcept;
error_condition(int val, const error_category&& cat) = delete;
[…]
// 19.5.5.3 [syserr.errcondition.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, and
setuid/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 for filesystem::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 than symlink_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 provides
linkat()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
options.-4- Effects:
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.]
(?.?) — 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).-5- Throws: As specified in 31.12.5 [fs.err.report].
-6- Remarks: For the signature with argument
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)?
The general container requirements say just:
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.
The allocator-aware container requirements add:
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.
I think, then, that the associative container requirements and unordered associative container requirements should specify different requirements for the "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".)
Daniel:
The "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.
We probably need to examine real implementations of these things and see what requirements are actually
necessary, as in general 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.
The second problem is that they do not have a separate specification to the serial forms. This is a problem in two ways. First, there is no more blanket wording saying a parallel policy algorithm has the same semantics as the serial form unless otherwise specified, so in principle these algorithms are totally unspecified. However, assuming that intent, all of the existing specifications use an as-if formulation with code that is explicitly serial in nature, so need a new specification that talks about the effects on each element without including the iteration order.
[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: 2026-07-14
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.
This leads to situation, when move/copy assigning subobject from 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.
[2026-07-14; Jonathan comments.]
The example in the discussion does not corrupt anything, because
is_nothrow_copy_constructible_v<std::string> is false and
is_nothrow_move_constructible_v<std::string> is true,
so operator= does
emplace<j>(Tj(std::forward<T>(t)))
which makes a copy before destroying the subobject.
To demonstrate the problem, replace str_t with:
struct str_t : std::string {
str_t(std::string s) : std::string(s) { }
str_t(const str_t& s) : std::string(s) { }
};
The proposed resolution changes the copy-assignment operator,
but the example uses operator=(T&&).
The proposed change would need to be made there instead.
Proposed resolution:
This wording is relative to N4727.
Change 22.6.3.4 [variant.assign] as indicated:
variant& operator=(const variant& rhs);-1- Let
jberhs.index().-2- Effects:
[…]
(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
jberhs.index().-7- Effects:
[…]
(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
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- Effects:
[…]
(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.
An implementation of 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: 2026-04-07
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.
It would be beneficial if users could migrate from 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.
[2025-12-10; P2827R1 addresses this]
[2026-04-07; P4168R0 addresses this]
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:
basehas a value between 2 and 36 (inclusive).-3- Effects: The pattern is the expected form of the subject sequence in the
"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:
fmthas the value of one of the enumerators ofchars_format.-7- Effects: The pattern is the expected form of the subject sequence in the
"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: 2026-04-07
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.
If the intent is merely that the floating point environment should not affect 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.
[2026-04-07; P4168R0 addresses this]
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:
fmthas the value of one of the enumerators ofchars_format.-7- Effects: The pattern is the expected form of the subject sequence in the
"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)?
The termination handlers and termination conditions I collected:
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 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 a
sizeparameter 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 a
sizeparameter 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 of copy 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.
I think it should. I checked the minutes of the LWG discussion from 2008 when this was added, and there was no discussion of overlapping ranges.
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:
resultshall not be in the range[first, first + n).-9- Effects: For each non-negative integer
i < n, performs*(result + i) = *(first + i).-10- Returns:
result + n.-11- Complexity: Exactly
nassignments.
[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
Nbe max(0, n).-11- Mandates: The type
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 integer
i < 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.
On the other hand, many standard library types are depicted as scoped enums, but still specified to be "bitmask types". As far as I can tell, it's impossible in library code to make a scoped enum implicitly convertible to its underlying type, and even if you could, what would be the point? Presumably the specification of those types as scoped enums is intended to have some sort of observable consequences.
In addition, some library types (notably in clause 31) are specified to be bitmask types, without indicating whether they are scoped or unscoped. It's not clear what the standard guarantees about e.g. whether they can be implicitly converted.
I assume the intent is that "bitmask type" doesn't specify an implicit conversion, or any of the other ways unscoped and scoped enums differ, but the standard doesn't actually say that. We really ought to rewrite [bitmask.types] as a requirements table, but here's a quick hack to the existing wording as a stopgap.
[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.
Except that it missed one: <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:
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.
Moreover, the attempt at avoiding this problem only tests 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".
The example should make it clear that this interpretation is allowed.
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:
relative_path().empty() ? path() : *--end().[Example:
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:
relative_path().empty() ? path() : *--end().[Example:
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: 2025-10-27
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:
Addresses RU-147
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.
This wording is relative to N4741.
- In 21.3.5.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.5.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.5.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.5.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.
For requirements on user code (e.g. [tuple.rel]), it is unclear whether all expressions of type 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_; }
};
}
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.
When only one composed operation handles the dynamic buffer, things seem to
work. However, if a composed operation wishes to invoke another composed
operation and pass that dynamic buffer, a problem arises. Consider two composed
operations 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 type
Xmeets 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:
vec.size() <= maximum_size-6- Effects: Initializes
vec_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.
If the intent is for 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: Ready Submitter: Jonathan Wakely Opened: 2018-06-26 Last modified: 2026-06-10
Priority: 3
View all other issues in [re.submatch].
View all issues with Ready 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]
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_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:
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++.
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).
[2026-06-10; Jonathan provides new wording]
Change operator== and operator<=> to take
string view by value not by reference.
[Brno 2026-06-10; Change status: New → Ready.]
Proposed resolution:
This wording is relative to N5046.
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, basic_string_view<typename iterator_traits<BiIter>::value_type, ST> rhs); template<class BiIter, class ST> bool operator<=>( const sub_match<BiIter>& lhs, 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, 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, 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.".
As a result, 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
a.destroy(p)if that call is well-formed; otherwise, invokesp->~T().-?- Throws: Nothing.
Section: 20.4.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.4.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).
The intention is that 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:
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.7.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 to pointer 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: Ready Submitter: Matthias Kretz Opened: 2018-12-06 Last modified: 2026-07-17
Priority: 3
View all issues with Ready 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.
It is not clear whether C's Annex F is "imported" into the C++ standard. [cmath.syn] p1 suggests that it
is: "The contents and meaning of the header <cmath> are the same as the C standard library
header <math.h>, […]".
[2018-12-21 Reflector prioritization]
Set Priority to 3
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
hypotfunctions compute the square root of the sum of the squares ofx,y, andz, without undue overflow or underflow. A range error may occur.-1- Returns: .
-?- Remarks: If
__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.
[2026-06-10; Tim provides new wording]
LWG in Brno is in favor of the direction of Option A. However, __STDC_IEC_559__ is not defined in C++.
The wording has been updated following the current C WD, taking into account
WG14 N2714.
[Brno 2026-06-12; Status changed: New → Ready.]
This wording is relative to N5050.
Modify 29.7.3 [c.math.hypot3] as indicated:
constexpr floating-point-type hypot(floating-point-type x, floating-point-type y, floating-point-type z);-?- Effects: Compute the square root of the sum of the squares of
x,y, andz, without undue overflow or underflow. A range error may occur.-1- Returns: .
-?- Remarks: If
numeric_limits<floating-point-type>::is_iec559istrue, the following guarantees hold:
hypot(x, y, z),hypot(x, z, y),hypot(z, y, x), andhypot(x, y, -z)are equivalent.if
yandzare zero andxis not a NaN,hypot(x, y, z)returns the absolute value ofx.if
xis positive or negative infinity,hypot(x, y, z)returns positive infinity, even ifyand/orzis a NaN.if
xis a NaN and neitherynorzis positive or negative infinity,hypot(x, y, z)returns a NaN.
[2026-06-30; Tim reopens and provides new wording. Status changed: Ready → Open.]
Matthias Kretz pointed out that is_iec559 is the wrong test as it only concerns representation and not behavior.
Until C++ gets its own floating point annex, this wording downgrades the Remarks: to a Recommended practice:
and ties the condition to whether the binary version of hypot conforms to C's Annex F.
[2026-07-17 LWG telecon; Change status New → Ready.]
Proposed resolution:
This wording is relative to N5050.
Modify 29.7.3 [c.math.hypot3] as indicated:
constexpr floating-point-type hypot(floating-point-type x, floating-point-type y, floating-point-type z);-?- Effects: Compute the square root of the sum of the squares of
x,y, andz, without undue overflow or underflow. A range error may occur.-1- Returns: .
-?- Recommended practice: An implementation whose two-parameter overload of
hypotconforms to ISO/IEC 9899:2024 Annex F should provide the following guarantees:
hypot(x, y, z),hypot(x, z, y),hypot(z, y, x), andhypot(x, y, -z)are equivalent.if
yandzare zero andxis not a NaN,hypot(x, y, z)returns the absolute value ofx.if
xis positive or negative infinity,hypot(x, y, z)returns positive infinity, even ifyand/orzis a NaN.if
xis a NaN and neitherynorzis positive or negative infinity,hypot(x, y, z)returns a NaN.
is_convertible is too strongSection: 21.3.7 [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.7 [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.7 [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&>.
This might also apply to the 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:
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 type allocator_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
function'sRequires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception whenthea function's precondition is violated.-2- Violation of any preconditions specified in an
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.
The standardese in [iterator.operations] strongly indicates that 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.8.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.8.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.
[expr.cond]/3 cannot apply since the operands are not glvalue bit-fields.
If 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&.
Only cases 5 and 6 reach [expr.cond]/6. This paragraph performs overload resolution, which may result in converting both operands to the same non-class type to invoke a builtin conditional operator "overload".
[expr.cond]/7 applies standard conversions including array-to-pointer and function-to-pointer conversion to the operands. Consequently, the operands are once more "decayed" if [expr.cond]/6 converted them to an array or function type. Again case-by-case:
[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.
Furthermore, there are now quite a few different cases describing the behavior of 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.8.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
COMMON-REF(A, B)is ill-formed.-3- Note A: For the
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
common_type<T...>::typeis valid, it denotes the same type asdecay_t<common_type<T...>::type>. — end note]-4- Note B: […]
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: 2026-06-11
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 […]".
Secondly, I think we shouldn't be using if-and-only-if since it restricts whether implementations can be
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.
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 type
T0can 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:
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
arrayis an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up toNelements whose types are convertible toT.-3- An
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:
!u.empty()forarray<T, N>where0 < N, andu.empty()for all other standard containers.-11- Complexity:
ConstantLinear forarray<T, N>where0 < Nand constant for all other standard containers.X u(rv); X u = rv;[…]-15- Postconditions:
uis equal to the value thatrvhad before this construction.-11- Complexity: Linear for
array<T, N>where0 < Nand constant for all other standard containers.a.swap(b)-45- Result:
void-46- Effects: Exchanges the contents of
aandb.-47- Complexity: Linear for
array<T, N>where0 < Nand constant for all other standard containers.
Modify 23.3.3.1 [array.overview] as indicated:
-2- An
arrayis an aggregate (9.5.2 [dcl.init.aggr]) that can be list-initialized with up toNelements whose types are convertible toT.-3- An
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.
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: 2026-06-09
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.
The issue LWG 2059(i) was reported and resolved in C++17 by adding an extra overload
for 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.
Consequently, this issue can be resolved in the same way as for 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.
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 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); […]
[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:
node_type-109- Effects: Removes the element pointed to by
q.-110- Returns: A
node_typeowning that element.-111- Complexity: Amortized constant.
a.extract(r)-?- Result:
node_type-?- Effects: Removes the element pointed to by
r.-?- Returns: A
node_typeowning that element.-?- Complexity: Amortized constant.
Modify 23.2.8.1 [unord.req.general] as indicated:
a.extract(q)-141- Result:
node_type-142- Effects: Removes the element pointed to by
q.-143- Returns: A
node_typeowning that element.-144- Complexity: Average case
𝒪(1), worst case𝒪(a.size()).a.extract(r)-?- Result:
node_type-?- Effects: Removes the element pointed to by
r.-?- Returns: A
node_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!
I thought about replacing 'entity' with 'name', but the same name may occur with
different contexts throughout the standard library. I thought about "contents of that
header", but run afoul of the same function overloads (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 object
Mis 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.
Further, for an allocator type 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.
Since some of containers in the standard have already explicitly relied on 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:
Y-17- Postconditions: For all
U(includingT),Y::template rebind<T>::otherisX.XX::is_always_equal::value == YY::is_always_equal::valueistrue.-18- Remarks: If
Allocatoris 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 template
rebindofXis 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.
I think if the allocator provided as template argument and the rebound have different propagation properties and behaves differently on propagation, then it is difficult or impossible for implementations to follow the current exception specifications.
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:
Y-17- Postconditions: For all
U(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: If
Allocatoris 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 template
rebindofXis 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]
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"]
Nevertheless, it's odd, and inconsistent. 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;
-11- Requires:tshall point to an object.-12- Effects: […]
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]).
However, the C++17 iterators did not support proxy references, so the use of 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().
32.5.6 [atomics.wait] p4 requires that the thread that calls 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.
However, both 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.
The names 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>.
This question becomes more important as header units arrive with modules, where there is an explicit choice whether to re-export names from subordinate headers.
[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?
Perhaps this means "value-initialized" or "default-initialized" instead of merely "initialized"?
[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.
For a non-empty 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:
regex_errorif the sequence[first, last)is not a valid regular expression.-18- Effects: Constructs an object of class
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.7.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
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.Looking at random through the library wording, the first case I found: 23.6.3.2 [queue.cons]/1:
Effects: Initializes
cwithcont.The meaning of this depends on whether this is direct- or copy-initialization. (It's obscure, but if
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.)Another random sample: 26.10.7 [partial.sum]/2:
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:
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
advanceand leaveprevas status quo" middle ground.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. ]
Modify 24.4.3 [iterator.operations] as indicated:
template<class InputIterator, class Distance> constexpr void advance(InputIterator& i, Distance n);-2- Expects:
nis negative only for bidirectional iterators.-3- Effects: Increments
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
Idoes not modelbidirectional_iterator,nis not negative.-2- Effects:
(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: Ready Submitter: Stephan T. Lavavej Opened: 2019-12-06 Last modified: 2026-06-10
Priority: 3
View all other issues in [locale].
View all issues with Ready 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.
[Brno 2026-06-10; Change status: New → Ready.]
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
other, replacing the current value.-15- Returns:
*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 in randint 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
gbe the per-thread engine. The first form setsgto an unpredictable state. The second form invokesg.seed(value).-8- Postconditions: Subsequent calls to
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:
Definitions in both files are done the same way (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 namespace std 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.integral_constant<size_t, TS::value>-5- Access checking is performed as if in a context unrelated to
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:
I < sizeof...(Types).-?- Type:
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.7 [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) of std::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: SG6 Submitter: Ahti Leppänen Opened: 2020-02-17 Last modified: 2026-04-13
Priority: 3
View all other issues in [rand.dist.bern.negbin].
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 of negative_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.
Problems with the added note:
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.
For these reasons the note added by resolution of LWG 2406 seems invalid and could be considered for
removal. However given the current status and history regarding handling of case 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 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.
The note in 32.5.7.2 [atomics.ref.ops]/2 does mention that lock-free operations
may require higher alignment, but it does not provide guidance to the implementations
so that 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.
To avoid the confusion, I propose to remove the sentence. The rest of the note can stay intact. However, this second edit is less important than the first one and can be omitted in case of disagreement.
[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
Abe 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.-2- [Note: Hardware could require an object referenced by an
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.)
We should revise the specifications to add relevant sources of exceptions.
The functions that should probably mention allocations are:
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
Proposed wording: None yet. Maybe insert something like "thrown by unspecified, internal bookkeeping logic" into each Throws: element, but perhaps something more specific is necessary.
[2020-04-04 Issue Prioritization]
Priority to 3 after reflector discussion.
Proposed resolution:
<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.
I have confirmed with the paper author that the intended design was to deprecate these too, but currently we have no wording.
[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.24.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
Rbe the typeremove_cv_t<T>, andUbe the typeremove_cvref_t<T>.-5- Mandates: For the first overload,
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<.TR>(std::move(*any_cast<U>(&operand)))-7- Throws:
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: 2026-06-09
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.
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
Rberemove_cv_t<T>.-17- Mandates:
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
Rberemove_cv_t<T>.-19- Mandates:
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.
[2025-11-19; This would be resolved by 4406(i).]
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
Rberemove_cv_t<T>.-15- Mandates:
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
Rberemove_cv_t<T>.-17- Mandates:
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
Rberemove_cv_t<T>.-16- Mandates:
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
Rberemove_cv_t<T>.-18- Mandates:
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 along
multimap<K, T, C1, A> multimap<K, T, C2, A>
(i.e. multimaps with differing Compare functions) are missing from the table.
(Same for 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".
There is no general specification for template parameters named "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.
In addition to that, the 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
basic_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).-?- Remarks: This function shall not participate in overload resolution unless the expression
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
rdbuf()->connect(std::forward<Args>(args)...). If that function returns a null pointer, callssetstate(failbit)(which may throwios_base::failure).-?- Remarks: This function shall not participate in overload resolution unless the expression
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>>; […]
std::from_chars is underspecifiedSection: 28.2.3 [charconv.from.chars] Status: New Submitter: Jonathan Wakely Opened: 2020-06-23 Last modified: 2026-04-07
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.
When 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]
[2026-04-07; P4168R0 addresses this]
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:
fmthas the value of one of the enumerators ofchars_format.-7- Effects: The pattern is the expected form of the subject sequence in the "C" locale, as described for
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:
fmthas the value of one of the enumerators ofchars_format.-7- Effects:
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 when To 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.
According to the standard, the following program is ill-formed as it
requires 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.
These constrains were introduced by P0574R1.
[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
Ubethe value type ofdecltype(first)decay_t<decltype(unary_op(*first))>.-2- […]
std::thread's constructor needs to be able to report general memory allocation failuresSection: 32.4.3.4 [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.
It might be theoretically possible to introduce an additional copy and synchronization where the starting thread blocks for the started thread to make a move constructed copy of that tuple from the parameters, but that would introduce unreasonable synchronization overhead since the starting thread would have to block for all TLS initializers and similar in the started thread.
It is technically possible to implement the current design by transforming this allocation failure into
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.4 [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.4 [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.
is_constructible<T[], T...> may be misleading in C++20Section: 21.3.5.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.5.4 [meta.unary.prop] p8,
or do we want to give a more useful/meaningful answer here?
Should we revisit 21.3.5.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 makes array<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_.
Given that the Preconditions element only mentions one of the three preconditions and essentially means that we are not at the end of the stream, we should replace 25.6.6.3 [range.istream.iterator] p2 by:
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:
isparent_->stream_ != nullptr*this != default_sentineltrue.-3- Effects: Equivalent to:
*parent_->stream_ >> parent_->object_; return *this;void operator++(int);-4- Preconditions:
isparent_->stream_ != nullptr*this != default_sentineltrue.-5- Effects: Equivalent to
++*this.Val& operator*() const;-6- Preconditions:
isparent_->stream_ != nullptr*this != default_sentineltrue.-7- Effects: Equivalent to:
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
VTbedecay_t<F>.Constraints:
— […]
—
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.
Since this is a constructor, and we want to give the right answer to
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.
This is similar to LWG 3420(i): in both cases we have a class
(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:
Fis Lvalue-Callable (22.10.17.3.1 [func.wrap.func.general]) for argument typesArgTypes...and return typeR, andis_copy_constructible_v<F>istrue.-9- Preconditions:
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."
It's unclear what "uniquely associated" means. Is it required to be a one-to-one mapping, so that every
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.
By introducing an expository 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
pbeSYNCBUF(os.rdbuf()).-8- Effects: If
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 tobasic_syncbufthat manages itsemit_on_syncflag. — end note]-9- Returns:
os.template<class charT, class traits> basic_ostream<charT, traits>& noemit_on_flush(basic_ostream<charT, traits>& os);Let
pbeSYNCBUF(os.rdbuf()).-10- Effects: If
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(false)-11- Returns:
os.template<class charT, class traits> basic_ostream<charT, traits>& flush_emit(basic_ostream<charT, traits>& os);Let
pbeSYNCBUF(os.rdbuf()).-12- Effects: Calls
os.flush(). Then, ifpis not nullos.rdbuf()a, callsbasic_syncbuf<charT, traits, Allocator>*, calledbuffor the purpose of expositionp.buf->emit()-13- Returns:
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".
This means that 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;
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.
If the scope of the "unique reference" is the "function scope" of the standard
library API, then the semantics of the rvalue reference argument are very similar
to those of C's 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 optimizing vector::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
this argumentthe value within the function's scope and may move from it.[Example ?:
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 form
T&&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 unless hash<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.3 [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.3 [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
P(x, y)be an unspecified total ordering overthread::idas described in 25.8.-7- Returns:
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.3 [thread.thread.id] as indicated:
strong_ordering operator<=>(thread::id x, thread::id y) noexcept;-6- Let
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.-7- Returns:
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 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 other issues in [set.intersection].
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
mergeable[alg.req.mergeable]-1- The
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.
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.9 [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.
For context, analogous operations for 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 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.
A literal reading suggests that any exceptions from any part of that expression should be stored in the
shared state. All of libstdc++, libc++ and MSVC only store exceptions from the call to 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 a
policyargument 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.4 [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 a
policyargument 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.4 [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.
In some cases it might be user observable whether short circuiting happens, as whether later conditions are tested might affect whether errors outside the immediate context are permitted or not.
We should clarify what is intended, and refactor any Constraints: elements that are special cases where something different is required.
[2021-09-20; Reflector poll]
Set priority to 3 after reflector poll.
Proposed resolution:
Section: 17.12.3 [cmp.common] Status: Ready Submitter: Peter Brett Opened: 2021-08-23 Last modified: 2026-06-15
Priority: 3
View all issues with Ready 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.
[Brno 2026-06-12; Status changed: New → Ready.]
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.
However, in this situation 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:
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.
We should remove this constraint and be consistent with the 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:
is_same_v<U, Iterator>isfalse,andconst U&modelsconvertible_to<Iterator>,assignable_from<Iterator&, const U&>is modeled.-6- Effects: Assigns
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:
is_same_v<U, Iterator>isfalse,andconst U&modelsconvertible_to<Iterator>,assignable_from<Iterator&, const U&>is modeled.-6- Effects: Assigns
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:
"The interfaces specified in POSIX.1-2017 do not permit the inclusion of a NUL character in an RE or in the string to be matched. If during the operation of a standard utility a NUL is included in the text designated to be matched, that NUL may designate the end of the text string for the purposes of matching."
So does that mean 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