Doc. no. D????
Date: 2018-11-29
Project: Programming Language C++
Reply to: Marshall Clow <lwgchair@gmail.com>

C++ Standard Library Active Issues List (Revision R113)

Revised 2018-11-29 at 03:02:05 UTC

Reference ISO/IEC IS 14882:2014(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:2014(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 List 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/. Requests for further information about this document should include the document number above, reference ISO/IEC 14882:2014(E), and be submitted to Information Technology Industry Council (ITI), 1250 Eye Street NW, Washington, DC 20005.

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 in the comp.std.c++ FAQ.

How to submit an issue

  1. Mail your issue to the author of this list.
  2. Specify a short descriptive title. If you fail to do so, the subject line of your mail will be used as the issue title.
  3. If the "From" on your email is not the name you wish to appear as issue submitter, then specify issue submitter.
  4. Provide a brief discussion of the problem you wish to correct. Refer to the latest working draft or standard using [section.tag] and paragraph numbers where appropriate.
  5. Provide proposed wording. This should indicate exactly how you want the standard to be changed. General solution statements belong in the discussion area. This area contains very clear and specific directions on how to modify the current draft. If you are not sure how to word a solution, you may omit this part. But your chances of a successful issue greatly increase if you attempt wording.
  6. It is not necessary for you to use html markup. However, if you want to, you can <ins>insert text like this</ins> and <del>delete text like this</del>. The only strict requirement is to communicate clearly to the list maintainer exactly how you want your issue to look.
  7. It is not necessary for you to specify other html font/formatting mark-up, but if you do the list maintainer will attempt to respect your formatting wishes (as described by html markup, or other common idioms).
  8. It is not necessary for you to specify open date or last modified date (the date of your mail will be used).
  9. It is not necessary for you to cross reference other issues, but you can if you like. You do not need to form the hyperlinks when you do, the list maintainer will take care of that.
  10. One issue per email is best.
  11. Between the time you submit the issue, and the next mailing deadline (date at the top of the Revision History), you own this issue. You control the content, the stuff that is right, the stuff that is wrong, the format, the misspellings, etc. You can even make the issue disappear if you want. Just let the list maintainer know how you want it to look, and he will try his best to accommodate you. After the issue appears in an official mailing, you no longer enjoy exclusive ownership of it.

Revision History

Issue Status

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++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.

Active Issues


423(i). Effects of negative streamsize in iostreams

Section: 28 [input.output] Status: Open Submitter: Martin Sebor Opened: 2003-09-18 Last modified: 2018-06-24

Priority: Not Prioritized

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.

Move to NAD Future.

[LEWG Kona 2017]

Recommend Open: We agree that we should require N >= 0 for the selected functions

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.]


484(i). Convertible to T

Section: 22.3.5.2 [input.iterators] Status: Open Submitter: Chris Jefferson Opened: 2004-09-16 Last modified: 2018-01-26

Priority: 2

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 unnessary conversion. However all implementations I have seen simply return a bool anyway. Typical implemtations 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, possibly a duplicate.

Marshall to research

[2017-07 Toronto Thurs Issue Prioritization]

Priority 2; same as 2962.

Proposed resolution:

Rationale:

[ San Francisco: ]

Solved by N2758.


523(i). regex case-insensitive character ranges are unimplementable as specified

Section: 29 [re] Status: Open Submitter: Eric Niebler Opened: 2005-07-01 Last modified: 2018-06-24

Priority: Not Prioritized

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.

Proposed resolution:


532(i). Tuple comparison

Section: 19.5.3.8 [tuple.rel], 99 [tr.tuple.rel] Status: LEWG Submitter: David Abrahams Opened: 2005-11-29 Last modified: 2017-02-03

Priority: Not Prioritized

View other active issues in [tuple.rel].

View all other issues in [tuple.rel].

View all issues with LEWG status.

Duplicate of: 348

Discussion:

Where possible, tuple comparison operators <,<=,=>, and > ought to be defined in terms of std::less rather than operator<, in order to support comparison of tuples of pointers.

[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]

[ 2009-10 Santa Cruz: ]

If we solve this for tuple we would have to solve it for pair algorithms, etc. It is too late to do that at this time. Move to NAD Future.

Proposed resolution:

change 6.1.3.5/5 from:

Returns: The result of a lexicographical comparison between t and u. The result is defined as: (bool)(get<0>(t) < get<0>(u)) || (!(bool)(get<0>(u) < get<0>(t)) && ttail < utail), where rtail for some tuple r is a tuple containing all but the first element of r. For any two zero-length tuples e and f, e < f returns false.

to:

Returns: The result of a lexicographical comparison between t and u. For any two zero-length tuples e and f, e < f returns false. Otherwise, the result is defined as: cmp( get<0>(t), get<0>(u)) || (!cmp(get<0>(u), get<0>(t)) && ttail < utail), where rtail for some tuple r is a tuple containing all but the first element of r, and cmp(x,y) is an unspecified function template defined as follows.

Where T is the type of x and U is the type of y:

if T and U are pointer types and T is convertible to U, returns less<U>()(x,y)

otherwise, if T and U are pointer types, returns less<T>()(x,y)

otherwise, returns (bool)(x < y)

[ Berlin: This issue is much bigger than just tuple (pair, containers, algorithms). Dietmar will survey and work up proposed wording. ]

Rationale:

Recommend NAD. This will be fixed with the next revision of concepts.

[ San Francisco: ]

Solved by N2770.


708(i). Locales need to be per thread and updated for POSIX changes

Section: 27 [localization] Status: Tentatively NAD Submitter: Peter Dimov Opened: 2007-07-28 Last modified: 2018-06-24

Priority: Not Prioritized

View all other issues in [localization].

View all issues with Tentatively NAD status.

Discussion:

The POSIX "Extended API Set Part 4,"

http://www.opengroup.org/sib/details.tpl?id=C065

introduces extensions to the C locale mechanism that allow multiple concurrent locales to be used in the same application by introducing a type locale_t that is very similar to std::locale, and a number of _l functions that make use of it.

The global locale (set by setlocale) is now specified to be per- process. If a thread does not call uselocale, the global locale is in effect for that thread. It can install a per-thread locale by using uselocale.

There is also a nice querylocale mechanism by which one can obtain the name (such as "de_DE") for a specific facet, even for combined locales, with no std::locale equivalent.

std::locale should be harmonized with the new POSIX locale_t mechanism and provide equivalents for uselocale and querylocale.

[ Kona (2007): Bill and Nick to provide wording. ]

[ San Francisco: Bill and Nick still intend to provide wording, but this is a part of the task to be addressed by the group that will look into issue 860. ]

[ 2009-07 Frankfurt: ]

It's our intention to stay in sync with WG14. If WG14 makes a decision that requires a change in WG21 the issue will be reopened.

Move to NAD Future.

[LEWG Kona 2017]

Recommend NAD: uselocale() is bad; pass locales around as objects

Proposed resolution:


935(i). clock error handling needs to be specified

Section: 26.7 [time.clock] Status: Tentatively NAD Submitter: Beman Dawes Opened: 2008-11-24 Last modified: 2018-06-24

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

Each of the three clocks specified in Clocks 26.7 [time.clock] provides the member function:

static time_point now();

The semantics specified by Clock requirements 26.3 [time.clock.req] make no mention of error handling. Thus the function may throw bad_alloc or an implementation-defined exception (15.5.5.12 [res.on.exception.handling] paragraph 4).

Some implementations of these functions on POSIX, Windows, and presumably on other operating systems, may fail in ways only detectable at runtime. Some failures on Windows are due to supporting chipset errata and can even occur after successful calls to a clock's now() function.

These functions are used in cases where exceptions are not appropriate or where the specifics of the exception or cause of error need to be available to the user. See N2828, Library Support for hybrid error handling (Rev 1), for more specific discussion of use cases. Thus some change in the interface of now is required.

The proposed resolution has been implemented in the Boost version of the chrono library. No problems were encountered.

[ Batavia (2009-05): ]

We recommend this issue be deferred until the next Committee Draft has been issued and the prerequisite paper has been accepted.

Move to Open.

[ 2009-10 Santa Cruz: ]

Mark as NAD future. Too late to make this change without having already accepted the hybrid error handling proposal.

[LEWG Kona 2017]

Recommend NAD. Needs a paper. Proposed resolution no longer applies.

Proposed resolution:

Accept the proposed wording of N2828, Library Support for hybrid error handling (Rev 1).

Change Clock requirements 26.3 [time.clock.req] as indicated:

-2- In Table 55 C1 and C2 denote clock types. t1 and t2 are values returned by C1::now() where the call returning t1 happens before (1.10) the call returning t2 and both of these calls happen before C1::time_point::max(). ec denotes an object of type error_code (18.5.3.1 [syserr.errcode.overview]).

Table 55 — Clock requirements
ExpressionReturn typeOperational semantics
... ... ...
C1::now() C1::time_point Returns a time_point object representing the current point in time.
C1::now(ec) C1::time_point Returns a time_point object representing the current point in time.

Change class system_clock 26.7.1 [time.clock.system] as indicated:

static time_point now(error_code& ec=throws());

Change class monotonic_clock 99 [time.clock.monotonic] as indicated:

static time_point now(error_code& ec=throws());

Change class high_resolution_clock 26.7.7 [time.clock.hires] as indicated:

static time_point now(error_code& ec=throws());

936(i). Mutex type overspecified

Section: 31.4.3 [thread.mutex.requirements] Status: LEWG Submitter: Pete Becker Opened: 2008-12-05 Last modified: 2017-03-05

Priority: Not Prioritized

View other active issues in [thread.mutex.requirements].

View all other issues in [thread.mutex.requirements].

View all issues with LEWG status.

Duplicate of: 961

Discussion:

31.4.3 [thread.mutex.requirements] describes the requirements for a type to be a "Mutex type". A Mutex type can be used as the template argument for the Lock type that's passed to condition_variable_any::wait (although Lock seems like the wrong name here, since Lock is given a different formal meaning in 31.4.4 [thread.lock]) and, although the WD doesn't quite say so, as the template argument for lock_guard and unique_lock.

The requirements for a Mutex type include:

Also, a Mutex type "shall not be copyable nor movable".

The latter requirement seems completely irrelevant, and the three requirements on return types are tighter than they need to be. For example, there's no reason that lock_guard can't be instantiated with a type that's copyable. The rule is, in fact, that lock_guard, etc. won't try to copy objects of that type. That's a constraint on locks, not on mutexes. Similarly, the requirements for void return types are unnecessary; the rule is, in fact, that lock_guard, etc. won't use any returned value. And with the return type of bool, the requirement should be that the return type is convertible to bool.

[ Summit: ]

Move to open. Related to conceptualization and should probably be tackled as part of that.

[ Post Summit Anthony adds: ]

Section 31.4.3 [thread.mutex.requirements] conflates the requirements on a generic Mutex type (including user-supplied mutexes) with the requirements placed on the standard-supplied mutex types in an attempt to group everything together and save space.

When applying concepts to chapter 30, I suggest that the concepts Lockable and TimedLockable embody the requirements for *use* of a mutex type as required by unique_lock/lock_guard/condition_variable_any. These should be relaxed as Pete describes in the issue. The existing words in 31.4.3 [thread.mutex.requirements] are requirements on all of std::mutex, std::timed_mutex, std::recursive_mutex and std::recursive_timed_mutex, and should be rephrased as such.

[2017-03-01, Kona]

SG1: Agreement that we need a paper.

Proposed resolution:


961(i). Various threading bugs #11

Section: 31.4.3 [thread.mutex.requirements] Status: LEWG Submitter: Pete Becker Opened: 2009-01-07 Last modified: 2017-03-05

Priority: Not Prioritized

View other active issues in [thread.mutex.requirements].

View all other issues in [thread.mutex.requirements].

View all issues with LEWG status.

Duplicate of: 936

Discussion:

31.4.3 [thread.mutex.requirements] describes required member functions of mutex types, and requires that they throw exceptions under certain circumstances. This is overspecified. User-defined types can abort on such errors without affecting the operation of templates supplied by standard-library.

[ Summit: ]

Move to open. Related to conceptualization and should probably be tackled as part of that.

[ 2009-10 Santa Cruz: ]

Would be OK to leave it as is for time constraints, could loosen later.

Mark as NAD Future.

[2017-03-01, Kona]

SG1: Agreement that we need a paper.

Proposed resolution:


1025(i). The library should provide more specializations for std::hash

Section: 19.14.18 [unord.hash] Status: LEWG Submitter: Alisdair Meredith Opened: 2009-03-11 Last modified: 2017-02-03

Priority: Not Prioritized

View other active issues in [unord.hash].

View all other issues in [unord.hash].

View all issues with LEWG status.

Discussion:

Addresses UK 208 [CD1]

std::hash should be implemented for much more of the standard library. In particular for pair, tuple and all the standard containers.

Proposed resolution:


1121(i). Support for multiple arguments

Section: 19.16.4 [ratio.arithmetic] Status: Tentatively NAD Submitter: Alisdair Meredith Opened: 2009-05-25 Last modified: 2018-06-24

Priority: Not Prioritized

View all other issues in [ratio.arithmetic].

View all issues with Tentatively NAD status.

Discussion:

Both add and multiply could sensibly be called with more than two arguments. The variadic template facility makes such declarations simple, and is likely to be frequently wrapped by end users if we do not supply the variant ourselves.

We deliberately ignore divide at this point as it is not transitive. Likewise, subtract places special meaning on the first argument so I do not suggest extending that immediately. Both could be supported with analogous wording to that for add/multiply below.

Note that the proposed resolution is potentially incompatible with that proposed for 921, although the addition of the typedef to ratio would be equally useful.

[ 2009-10-30 Alisdair adds: ]

The consensus of the group when we reviewed this in Santa Cruz was that 921 would proceed to Ready as planned, and the multi-paramater add/multiply templates should be renamed as ratio_sum and ratio_product to avoid the problem mixing template aliases with partial specializations.

It was also suggested to close this issue as NAD Future as it does not correspond directly to any NB comment. NBs are free to submit a specific comment (and re-open) in CD2 though.

Walter Brown also had concerns on better directing the order of evaluation to avoid overflows if we do proceed for 0x rather than TR1, so wording may not be complete yet.

[ Alisdair updates wording. ]

[ 2009-10-30 Howard: ]

Moved to Tentatively NAD Future after 5 positive votes on c++std-lib.

[LEWG Kona 2017]

PR for ratio_product is wrong, uses ratio_add instead of ratio_multiply. Recommend NAD: Doesn't meet the bar for standardization: hasn't been requested again in 7 years, easy to implement yourself.

Rationale:

Does not have sufficient support at this time. May wish to reconsider for a future standard.

Proposed resolution:

Add the following type traits to p3 19.16 [ratio]

// ratio arithmetic
template <class R1, class R2> struct ratio_add;
template <class R1, class R2> struct ratio_subtract;
template <class R1, class R2> struct ratio_multiply;
template <class R1, class R2> struct ratio_divide;
template <class R1, class ... RList> struct ratio_sum;
template <class R1, class ... RList> struct ratio_product;

after 19.16.4 [ratio.arithmetic] p1: add

template <class R1, class ... RList> struct ratio_sum; // declared, never defined

template <class R1> struct ratio_sum<R1> : R1 {};

Requires: R1 is a specialization of class template ratio

template <class R1, class R2, class ... RList> 
 struct ratio_sum<R1, R2, RList...>
   : ratio_add< R1, ratio_sum<R2, RList...>> {
};

Requires: R1 and each element in parmater pack RList is a specialization of class template ratio

after 19.16.4 [ratio.arithmetic] p3: add

template <class R1, class ... RList> struct ratio_product; // declared, never defined

template <class R1> struct ratio_product<R1> : R1 {};

Requires: R1 is a specialization of class template ratio

template <class R1, class R2, class ... RList> 
 struct ratio_sum<R1, R2, RList...>
   : ratio_add< R1, ratio_product<R2, RList...>> {
};

Requires: R1 and each element in parmater pack RList is a specialization of class template ratio


1154(i). complex should accept integral types

Section: 25.4 [complex.numbers] Status: Tentatively NAD Submitter: LWG Opened: 2009-06-28 Last modified: 2018-06-24

Priority: Not Prioritized

View other active issues in [complex.numbers].

View all other issues in [complex.numbers].

View all issues with Tentatively NAD status.

Discussion:

Addresses FR 35

Description

Instantiations of the class template complex<> have to be allowed for integral types, to reflect existing practice and ISO standards (LIA-III).

Suggestion

[ 2009-10-26 Proposed wording in N3002. ]

[ 2010 Pittsburgh: ]

Moved to NAD Future. Rationale added.

[LEWG Kona 2017]

Recommend SG6

[2017-03-03, Kona]

SG6 suggests this issue is a new feature, not a problem with the existing standard, and should therefore be closed NAD. However, SG6 invites papers that bring the proposal up to date with the current standard.

Rationale:

There is no consensus for making this change at this time.

Proposed resolution:

Adopt N3002.


1175(i). unordered complexity

Section: 21.2.7 [unord.req] Status: Open Submitter: Pablo Halpern Opened: 2009-07-17 Last modified: 2017-02-03

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 n calls to construct value_type from InputIterator::value_type (where n = distance(f,l)). The number of calls to key_equal::operator() is proportional to n in the average case and n*n in 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.

Proposed resolution:

  1. 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 least n
    buckets, using hf as the hash function and eq as the key
    equality predicate, and inserts elements from [i, j) into it.
    Average case 𝒪(n + N) (N is distance(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 least n
    buckets, using hf as the hash function and key_equal() as the key
    equality predicate, and inserts elements from [i, j) into it.
    Average case 𝒪(n + N) (N is distance(i, j)),
    worst case 𝒪(n + N2)
    X(i, j, n)
    X a(i, j, n)
    X
    Effects: Constructs an empty container with at least n
    buckets, using hasher() as the hash function and key_equal() as the key
    equality predicate, and inserts elements from [i, j) into it.
    Average case 𝒪(n + N) (N is distance(i, j)),
    worst case 𝒪(n + N2)
  2. Modify 21.5.4.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_map using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in unordered_map. max_load_factor() returns 1.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_map using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in unordered_map. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.

    4 Complexity: Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to number of elements in the range [f,l).

  3. Modify 21.5.5.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_multimap using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in unordered_multimap. max_load_factor() returns 1.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_multimap using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in unordered_multimap. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.

    4 Complexity: Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to number of elements in the range [f,l).

  4. Modify 21.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_set using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in unordered_set. max_load_factor() returns 1.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_set using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in unordered_set. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.

    4 Complexity: Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to number of elements in the range [f,l).

  5. Modify 21.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_multiset using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in unordered_multiset. max_load_factor() returns 1.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_multiset using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecifiedimpldefdefault number of buckets in unordered_multiset. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.

    4 Complexity: Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to number of elements in the range [f,l).


1188(i). Unordered containers should have a minimum load factor as well as a maximum

Section: 21.2.7 [unord.req], 21.5 [unord] Status: Tentatively NAD Submitter: Matt Austern Opened: 2009-08-10 Last modified: 2018-06-21

Priority: Not Prioritized

View other active issues in [unord.req].

View all other issues in [unord.req].

View all issues with Tentatively NAD status.

Discussion:

Unordered associative containers have a notion of a maximum load factor: when the number of elements grows large enough, the containers automatically perform a rehash so that the number of elements per bucket stays below a user-specified bound. This ensures that the hash table's performance characteristics don't change dramatically as the size increases.

For similar reasons, Google has found it useful to specify a minimum load factor: when the number of elements shrinks by a large enough, the containers automatically perform a rehash so that the number of elements per bucket stays above a user-specified bound. This is useful for two reasons. First, it prevents wasting a lot of memory when an unordered associative container grows temporarily. Second, it prevents amortized iteration time from being arbitrarily large; consider the case of a hash table with a billion buckets and only one element. (This was discussed even before TR1 was published; it was TR issue 6.13, which the LWG closed as NAD on the grounds that it was a known design feature. However, the LWG did not consider the approach of a minimum load factor.)

The only interesting question is when shrinking is allowed. In principle the cleanest solution would be shrinking on erase, just as we grow on insert. However, that would be a usability problem; it would break a number of common idioms involving erase. Instead, Google's hash tables only shrink on insert and rehash.

The proposed resolution allows, but does not require, shrinking in rehash, mostly because a postcondition for rehash that involves the minimum load factor would be fairly complicated. (It would probably have to involve a number of special cases and it would probably have to mention yet another parameter, a minimum bucket count.)

The current behavior is equivalent to a minimum load factor of 0. If we specify that 0 is the default, this change will have no impact on backward compatibility.

[ 2010 Rapperswil: ]

This seems to a useful extension, but is too late for 0x. Move to Tentatively NAD Future.

[ Moved to NAD Future at 2010-11 Batavia ]

[LEWG Kona 2017]

Should there be a shrink_to_fit()? Is it too surprising to shrink on insert()? (We understand that shrinking on erase() is not an option.) Maybe make people call rehash(0) to shrink to the min_load_factor? On clear(), the load factor goes to 0 or undefined (0/0), which is likely to violate min_load_factor() min_load_factor(z)'s wording should match max_load_factor(z)'s, e.g. "May change the container’s maximum load factor" Want a paper exploring whether shrink-on-insert has been surprising. From Titus: Google's experience is that maps don't shrink in the way this would help with. NAD, not worth the time. Write a paper if you can demonstrate a need for this.

Proposed resolution:

Add two new rows, and change rehash's postcondition in the unordered associative container requirements table in 21.2.7 [unord.req]:

Table 87 — Unordered associative container requirements (in addition to container)
ExpressionReturn typeAssertion/note pre-/post-condition Complexity
a.min_load_factor() float Returns a non-negative number that the container attempts to keep the load factor greater than or equal to. The container automatically decreases the number of buckets as necessary to keep the load factor above this number. constant
a.min_load_factor(z) void Pre: z shall be non-negative. Changes the container's minimum load factor, using z as a hint. [Footnote: the minimum load factor should be significantly smaller than the maximum. If z is too large, the implementation may reduce it to a more sensible value.] constant
a.rehash(n) void Post: a.bucket_count() >= n, and a.size() <= a.bucket_count() * a.max_load_factor(). [Footnote: It is intentional that the postcondition does not mention the minimum load factor. This member function is primarily intended for cases where the user knows that the container's size will increase soon, in which case the container's load factor will temporarily fall below a.min_load_factor().] a.bucket_cout > a.size() / a.max_load_factor() and a.bucket_count() >= n. Average case linear in a.size(), worst case quadratic.

Add a footnote to 21.2.7 [unord.req] p12:

The insert members shall not affect the validity of references to container elements, but may invalidate all iterators to the container. The erase members shall invalidate only iterators and references to the erased elements.

[A consequence of these requirements is that while insert may change the number of buckets, erase may not. The number of buckets may be reduced on calls to insert or rehash.]

Change paragraph 13:

The insert members shall not affect the validity of iterators if (N+n) < z * B zmin * B <= (N+n) <= zmax * B, where N is the number of elements in the container prior to the insert operation, n is the number of elements inserted, B is the container's bucket count, zmin is the container's minimum load factor, and zmax is the container's maximum load factor.

Add to the unordered_map class synopsis in section 21.5.4 [unord.map], the unordered_multimap class synopsis in 21.5.5 [unord.multimap], the unordered_set class synopsis in 21.5.6 [unord.set], and the unordered_multiset class synopsis in 21.5.7 [unord.multiset]:


float min_load_factor() const;
void min_load_factor(float z);

In 21.5.4.2 [unord.map.cnstr], 21.5.5.2 [unord.multimap.cnstr], 21.5.6.2 [unord.set.cnstr], and 21.5.7.2 [unord.multiset.cnstr], change:

... max_load_factor() returns 1.0 and min_load_factor() returns 0.


1203(i). More useful rvalue stream insertion

Section: 28.7.5.5 [ostream.rvalue], 28.7.4.5 [istream.rvalue] Status: Open Submitter: Howard Hinnant Opened: 2009-09-06 Last modified: 2018-06-24

Priority: Not Prioritized

View all other issues in [ostream.rvalue].

View all issues with Open status.

Discussion:

28.7.5.5 [ostream.rvalue] was created to preserve the ability to insert into (and extract from 28.7.4.5 [istream.rvalue]) rvalue streams:

template <class charT, class traits, class T>
  basic_ostream<charT, traits>&
  operator<<(basic_ostream<charT, traits>&& os, const T& x);

1 Effects: os << x

2 Returns: os

This is good as it allows code that wants to (for example) open, write to, and close an ofstream all in one statement:

std::ofstream("log file") << "Some message\n";

However, I think we can easily make this "rvalue stream helper" even easier to use. Consider trying to quickly create a formatted string. With the current spec you have to write:

std::string s = static_cast<std::ostringstream&>(std::ostringstream() << "i = " << i).str();

This will store "i = 10" (for example) in the string s. Note the need to cast the stream back to ostringstream& prior to using the member .str(). This is necessary because the inserter has cast the ostringstream down to a more generic ostream during the insertion process.

I believe we can re-specify the rvalue-inserter so that this cast is unnecessary. Thus our customer now has to only type:

std::string s = (std::ostringstream() << "i = " << i).str();

This is accomplished by having the rvalue stream inserter return an rvalue of the same type, instead of casting it down to the base class. This is done by making the stream generic, and constraining it to be an rvalue of a type derived from ios_base.

The same argument and solution also applies to the inserter. This code has been implemented and tested.

[ 2009 Santa Cruz: ]

NAD Future. No concensus for change.

[LEWG Kona 2017]

Recommend Open: Design looks right.

[ 2018-05-25, Billy O'Neal requests this issue be reopened and provides P/R rebased against N4750 ]

Billy O'Neal requests this issue be reopened, as changing operator>> and operator<< to use perfect forwarding as described here is necessary to implement LWG 2534 which was accepted. Moreover, this P/R also resolves LWG 2498.

Previous resolution [SUPERSEDED]:

Change 28.7.4.5 [istream.rvalue]:

template <class charT, class traits Istream, class T>
  basic_istream<charT, traits>& Istream&&
  operator>>(basic_istream<charT, traits> Istream&& is, T& x);

1 Effects: is >> x

2 Returns: std::move(is)

3 Remarks: This signature shall participate in overload resolution if and only if Istream is not an lvalue reference type and is derived from ios_base.

Change 28.7.5.5 [ostream.rvalue]:

template <class charT, class traits Ostream, class T>
  basic_ostream<charT, traits>& Ostream&&
  operator<<(basic_ostream<charT, traits> Ostream&& os, const T& x);

1 Effects: os << x

2 Returns: std::move(os)

3 Remarks: This signature shall participate in overload resolution if and only if Ostream is not an lvalue reference type and is derived from ios_base.

Proposed resolution:

This resolution is relative to N4750.

Change 28.7.4.5 [istream.rvalue] as follows:

template <class charT, class traits Istream, class T>
  basic_istream<charT, traits>& Istream&&
  operator>>(basic_istream<charT, traits> Istream&& is, T&& x);

-1- Effects: Equivalent to:

is >> std::forward<T>(x)
return std::move(is);

-2- Remarks: This function shall not participate in overload resolution unless the expression is >> std::forward<T>(x) is well-formed, Istream is not an lvalue reference type, and Istream is derived from ios_base.

Change 28.7.5.5 [ostream.rvalue]:

template <class charT, class traits Ostream, class T>
  basic_ostream<charT, traits>& Ostream&&
  operator<<(basic_ostream<charT, traits> Ostream&& os, const T& x);

-1- Effects: As if by: os << x;

-2- Returns: std::move(os)

-3- Remarks: This signature shall not participate in overload resolution unless the expression os << x is well-formed, Ostream is not an lvalue reference type, and Ostream is derived from ios_base.


1213(i). Meaning of valid and singular iterator underspecified

Section: 22.3 [iterator.requirements] Status: Open Submitter: Daniel Krügler Opened: 2009-09-19 Last modified: 2017-02-03

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 (including further updates by 278). In 22.3 [iterator.requirements] as of N2723 the standard says now:

5 - These values are called past-the-end values. Values of an iterator i for which the expression *i is 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 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. 19.10.11 [specialized.algorithms]/1 says:

In all of the following algorithms, the formal template parameter ForwardIterator is 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: p shall be a non-singular pointer value obtained from a call to allocate() 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:


1217(i). Quaternion support

Section: 25.4 [complex.numbers] Status: Tentatively NAD Submitter: Ted Shaneyfelt Opened: 2009-09-26 Last modified: 2018-06-24

Priority: Not Prioritized

View other active issues in [complex.numbers].

View all other issues in [complex.numbers].

View all issues with Tentatively NAD status.

Discussion:

Concerning mathematically proper operation of the type:

complex<complex<T> >

Generally accepted mathematical semantics of such a construct correspond to quaternions through Cayly-Dickson construct

(w+xi) + (y+zi) j

The proper implementation seems straightforward by adding a few declarations like those below. I have included operator definition for combining real scalars and complex types, as well, which seems appropriate, as algebra of complex numbers allows mixing complex and real numbers with operators. It also allows for constructs such as complex<double> i=(0,1), x = 12.34 + 5*i;

Quaternions are often used in areas such as computer graphics, where, for example, they avoid the problem of Gimbal lock when rotating objects in 3D space, and can be more efficient than matrix multiplications, although I am applying them to a different field.

/////////////////////////ALLOW OPERATORS TO COMBINE REAL SCALARS AND COMPLEX VALUES /////////////////////////
template<typename T,typename S> complex<T> operator+(const complex<T> x,const S a) {
    complex<T> result(x.real()+a, x.imag());
    return result;
}
template<typename T,typename S> complex<T> operator+(const S a,const complex<T> x) {
    complex<T> result(a+x.real(), x.imag());
    return result;
}
template<typename T,typename S> complex<T> operator-(const complex<T> x,const S a) {
    complex<T> result(x.real()-a, x.imag());
    return result;
}
template<typename T,typename S> complex<T> operator-(const S a,const complex<T> x) {
    complex<T> result(a-x.real(), x.imag());
    return result;
}
template<typename T,typename S> complex<T> operator*(const complex<T> x,const S a) {
    complex<T> result(x.real()*a, x.imag()*a);
    return result;
}
template<typename T,typename S> complex<T> operator*(const S a,const complex<T> x) {
    complex<T> result(a*x.real(), a*x.imag());
    return result;
}

/////////////////////////PROPERLY IMPLEMENT QUATERNION SEMANTICS/////////////////////////
template<typename T> double normSq(const complex<complex<T> >q) {
    return q.real().real()*q.real().real()
         + q.real().imag()*q.real().imag()
         + q.imag().real()*q.imag().real()
         + q.imag().imag()*q.imag().imag();
}
template<typename T> double norm(const complex<complex<T> >q) {
    return sqrt(normSq(q));
}
/////// Cayley-Dickson Construction
template<typename T> complex<complex<T> > conj(const complex<complex<T> > x) {
    complex<complex<T> > result(conj(x.real()),-x.imag());
    return result;
}
template<typename T> complex<complex<T> > operator*(const complex<complex<T> > ab,const complex<complex<T> > cd) {
    complex<T> re(ab.real()*cd.real()-conj(cd.imag())*ab.imag());
    complex<T> im(cd.imag()*ab.real()+ab.imag()*conj(cd.real()));
    complex<complex<double> > q(re,im);
    return q;
}
//// Quaternion division
template<typename S,typename T> complex<complex<T> > operator/(const complex<complex<T> > q,const S a) {
    return q * (1/a);
}
template<typename S,typename T> complex<complex<T> > operator/(const S a,const complex<complex<T> > q) {
    return a*conj(q)/normSq(q);
}
template<typename T> complex<complex<T> > operator/(const complex<complex<T> > n, const complex<complex<T> > d) {
    return n * (conj(d)/normSq(d));
}

[ 2009-10 Santa Cruz: ]

NAD Future. There is no consensus or time to move this into C++0X.

[LEWG Kona 2017]

Recommend SG6 - We note that complex<complex> is the wrong way to spell this

[2017-03-03, Kona]

SG6 suggests this issue is a new feature, not a problem with the existing standard, and should therefore be closed NAD. However, SG6 invites papers that bring the proposal up to date with the current standard.

Proposed resolution:


1235(i). Issue with C++0x random number proposal

Section: 25.6.2.6 [rand.req.dist] Status: Tentatively NAD Submitter: Matthias Troyer Opened: 2009-10-12 Last modified: 2018-06-21

Priority: Not Prioritized

View all other issues in [rand.req.dist].

View all issues with Tentatively NAD status.

Discussion:

There exist optimized, vectorized vendor libraries for the creation of random number generators, such as Intel's MKL [1] and AMD's ACML [2]. In timing tests we have seen a performance gain of a factor of up to 80 (eighty) compared to a pure C++ implementation (in Boost.Random) when using these generator to generate a sequence of normally distributed random numbers. In codes dominated by the generation of random numbers (we have application codes where random number generation is more than 50% of the CPU time) this factor 80 is very significant.

To make use of these vectorized generators, we use a C++ class modeling the RandomNumberEngine concept and forwarding the generation of random numbers to those optimized generators. For example:

namespace mkl {
 class mt19937 {.... };
}

For the generation of random variates we also want to dispatch to optimized vectorized functions in the MKL or ACML libraries. See this example:

mkl::mt19937 eng;
std::normal_distribution<double> dist;

double n = dist(eng);

Since the variate generation is done through the operator() of the distribution there is no customization point to dispatch to Intel's or AMD's optimized functions to generate normally distributed numbers based on the mt19937 generator. Hence, the performance gain of 80 cannot be achieved.

Contrast this with TR1:

mkl::mt19937 eng;
std::tr1::normal_distribution<double> dist;
std::tr1::variate_generator<mkl::mt19937,std::tr1::normal_distribution<double> > rng(eng,dist);
double n = rng();

This - admittedly much uglier from an aestethic point of view - design allowed optimization by specializing the variate_generator template for mkl::mt19937:

namespace std { namespace tr1 {

template<>
class variate_generator<mkl::mt19937,std::tr1::normal_distribution<double> > { .... };

} }

A similar customization point is missing in the C++0x design and prevents the optimized vectorized version to be used.

Suggested resolution:

Add a customization point to the distribution concept. Instead of the variate_generator template this can be done through a call to a free function generate_variate found by ADL instead of operator() of the distribution:

template <RandomNumberDistribution, class RandomNumberEngine>
typename RandomNumberDistribution ::result_type
generate_variate(RandomNumberDistribution const& dist, RandomNumberEngine& eng);

This function can be overloaded for optimized enginges like mkl::mt19937.

[ 2009-10 Santa Cruz: ]

NAD Future. No time to add this feature for C++0X.

[LEWG Kona 2017]

Recommend NAD: The standard has changed enough that the issue doesn't make sense anymore. Write a paper proposing a way to get this performance as changes to the current library.

Proposed resolution:


1238(i). Defining algorithms taking iterator for range

Section: 24 [algorithms] Status: Open Submitter: Alisdair Meredith Opened: 2009-10-15 Last modified: 2018-06-24

Priority: Not Prioritized

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

Rationale:

Does not have sufficient support at this time. May wish to reconsider for a future standard.

Proposed resolution:


1282(i). A proposal to add std::split algorithm

Section: 24 [algorithms] Status: Tentatively NAD Submitter: Igor Semenov Opened: 2009-12-07 Last modified: 2018-06-24

Priority: Not Prioritized

View other active issues in [algorithms].

View all other issues in [algorithms].

View all issues with Tentatively NAD status.

Discussion:

  1. Motivation and Scope

    Splitting strings into parts by some set of delimiters is an often task, but there is no simple and generalized solution in C++ Standard. Usually C++ developers use std::basic_stringstream<> to split string into parts, but there are several inconvenient restrictions:

  2. Impact on the Standard

    This algorithm doesn't interfere with any of current standard algorithms.

  3. Design Decisions

    This algorithm is implemented in terms of input/output iterators. Also, there is one additional wrapper for const CharType * specified delimiters.

  4. Example implementation

    template< class It, class DelimIt, class OutIt >
    void split( It begin, It end, DelimIt d_begin, DelimIt d_end, OutIt out )
    {
       while ( begin != end )
       {
           It it = std::find_first_of( begin, end, d_begin, d_end );
           *out++ = std::make_pair( begin, it );
           begin = std::find_first_of( it, end, d_begin, d_end,
               std::not2( std::equal_to< typename It::value_type >() ) );
       }
    }
    
    template< class It, class CharType, class OutIt >
    void split( It begin, It end, const CharType * delim, OutIt out )
    {
       split( begin, end, delim, delim + std::strlen( delim ), out );
    }
    
  5. Usage

    std::string ss( "word1 word2 word3" );
    std::vector< std::pair< std::string::const_iterator, std::string::const_iterator > > v;
    split( ss.begin(), ss.end(), " ", std::back_inserter( v ) );
    
    for ( int i = 0; i < v.size(); ++i )
    {
       std::cout << std::string( v[ i ].first, v[ i ].second ) << std::endl;
    }
    // word1
    // word2
    // word3
    

[ 2010-01-22 Moved to Tentatively NAD Future after 5 positive votes on c++std-lib. Rationale added below. ]

[LEWG Kona 2017]

Recommend NAD: Paper encouraged. Have papers for this; LEWG259.

Rationale:

The LWG is not considering completely new features for standardization at this time. We would like to revisit this good suggestion for a future TR and/or standard.

Proposed resolution:

Add to the synopsis in 24.1 [algorithms.general]:

template< class ForwardIterator1, class ForwardIterator2, class OutputIterator >
  void split( ForwardIterator1 first, ForwardIterator1 last,
              ForwardIterator2 delimiter_first, ForwardIterator2 delimiter_last,
              OutputIterator result );

template< class ForwardIterator1, class CharType, class OutputIterator >
  void split( ForwardIterator1 first, ForwardIterator1 last,
              const CharType * delimiters, OutputIterator result );

Add a new section [alg.split]:

template< class ForwardIterator1, class ForwardIterator2, class OutputIterator >
  void split( ForwardIterator1 first, ForwardIterator1 last,
              ForwardIterator2 delimiter_first, ForwardIterator2 delimiter_last,
              OutputIterator result );

1. Effects: splits the range [first, last) into parts, using any element of [delimiter_first, delimiter_last) as a delimiter. Results are pushed to output iterator in the form of std::pair<ForwardIterator1, ForwardIterator1>. Each of these pairs specifies a maximal subrange of [first, last) which does not contain a delimiter.

2. Returns: nothing.

3. Complexity: Exactly last - first assignments.

template< class ForwardIterator1, class CharType, class OutputIterator >
  void split( ForwardIterator1 first, ForwardIterator1 last,
              const CharType * delimiters, OutputIterator result );

1. Effects: split the range [first, last) into parts, using any element of delimiters (interpreted as zero-terminated string) as a delimiter. Results are pushed to output iterator in the form of std::pair<ForwardIterator1, ForwardIterator1>. Each of these pairs specifies a maximal subrange of [first, last) which does not contain a delimiter.

2. Returns: nothing.

3. Complexity: Exactly last - first assignments.


1289(i). Generic casting requirements for smart pointers

Section: 19.2 [utility] Status: Tentatively NAD Submitter: Ion Gaztañaga Opened: 2009-12-14 Last modified: 2018-06-21

Priority: Not Prioritized

View all other issues in [utility].

View all issues with Tentatively NAD status.

Discussion:

In section 15.5.3.5 [allocator.requirements], Table 40 — Allocator requirements, the following expression is required for allocator pointers:

Table 40 — Allocator requirements
Expression Return type Assertion/note
pre-/post-condition
Default
static_cast<X::pointer>(w) X::pointer static_cast<X::pointer>(w) == p  

To achieve this expression, a smart pointer writer must introduce an explicit conversion operator from smart_ptr<void> to smart_ptr<T> so that static_cast<pointer>(void_ptr) is a valid expression. Unfortunately this explicit conversion weakens the safety of a smart pointer since the following expression (invalid for raw pointers) would become valid:

smart_ptr<void> smart_v = ...;
smart_ptr<T> smart_t(smart_v);

On the other hand, shared_ptr also defines its own casting functions in 19.11.3.9 [util.smartptr.shared.cast], and although it's unlikely that a programmer will use shared_ptr as allocator::pointer, having two different ways to do the same cast operation does not seem reasonable. A possible solution would be to replace static_cast<X::pointer>(w) expression with a user customizable (via ADL) static_pointer_cast<value_type>(w), and establish the xxx_pointer_cast functions introduced by shared_ptr as the recommended generic casting utilities of the standard.

Unfortunately, we've experienced problems in Boost when trying to establish xxx_pointer_cast as customization points for generic libraries (http://objectmix.com/c/40424-adl-lookup-explicit-template-parameters.html) because these casting functions are called with explicit template parameters and the standard says in 12.9.1 [temp.arg.explicit] p.8 "Explicit template argument specification":

8 ...But when a function template with explicit template arguments is used, the call does not have the correct syntactic form unless there is a function template with that name visible at the point of the call. If no such name is visible, the call is not syntactically well-formed and argument-dependent lookup does not apply.

So we can do this:

template<class BasePtr>
void generic_ptr_swap(BasePtr p)
{
  //ADL customization point
  swap(p, p);
  //...
}

but not the following:

template<class BasePtr>
void generic_ptr_algo(BasePtr p)
{
  typedef std::pointer_traits<BasePtr>::template
     rebind<Derived> DerivedPtr;
  DerivedPtr dp = static_pointer_cast<Derived>(p);
}

The solution to make static_pointer_cast a customization point is to add a generic declaration (no definition) of static_pointer_cast in a namespace (like std) and apply "using std::static_pointer_cast" declaration to activate ADL:

namespace std{

template<typename U, typename T>
unspecified
static_pointer_cast(T&&) = delete;

}

template<class BasePtr>
void generic_ptr_algo(BasePtr p)
{
  typedef std::pointer_traits<BasePtr>::template
     rebind<Derived> DerivedPtr;

  //ADL applies because static_pointer_cast is made
  //  visible according to [temp.arg.explicit]/8
  using std::static_pointer_cast;

  DerivedPtr dp = static_pointer_cast<Derived>(p);

  //...
}

A complete solution will need also the definition of static_pointer_cast for raw pointers, and this definition has been present in Boost (http://www.boost.org/boost/ pointer_cast.hpp) for years.

[ 2010-03-26 Daniel made editorial adjustments to the proposed wording. ]

[ Moved to NAD Future at 2010-11 Batavia ]

This is a new feature rather than a defect. It can be added later: "this is such a hairy area that people will put up with changes"

[LEWG Kona 2017]

Recommend NAD: NAD. Should bring a paper as a proposal for 2020.

Proposed resolution:

Add to section 19.2 [utility] Utility components, Header <utility> synopsis:

// 20.3.X, generic pointer cast functions

template<typename U, typename T>
unspecified
static_pointer_cast(T&&) = delete;

template<typename U, typename T>
unspecified
dynamic_pointer_cast(T&&) = delete;

template<typename U, typename T>
unspecified
const_pointer_cast(T&&) = delete;

//Overloads for raw pointers
template<typename U, typename T>
auto static_pointer_cast(T* t) -> decltype(static_cast<U*>(t));

template<typename U, typename T>
auto dynamic_pointer_cast(T* t) -> decltype(dynamic_cast<U*>(t));

template<typename U, typename T>
auto const_pointer_cast(T* t) -> decltype(const_cast<U*>(t));

Add to section 19.2 [utility] Utility components, a new subclause 20.3.X Pointer cast utilities [pointer.cast]:

20.3.X Pointer cast utilities [pointer.cast]

1 The library defines generic pointer casting function templates so that template code can explicitly make these names visible and activate argument-dependent lookup for pointer cast calls.

//Generic declarations
template<typename U, typename T>
unspecified
static_pointer_cast(T&&) = delete;

template<typename U, typename T>
unspecified
dynamic_pointer_cast(T&&) = delete;

template<typename U, typename T>
unspecified
const_pointer_cast(T&&) = delete;

2 The library also defines overloads of these functions for raw pointers.

//Overloads for raw pointers
template<typename U, typename T>
auto static_pointer_cast(T* t) -> decltype(static_cast<U*>(t));

Returns: static_cast<U*>(t)

template<typename U, typename T>
auto dynamic_pointer_cast(T* t) -> decltype(dynamic_cast<U*>(t));

Returns: dynamic_cast<U*>(t)

template<typename U, typename T>
auto const_pointer_cast(T* t) -> decltype(const_cast<U*>(t));

Returns: const_cast<U*>(t)

[Example:

#include <utility> //static_pointer_cast
#include <memory>  //pointer_traits

class Base{};
class Derived : public Base{};

template<class BasePtr>
void generic_pointer_code(BasePtr b)
{
   typedef std::pointer_traits<BasePtr>::template
      rebind<Derived> DerivedPtr;

   using std::static_pointer_cast;
   //ADL applies now that static_pointer_cast is visible
   DerivedPtr d = static_pointer_cast<Derived>(b);
}

end example]

Replace in section 15.5.3.5 [allocator.requirements] Table 40 — Allocator requirements, the following table entries for allocator pointers:

Table 40 — Allocator requirements
Expression Return type Assertion/note
pre-/post-condition
Default
static_pointer_cast<X::pointerT>(w) X::pointer static_pointer_cast<X::pointerT>(w) == p  
static_pointer_cast<X::const_pointerconst T>(w) X::const_pointer static_pointer_cast<X::const_pointerconst T>(z) == q  

1317(i). make_hash

Section: 19.14.18 [unord.hash] Status: Tentatively NAD Submitter: Nicolai M. Josuttis Opened: 2010-02-10 Last modified: 2018-06-24

Priority: Not Prioritized

View other active issues in [unord.hash].

View all other issues in [unord.hash].

View all issues with Tentatively NAD status.

Discussion:

Currently, the library lacks a convenient way to provide a hash function that can be used with the provided unordered containers to allow the usage of non trivial element types.

While we can easily declare an

std::unordered_set<int>

or

std::unordered_set<std::string>

we have no easy way to declare an unordered_set for a user defined type. IMO, this is a big obstacle to use unordered containers in practice. Note that in Java, the wide usage of HashMap is based on the fact that there is always a default hash function provided.

Of course, a default hash function implies the risk to provide poor hash functions. But often even poor hash functions are good enough.

While I really would like to see a default hash function, I don't propose it here because this would probably introduce a discussion that's too big for this state of C++0x.

However, I strongly suggest at least to provide a convenience variadic template function make_hash<>() to allow an easy definition of a (possibly poor) hash function.

As a consequence for a user-defined type such as

class Customer {
   friend class CustomerHash;
   private:
     string firstname;
     string lastname;
     long   no;
   ...
 };

would allow to specify:

class CustomerHash : public std::unary_function<Customer, std::size_t>
{
  public:
    std::size_t operator() (const Customer& c) const  {
       return make_hash(c.firstname,c.lastname,c.no);
    }
};

instead of:

class CustomerHash : public std::unary_function<Customer, std::size_t>
{
  public:
    std::size_t operator() (const Customer& c) const  {
       return std::hash<std::string>()(c.firstname) +
              std::hash<std::string>()(c.lastname) +
              std::hash<long>()(c.no);
    }
};

Note that, in principle, we can either specify that

make_hash returns the sum of a call of std::hash<T>()(x) for each argument x of type T

or we can specify that

make_hash provides a hash value for each argument, for which a std::hash() function is provided

with the possible note that the hash value may be poor or only a good hash value if the ranges of all passed arguments is equally distributed.

For my convenience, I propose wording that describes the concrete implementation.

[ 2010 Pittsburgh: Moved to NAD Editorial, rationale added below. ]

[LEWG Kona 2017]

Recommend NAD: Feature? Needs a paper. (This is LEWG21)

Rationale:

There is no consensus to make this change at this time.

Proposed resolution:

In Function objects 19.14 [function.objects] in paragraph 2 at the end of the Header <functional> synopsis insert:

// convenience functions
template <class T>
  size_t make_hash (const T&);
template <class T, class... Types>
  size_t make_hash (const T&, const Types&...);

In Class template hash 19.14.18 [unord.hash] add:

20.7.16.1 Hash creation functions [hash.creation]

template <class T>
  size_t make_hash (const T& val);

Returns: hash<T>()(val);

template <class T, class... Types>
  size_t make_hash (const T& val, const Types&... args);

Returns: hash<T>()(val) + std::make_hash(args...)


1396(i). regex should support allocators

Section: 29.8 [re.regex] Status: Open Submitter: INCITS Opened: 2010-08-25 Last modified: 2018-06-24

Priority: Not Prioritized

View all other issues in [re.regex].

View all issues with Open status.

Duplicate of: 1451

Discussion:

Addresses US-104, US-141

std::basic_regex should have an allocator for all the reasons that a std::string does. For example, I can use boost::interprocess to put a string or vector in shared memory, but not a regex.

[ Resolution proposed by ballot comment ]

Add allocators to regexes

[ 2010-10-24 Daniel adds: ]

Accepting n3171 would solve this issue.

[2011-03-22 Madrid]

Close 1396 as NAD Future.

[LEWG Kona 2017]

Recommend Open: Covered in LEWG9, P0269, which is in wording review.

Rationale:

No consensus for a change at this time

Proposed resolution:


1406(i). Support hashing smart-pointers based on owner

Section: 19.11.3 [util.smartptr.shared] Status: Tentatively NAD Submitter: Japan Opened: 2010-08-25 Last modified: 2018-06-21

Priority: Not Prioritized

View other active issues in [util.smartptr.shared].

View all other issues in [util.smartptr.shared].

View all issues with Tentatively NAD status.

Discussion:

Addresses JP-5

Hash support based on ownership sharing should be supplied for shared_ptr and weak_ptr. For two shared_ptr objects p and q, two distinct equivalence relations can be defined. One is based on equivalence of pointer values, which is derived from the expression p.get() == q.get() (hereafter called address based equivalence relation), the other is based on equivalence of ownership sharing, which is derived from the expression !p.owner_before(q) && !q.owner_before(p) (hereafter called ownership-based equivalence relation). These two equivalence relations are independent in general. For example, a shared_ptr object created by the constructor of the signature shared_ptr(shared_ptr<U> const &, T *) could reveal a difference between these two relations. Therefore, hash support based on each equivalence relation should be supplied for shared_ptr. However, while the standard library provides the hash support for address-based one (20.9.11.6 paragraph 2), it lacks the hash support for ownership-based one. In addition, associative containers work well in combination with the shared_ptr's ownership-based comparison but unordered associative containers don't. This is inconsistent.

For the case of weak_ptr, hash support for the ownership based equivalence relation can be safely defined on weak_ptrs, and even on expired ones. The absence of hash support for the ownership-based equivalence relation is fatal, especially for expired weak_ptrs. And the absence of such hash support precludes some quite effective use-cases, e.g. erasing the unordered_map entry of an expired weak_ptr key from a customized deleter supplied to shared_ptrs.

Hash support for the ownership-based equivalence relation cannot be provided by any user-defined manner because information about ownership sharing is not available to users at all. Therefore, the only way to provide ownership-based hash support is to offer it intrusively by the standard library.

As far as we know, such hash support is implementable. Typical implementation of such hash function could return the hash value of the pointer of the counter object that is internally managed by shared_ptr and weak_ptr.

[2010 Rapperswil:]

No consensus to make this change at this time.

[LEWG Kona 2017]

Recommend NAD: Needs a paper. Feature. Exposing an implementation detail (indirectly observable via hash).

Proposed resolution:

Add the following non-static member functions to shared_ptr and weak_ptr class template;

Update [util.smartptr.shared], 20.9.11.2 paragraph 1

namespace std{
template<class T> class shared_ptr {
public:
...
  size_t owner_hash() const;
...
};
}

Update [util.smartptr.weak], 20.9.11.3 paragraph 1

namespace std{
template<class T> class weak_ptr {
public:
...
  size_t owner_hash() const;
...
};
}

These functions satisfy the following requirements. Let p and q be objects of either shared_ptr or weak_ptr, H be a hypothetical function object type that satisfies the hash requirements ([hash.requirements], 20.2.4) and h be an object of the type H. The expression p.owner_hash() behaves as if it were equivalent to the expression h(p). In addition, h(p) == h(q) must become true if p and q share ownership.


1422(i). vector<bool> iterators are not random access

Section: 21.3.12 [vector.bool] Status: Open Submitter: BSI Opened: 2010-08-25 Last modified: 2017-06-15

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 21.3.12 [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 the reference type 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 to vector<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.


1459(i). Overlapping evaluations are allowed

Section: 30.4 [atomics.order] Status: LEWG Submitter: Canada Opened: 2010-08-25 Last modified: 2017-02-03

Priority: Not Prioritized

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

30.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.8.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 30.5 [atomics.lockfree] p.8. We are not sure of the intention here and do not offer a suggestion for change, but note that 30.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.8.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 30.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 30.4 [atomics.order] p9 (formerly p8), and I agree with the comments that this isn't entirely satisfactory. 30.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:

  1. 6.8.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.

  2. 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.8.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? 30.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 30.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

Proposed resolution:


1484(i). Need a way to join a thread with a timeout

Section: 31.3.2 [thread.thread.class] Status: LEWG Submitter: INCITS Opened: 2010-08-25 Last modified: 2017-03-05

Priority: Not Prioritized

View all issues with LEWG status.

Discussion:

Addresses US-183

There is no way to join a thread with a timeout.

[ Resolution proposed by ballot comment: ]

Add join_for and join_until. Or decide one should never join a thread with a timeout since pthread_join doesn't have a timeout version.

[ 2010 Batavia ]

The concurrency working group deemed this an extension beyond the scope of C++0x.

Rationale:

The LWG does not wish to make a change at this time.

[2017-03-01, Kona]

SG1 recommends: Close as NAD

There has not been much demand for it, and it would usually be difficult to deal with thread_local destructor races. It can be approximated with a condition variable wait followed by an unconditional join. Adding it would create implementation issues on Posix. As always, this may be revisited if we have a paper exploring the issues in detail.

Proposed resolution:


1488(i). Improve interoperability between the C++0x and C1x threads APIs

Section: 31.4 [thread.mutex] Status: LEWG Submitter: INCITS Opened: 2010-08-25 Last modified: 2017-03-05

Priority: Not Prioritized

View all other issues in [thread.mutex].

View all issues with LEWG status.

Discussion:

Addresses US-185

Cooperate with WG14 to improve interoperability between the C++0x and C1x threads APIs. In particular, C1x mutexes should be conveniently usable with a C++0x lock_guard. Performance overheads for this combination should be considered.

[ Resolution proposed by ballot comment: ]

Remove C++0x timed_mutex and timed_recursive_mutex if that facilitates development of more compatible APIs.

[ 2010 Batavia ]

The concurrency sub-group reviewed the options, and decided that closer harmony should wait until both standards are published.

Rationale:

The LWG does not wish to make any change at this time.

[2017-03-01, Kona]

SG1 recommends: Close as NAD

Papers about C compatibility are welcome, but there may be more pressing issues. C threads are not consistently available at this point, so there seems to be little demand to fix this particular problem.

Proposed resolution:


1493(i). Add mutex, recursive_mutex, is_locked function

Section: 31.4.3 [thread.mutex.requirements] Status: LEWG Submitter: INCITS Opened: 2010-08-25 Last modified: 2017-03-05

Priority: Not Prioritized

View other active issues in [thread.mutex.requirements].

View all other issues in [thread.mutex.requirements].

View all issues with LEWG status.

Discussion:

Addresses US-189

mutex and recursive_mutex should have an is_locked() member function. is_locked allows a user to test a lock without acquiring it and can be used to implement a lightweight try_try_lock.

[ Resolution proposed by ballot comment: ]

Add a member function:

bool is_locked() const;

to std::mutex and std::recursive_mutex. These functions return true if the current thread would not be able to obtain a mutex. These functions do not synchronize with anything (and, thus, can avoid a memory fence).

[ 2010 Batavia ]

The Concurrency subgroup reviewed this issue and deemed it to be an extension to be handled after publishing C++0x.

Rationale:

The LWG does not wish to make a change at this time.

[2017-03-01, Kona]

SG1 recommends: Close as NAD

Several participants voiced strong objections, based on either memory model issues or lock elision. No support. It is already possible to write a wrapper that explicitly tracks ownership for testing in the owning thread, which may have been part of the intent here.

Proposed resolution:


1499(i). Condition variables preclude wakeup optimization

Section: 31.5 [thread.condition] Status: Tentatively NAD Submitter: INCITS Opened: 2010-08-25 Last modified: 2018-06-21

Priority: Not Prioritized

View all other issues in [thread.condition].

View all issues with Tentatively NAD status.

Discussion:

Addresses US-193

Condition variables preclude a wakeup optimization.

[ Resolution proposed by ballot comment: ]

Change condition_variable to allow such optimization. See Appendix 1 - Additional Details

[ 2010 Batavia ]

The Concurrency subgroup reviewed the issue, and deemed it an extension to be handled after C++0x.

Rationale:

The LWG does not wish to make the change at this time.

[LEWG Kona 2017]

Recommend NAD: (N4618 numbering) 30.5.1[thread.condition.condvar] p10.3 allows spurious wakeups. This issue is out of date.

[2017-03-01, Kona]

SG1 recommends: Close as NAD

The approach suggested there raises all sorts of issues about thread_locals, etc. It's probably way too late to change this anyway, but this would have required a careful paper.

Proposed resolution:


1521(i). Requirements on internal pointer representations in containers

Section: 21.2.1 [container.requirements.general] Status: Open Submitter: Mike Spertus Opened: 2010-10-16 Last modified: 2018-06-21

Priority: Not Prioritized

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

Proposed resolution:

Add to the end of 21.2.1 [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 form T * or T & except insofar as they are part of the item type or members.


2035(i). Output iterator requirements are broken

Section: 22.3.5.3 [output.iterators] Status: Open Submitter: Daniel Krügler Opened: 2011-02-27 Last modified: 2017-02-03

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 and I would prefer if we could solve both at one location. I suggest the following approach:

  1. 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 22.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.

  2. 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: 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:

  1. Add a reference to 22.3.1 [iterator.requirements.general] to the following parts of the library preceding Clause 24 Iterators library: (I stopped from 21.2.7 [unord.req] on, because the remaining references are the concrete containers)

    1. 15.5.3.2 [swappable.requirements] p5:

      -5- A type X satisfying any of the iterator requirements (24.2) is ValueSwappable if, for any dereferenceable (22.3.1 [iterator.requirements.general]) object x of type X, *x is swappable.

    2. 15.5.3.5 [allocator.requirements], Table 27 — "Descriptive variable definitions", row with the expression c:

      a dereferenceable (22.3.1 [iterator.requirements.general]) pointer of type C*

    3. 19.10.3.2 [pointer.traits.functions]:

      Returns: The first template function returns a dereferenceable (22.3.1 [iterator.requirements.general]) pointer to r obtained by calling Ptr::pointer_to(r); […]

    4. 20.3.2.3 [string.iterators] p. 2:

      Returns: An iterator which is the past-the-end value (22.3.1 [iterator.requirements.general]).

    5. 27.4.5.1.2 [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: t shall be dereferenceable (22.3.1 [iterator.requirements.general]).

    6. 21.2.1 [container.requirements.general] p. 6:

      […] end() returns an iterator which is the past-the-end (22.3.1 [iterator.requirements.general]) value for the container. […]

    7. 21.2.3 [sequence.reqmts] p. 3:

      […] q denotes a valid dereferenceable (22.3.1 [iterator.requirements.general]) const iterator to a, […]

    8. 21.2.6 [associative.reqmts] p. 8 (I omit intentionally one further reference in the same sub-clause):

      […] q denotes a valid dereferenceable (22.3.1 [iterator.requirements.general]) const iterator to a, […]

    9. 21.2.7 [unord.req] p. 10 (I omit intentionally one further reference in the same sub-clause):

      […] q and q1 are valid dereferenceable (22.3.1 [iterator.requirements.general]) const iterators to a, […]

  2. Edit 22.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 i for which the expression *i is defined are called dereferenceable. Values of an iterator i for which the expression ++i is 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. […]

  3. Modify the column contents of Table 106 — "Iterator requirements", 22.3.5.1 [iterator.iterators], as indicated:

    Table 106 — Iterator requirements
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    *r reference   pre: r is dereferenceable.
    ++r X&   pre: r is incrementable.
  4. Modify the column contents of Table 107 — "Input iterator requirements", 22.3.5.2 [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 specificationend rationale]:

    Table 107 — Input iterator requirements (in addition to Iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    a != b contextually
    convertible to bool
    !(a == b) pre: (a, b) is in the domain
    of ==.
    *a convertible to T   pre: a is dereferenceable.
    The expression
    (void)*a, *a is equivalent
    to *a.
    If a == b and (a,b) is in
    the domain of == then *a is
    equivalent to *b.
    a->m   (*a).m pre: a is dereferenceable.
    ++r X&   pre: r is dereferenceableincrementable.
    post: r is dereferenceable or
    r is past-the-end.
    post: any copies of the
    previous value of r are no
    longer required either to be
    dereferenceable, incrementable,
    or to be in the domain of ==.
    (void)r++   (void)++r equivalent to (void)++r
    *r++ convertible to T { T tmp = *r;
    ++r;
    return tmp; }
     
  5. Modify the column contents of Table 108 — "Output iterator requirements", 22.3.5.3 [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 specificationend rationale]:

    Table 108 — Output iterator requirements (in addition to Iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    *r = o result is not used   pre: r is dereferenceable.
    Remark: After this operation
    r is not required to be
    dereferenceable and any copies of
    the previous value of r are no
    longer required to be dereferenceable
    or incrementable.

    post: r is incrementable.
    ++r X&   pre: r is incrementable.
    &r == &++r.
    Remark: After this operation
    r is not required to be
    dereferenceable.
    Remark: After this operation
    r is not required to be
    incrementable and any copies of
    the previous value of r are no
    longer required to be dereferenceable
    or incrementable.

    post: r is dereferenceable
    or r is past-the-end
    incrementable.
    r++ convertible to const X& { X tmp = r;
    ++r;
    return tmp; }
    Remark: After this operation
    r is not required to be
    dereferenceable.
    post: r is incrementable.
    *r++ = o result is not used { *r = o; ++r; } Remark: After this operation
    r is not required to be
    dereferenceable.
    post: r is incrementable.
  6. Modify the column contents of Table 109 — "Forward iterator requirements", 22.3.5.4 [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-condition
    r++ convertible to const X& { X tmp = r;
    ++r;
    return tmp; }
     
    *r++ reference { reference tmp = *r;
    ++r;
    return tmp; }
     
  7. Modify the column contents of Table 110 — "Bidirectional iterator requirements", 22.3.5.5 [bidirectional.iterators], as indicated:

    Table 110 — Bidirectional iterator requirements (in addition to forward iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    --r X&   pre: there exists s such that
    r == ++s.
    post: r is dereferenceableincrementable.
    --(++r) == r.
    --r == --s implies r == s.
    &r == &--r.
    r-- convertible to const X& { X tmp = r;
    --r;
    return tmp; }
     
    *r-- reference { reference tmp = *r;
    --r;
    return tmp; }
     

2038(i). Missing definition for incrementable iterator

Section: 22.3.5.3 [output.iterators] Status: Open Submitter: Pete Becker Opened: 2011-02-27 Last modified: 2017-02-03

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 15.5.3.2 [swappable.requirements] there is a mention of "dereferenceable object"; in 15.5.3.5 [allocator.requirements] the table of Descriptive variable definitions refers to a "dereferenceable pointer"; 19.10.3.2 [pointer.traits.functions] refers to a "dereferenceable pointer"; in 27.4.5.1.2 [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 solves this issue as well.

[ 2011 Bloomington ]

Agree with Daniel, this will be handled by the resolution of 2035.

Proposed resolution:


2077(i). Further incomplete constraints for type traits

Section: 19.15.4.3 [meta.unary.prop] Status: Open Submitter: Daniel Krügler Opened: 2011-08-20 Last modified: 2017-02-03

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 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:


2088(i). std::terminate problem

Section: 16.9.4 [exception.terminate] Status: Open Submitter: Daniel Krügler Opened: 2011-09-25 Last modified: 2017-02-03

Priority: 3

View all issues with Open status.

Discussion:

Andrzej Krzemienski reported the following on comp.std.c++:

In N3290, which is to become the official standard, in 16.9.4.4 [terminate], paragraph 1 reads

Remarks: Called by the implementation when exception handling must be abandoned for any of several reasons (15.5.1), in effect immediately after evaluating the throw-expression (18.8.3.1). May also be called directly by the program.

It is not clear what is "in effect". It was clear in previous drafts where paragraphs 1 and 2 read:

Called by the implementation when exception handling must be abandoned for any of several reasons (15.5.1). May also be called directly by the program.

Effects: Calls the terminate_handler function in effect immediately after evaluating the throw-expression (18.8.3.1), if called by the implementation, or calls the current terminate_handler function, if called by the program.

It was changed by N3189. The same applies to function unexpected (D. 11.4, paragraph 1).

Assuming the previous wording is still intended, the wording can be read "unless std::terminate is called by the program, we will use the handler that was in effect immediately after evaluating the throw-expression".

This assumes that there is some throw-expression connected to every situation that triggers the call to std::terminate. But this is not the case:

Which one is referred to?

In case std::nested_exception::rethrow_nested is called for an object that has captured no exception, there is no throw-expression involved directly (and may no throw be involved even indirectly).

Next, 16.9.4.1 [terminate.handler], paragraph 2 says

Required behavior: A terminate_handler shall terminate execution of the program without returning to the caller.

This seems to allow that the function may exit by throwing an exception (because word "return" implies a normal return).

One could argue that words "terminate execution of the program" are sufficient, but then why "without returning to the caller" would be mentioned. In case such handler throws, noexcept specification in function std::terminate is violated, and std::terminate would be called recursively - should std::abort not be called in case of recursive std::terminate call? On the other hand some controlled recursion could be useful, like in the following technique.

The here mentioned wording changes by N3189 in regard to 16.9.4.4 [terminate] p1 were done for a better separation of effects (Effects element) and additional normative wording explanations (Remarks element), there was no meaning change intended. Further, there was already a defect existing in the previous wording, which was not updated when further situations where defined, when std::terminate where supposed to be called by the implementation.

The part

"in effect immediately after evaluating the throw-expression"

should be removed and the quoted reference to 16.9.4.1 [terminate.handler] need to be part of the effects element where it refers to the current terminate_handler function, so should be moved just after

"Effects: Calls the current terminate_handler function."

It seems ok to allow a termination handler to exit via an exception, but the suggested idiom should better be replaced by a more simpler one based on evaluating the current exception pointer in the terminate handler, e.g.

void our_terminate (void) {
  std::exception_ptr p = std::current_exception();
  if (p) {
    ... // OK to rethrow and to determine it's nature
  } else {
    ... // Do something else
  }
}

[2011-12-09: Daniel comments]

A related issue is 2111.

[2012, Kona]

Move to Open.

There is an interaction with Core issues in this area that Jens is already supplying wording for. Review this issue again once Jens wording is available.

Alisdair to review clause 15.5 (per Jens suggestion) and recommend any changes, then integrate Jens wording into this issue.

Proposed resolution:


2089(i). std::allocator::construct should use uniform initialization

Section: 19.10.10.1 [allocator.members] Status: EWG Submitter: David Krauss Opened: 2011-10-07 Last modified: 2018-08-27

Priority: 2

View all other issues in [allocator.members].

View all issues with EWG status.

Discussion:

When the EmplaceConstructible (21.2.1 [container.requirements.general]/13) requirement is used to initialize an object, direct-initialization occurs. Initializing an aggregate or using a std::initializer_list constructor with emplace requires naming the initialized type and moving a temporary. This is a result of std::allocator::construct using direct-initialization, not list-initialization (sometimes called "uniform initialization") syntax.

Altering std::allocator<T>::construct to use list-initialization would, among other things, give preference to std::initializer_list constructor overloads, breaking valid code in an unintuitive and unfixable way — there would be no way for emplace_back to access a constructor preempted by std::initializer_list without essentially reimplementing push_back.

std::vector<std::vector<int>> v;
v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization

The proposed compromise is to use SFINAE with std::is_constructible, which tests whether direct-initialization is well formed. If is_constructible is false, then an alternative std::allocator::construct overload is chosen which uses list-initialization. Since list-initialization always falls back on direct-initialization, the user will see diagnostic messages as if list-initialization (uniform-initialization) were always being used, because the direct-initialization overload cannot fail.

I can see two corner cases that expose gaps in this scheme. One occurs when arguments intended for std::initializer_list satisfy a constructor, such as trying to emplace-insert a value of {3, 4} in the above example. The workaround is to explicitly specify the std::initializer_list type, as in v.emplace_back(std::initializer_list<int>(3, 4)). Since this matches the semantics as if std::initializer_list were deduced, there seems to be no real problem here.

The other case is when arguments intended for aggregate initialization satisfy a constructor. Since aggregates cannot have user-defined constructors, this requires that the first nonstatic data member of the aggregate be implicitly convertible from the aggregate type, and that the initializer list have one element. The workaround is to supply an initializer for the second member. It remains impossible to in-place construct an aggregate with only one nonstatic data member by conversion from a type convertible to the aggregate's own type. This seems like an acceptably small hole.

The change is quite small because EmplaceConstructible is defined in terms of whatever allocator is specified, and there is no need to explicitly mention SFINAE in the normative text.

[2012, Kona]

Move to Open.

There appears to be a real concern with initializing aggregates, that can be performed only using brace-initialization. There is little interest in the rest of the issue, given the existence of 'emplace' methods in C++11.

Move to Open, to find an acceptable solution for intializing aggregates. There is the potential that EWG may have an interest in this area of language consistency as well.

[2013-10-13, Ville]

This issue is related to 2070.

[2015-02 Cologne]

Move to EWG, Ville to write a paper.

[2015-09, Telecon]

Ville: N4462 reviewed in Lenexa. EWG discussion to continue in Kona.

[2016-08 Chicago]

See N4462

The notes in Lenexa say that Marshall & Jonathan volunteered to write a paper on this

[2018-08-23 Batavia Issues processing]

P0960 (currently in flight) should resolve this.

Proposed resolution:

This wording is relative to the FDIS.

Change 19.10.10.1 [allocator.members] p12 as indicated:

template <class U, class... Args>
  void construct(U* p, Args&&... args);

12 Effects: ::new((void *)p) U(std::forward<Args>(args)...) if is_constructible<U, Args...>::value is true, else ::new((void *)p) U{std::forward<Args>(args)...}


2095(i). promise and packaged_task missing constructors needed for uses-allocator construction

Section: 31.6.6 [futures.promise], 31.6.10 [futures.task] Status: LEWG Submitter: Jonathan Wakely Opened: 2011-11-01 Last modified: 2017-02-03

Priority: 4

View other active issues in [futures.promise].

View all other issues in [futures.promise].

View all issues with LEWG status.

Discussion:

This example is ill-formed according to C++11 because uses_allocator<promise<R>, A>::value is true, but is_constructible<promise<R>, A, promise<R>&&>::value is false. Similarly for packaged_task.

#include <future>
#include <memory>
#include <tuple>

using namespace std;

typedef packaged_task<void()> task;
typedef promise<void> prom;
allocator<task> a;

tuple<task, prom> t1{ allocator_arg, a };
tuple<task, prom> t2{ allocator_arg, a, task{}, prom{} };

[2012, Portland]

This is an allocator issue, and should be dealt with directly by LWG.

[2013-03-06]

Jonathan suggests to make the new constructors non-explicit and makes some representational improvements.

[2013-09 Chicago]

Move to deferred.

This issue has much in common with similar problems with std::function that are being addressed by the polymorphic allocators proposal currently under evaluation in LEWG. Defer further discussion on this topic until the final outcome of that paper and its proposed resolution is known.

[2014-02-20 Re-open Deferred issues as Priority 4]

[2016-08 Chicago]

Fri PM: Send to LEWG - and this also applies to function in LFTS.

Proposed resolution:

[This wording is relative to the FDIS.]

  1. Add to 31.6.6 [futures.promise], class template promise synopsis, as indicated:

    namespace std {
      template <class R>
      class promise {
      public:
        promise();
        template <class Allocator>
        promise(allocator_arg_t, const Allocator& a);
        template <class Allocator>
        promise(allocator_arg_t, const Allocator& a, promise&& rhs) noexcept;
        promise(promise&& rhs) noexcept;
        promise(const promise& rhs) = delete;
        ~promise();	
        […]
      };
      […]
    }
    
  2. Change 31.6.6 [futures.promise] as indicated:

    promise(promise&& rhs) noexcept;
    template <class Allocator>
    promise(allocator_arg_t, const Allocator& a, promise&& rhs) noexcept;
    

    -5- Effects: constructs a new promise object and transfers ownership of the shared state of rhs (if any) to the newly-constructed object.

    -6- Postcondition: rhs has no shared state.

    -?- [Note: a is not used — end note]

  3. Add to 31.6.10 [futures.task], class template packaged_task synopsis, as indicated:

    namespace std {
      template<class> class packaged_task; // undefined
    
      template<class R, class... ArgTypes>
      class packaged_task<R(ArgTypes...)> {
      public:
        // construction and destruction
        packaged_task() noexcept;
        template <class Allocator>
          packaged_task(allocator_arg_t, const Allocator& a) noexcept;
        template <class F>
          explicit packaged_task(F&& f);
        template <class F, class Allocator>
          explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f);
        ~packaged_task();
    	
        // no copy
        packaged_task(const packaged_task&) = delete;
        template<class Allocator>
          packaged_task(allocator_arg_t, const Allocator& a, const packaged_task&) = delete;
        packaged_task& operator=(const packaged_task&) = delete;
        
        // move support
        packaged_task(packaged_task&& rhs) noexcept;
        template <class Allocator>
          packaged_task(allocator_arg_t, const Allocator& a, packaged_task&& rhs) noexcept;
        packaged_task& operator=(packaged_task&& rhs) noexcept;
        void swap(packaged_task& other) noexcept;
        […]
      };
      […]
    }
    
  4. Change 31.6.10.1 [futures.task.members] as indicated:

    packaged_task() noexcept;
    template <class Allocator>
      packaged_task(allocator_arg_t, const Allocator& a) noexcept;
    

    -1- Effects: constructs a packaged_task object with no shared state and no stored task.

    -?- [Note: a is not used — end note]

    […]

    packaged_task(packaged_task&& rhs) noexcept;
    template <class Allocator>
      packaged_task(allocator_arg_t, const Allocator& a, packaged_task&& rhs) noexcept;
    

    -5- Effects: constructs a new packaged_task object and transfers ownership of rhs's shared state to *this, leaving rhs with no shared state. Moves the stored task from rhs to *this.

    -6- Postcondition: rhs has no shared state.

    -?- [Note: a is not used — end note]


2114(i). Incorrect "contextually convertible to bool" requirements

Section: 15.5.3.3 [nullablepointer.requirements], 22.3.5.2 [input.iterators], 22.3.5.6 [random.access.iterators], 24.1 [algorithms.general], 24.7 [alg.sorting], 31.2.1 [thread.req.paramname] Status: Open Submitter: Daniel Krügler Opened: 2011-12-09 Last modified: 2017-02-03

Priority: 3

View all issues with Open status.

Discussion:

As of 15.5.3.1 [utility.arg.requirements] Table 17/18, the return types of the expressions

a == b

or

a < b

for types satisfying the EqualityComparable or LessThanComparable types, respectively, are required to be "convertible to bool" which corresponds to a copy-initialization context. But several newer parts of the library that refer to such contexts have lowered the requirements taking advantage of the new terminology of "contextually convertible to bool" instead, which corresponds to a direct-initialization context (In addition to "normal" direct-initialization constructions, operands of logical operations as well as if or switch conditions also belong to this special context).

One example for these new requirements are input iterators which satisfy EqualityComparable but also specify that the expression

a != b

shall be just "contextually convertible to bool". The same discrepancy exists for requirement set NullablePointer in regard to several equality-related expressions.

For random access iterators we have

a < b contextually convertible to bool

as well as for all derived comparison functions, so strictly speaking we could have a random access iterator that does not satisfy the LessThanComparable requirements, which looks like an artifact to me.

Even if we keep with the existing requirements based on LessThanComparable or EqualityComparable we still would have the problem that some current specifications are actually based on the assumption of implicit convertibility instead of "explicit convertibility", e.g. 19.11.1.5 [unique.ptr.special] p3:

template <class T1, class D1, class T2, class D2>
bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);

-3- Returns: x.get() != y.get().

Similar examples exist in 19.11.1.2.2 [unique.ptr.single.dtor] p2, 19.11.1.2.3 [unique.ptr.single.asgn] p9, 19.11.1.2.4 [unique.ptr.single.observers] p1+3+8, etc.

In all these places the expressions involving comparison functions (but not those of the conversion of a NullablePointer to bool!) assume to be "convertible to bool". I think this is a very natural assumption and all delegations of the comparison functions of some type X to some other API type Y in third-party code does so assuming that copy-initialization semantics will just work.

The actual reason for using the newer terminology can be rooted back to LWG 556. My hypotheses is that the resolution of that issue also needs a slight correction. Why so?

The reason for opening that issue were worries based on the previous "convertible to bool" wording. An expressions like "!pred(a, b)" might not be well-formed in those situations, because operator! might not be accessible or might have an unusual semantics (and similarly for other logical operations). This can indeed happen with unusual proxy return types, so the idea was that the evaluation of Predicate, BinaryPredicate (24.1 [algorithms.general] p8+9), and Compare (24.7 [alg.sorting] p2) should be defined based on contextual conversion to bool. Unfortunately this alone is not sufficient: In addition, I think, we also want the predicates to be (implicitly) convertible to bool! Without this wording, several conditions are plain wrong, e.g. 24.5.5 [alg.find] p2, which talks about "pred(*i) != false" (find_if) and "pred(*i) == false" (find_if_not). These expressions are not within a boolean context!

While we could simply fix all these places by proper wording to be considered in a "contextual conversion to bool", I think that this is not the correct solution: Many third-party libraries already refer to the previous C++03 Predicate definition — it actually predates C++98 and is as old as the SGI specification. It seems to be a high price to pay to switch to direct initialization here instead of fixing a completely different specification problem.

A final observation is that we have another definition for a Predicate in 31.2.1 [thread.req.paramname] p2:

If a parameter is Predicate, operator() applied to the actual template argument shall return a value that is convertible to bool.

The problem here is not that we have two different definitions of Predicate in the standard — this is confusing, but this fact alone is not a defect. The first (minor) problem is that this definition does not properly apply to function objects that are function pointers, because operator() is not defined in a strict sense. But the actually worse second problem is that this wording has the very same problem that has originally lead to LWG 556! We only need to look at 31.5.3 [thread.condition.condvar] p15 to recognice this:

while (!pred())
  wait(lock);

The negation expression here looks very familiar to the example provided in LWG 556 and is sensitive to the same "unusual proxy" problem. Changing the 31.2.1 [thread.req.paramname] wording to a corresponding "contextual conversion to bool" wouldn't work either, because existing specifications rely on "convertible to bool", e.g. 31.5.3 [thread.condition.condvar] p32+33+42 or 31.5.4 [thread.condition.condvarany] p25+26+32+33.

To summarize: I believe that LWG 556 was not completely resolved. A pessimistic interpretation is, that even with the current wording based on "contextually convertible to bool" the actual problem of that issue has not been fixed. What actually needs to be required here is some normative wording that basically expresses something along the lines of:

The semantics of any contextual conversion to bool shall be equivalent to the semantics of any implicit conversion to bool.

This is still not complete without having concepts, but it seems to be a better approximation. Another way of solving this issue would be to define a minimum requirements table with equivalent semantics. The proposed wording is a bit simpler but attempts to express the same thing.

[2012, Kona]

Agree with Daniel that we potentially broke some C++03 user code, accept the changes striking "contextually" from tables. Stefan to provide revised wording for section 25, and figure out changes to section 30.

Move to open, and then to Review when updated wording from Stefan is available.

[2012-10-12, STL comments]

  1. The current proposed resolution still isn't completely satisfying. It would certainly be possible for the Standard to require these various expressions to be implicitly and contextually convertible to bool, but that would have a subtle consequence (which, I will argue, is undesirable - regardless of the fact that it dates all the way back to C++98/03). It would allow users to provide really wacky types to the Standard Library, with one of two effects:

    1. Standard Library implementations would have to go to great lengths to respect such wacky types, essentially using static_cast<bool> when invoking any predicates or comparators.

    2. Otherwise, such wacky types would be de facto nonportable, because they would make Standard Library implementations explode.

    Effect B is the status quo we're living with today. What Standard Library implementations want to do with pred(args) goes beyond "if (pred(args))" (C++03), contextually converting pred(args) to bool (C++11), or implicitly and contextually converting pred(args) to bool (the current proposed resolution). Implementations want to say things like:

    if (pred(args))
    if (!pred(args))
    if (cond && pred(args))
    if (cond && !pred(args))
    

    These are real examples taken from Dinkumware's implementation. There are others that would be realistic ("pred(args) && cond", "cond || pred(args)", etc.)

    Although negation was mentioned in this issue's Discussion section, and in LWG 556's, the current proposed resolution doesn't fix this problem. Requiring pred(args) to be implicitly and contextually convertible to bool doesn't prevent operator!() from being overloaded and returning std::string (as a wacky example). More ominously, it doesn't prevent operator&&() and operator||() from being overloaded and destroying short-circuiting.

  2. I would like LWG input before working on Standardese for a new proposed resolution. Here's an outline of what I'd like to do:

    1. Introduce a new "concept" in 15.5.3 [utility.requirements], which I would call BooleanTestable in the absence of better ideas.

    2. Centralize things and reduce verbosity by having everything simply refer to BooleanTestable when necessary. I believe that the tables could say "Return type: BooleanTestable", while Predicate/BinaryPredicate/Compare would need the incantation "shall satisfy the requirements of BooleanTestable".

    3. Resolve the tug-of-war between users (who occasionally want to do weird things) and implementers (who don't want to have to contort their code) by requiring that:

      1. Given a BooleanTestable x, x is both implicitly and contextually convertible to bool.

      2. Given a BooleanTestable x, !x is BooleanTestable. (This is intentionally "recursive".)

      3. Given a BooleanTestable x, bool t = x, t2(x), f = !x; has the postcondition t == t2 && t != f.

      4. Given a BooleanTestable x and a BooleanTestable y of possibly different types, "x && y" and "x || y" invoke the built-in operator&&() and operator||(), triggering short-circuiting.

      5. bool is BooleanTestable.

    I believe that this simultaneously gives users great latitude to use types other than bool, while allowing implementers to write reasonable code in order to get their jobs done. (If I'm forgetting anything that implementers would want to say, please let me know.)

  3. About requirement (I): As Daniel patiently explained to me, we need to talk about both implicit conversions and contextual conversions, because it's possible for a devious type to have both "explicit operator bool()" and "operator int()", which might behave differently (or be deleted, etc.).

  4. About requirement (IV): This is kind of tricky. What we'd like to say is, "BooleanTestable can't ever trigger an overloaded logical operator". However, given a perfectly reasonable type Nice - perhaps even bool itself! - other code (perhaps a third-party library) could overload operator&&(Nice, Evil). Therefore, I believe that the requirement should be "no first use" - the Standard Library will ask for various BooleanTestable types from users (for example, the result of "first != last" and the result of "pred(args)"), and as long as they don't trigger overloaded logical operators with each other, everything is awesome.

  5. About requirement (V): This is possibly redundant, but it's trivial to specify, makes it easier for users to understand what they need to do ("oh, I can always achieve this with bool"), and provides a "base case" for requirement (IV) that may or may not be necessary. Since bool is BooleanTestable, overloading operator&&(bool, Other) (etc.) clearly makes the Other type non-BooleanTestable.

Previous resolution from Daniel [SUPERSEDED]:

This wording is relative to the FDIS.

  1. Change Table 25 — "NullablePointer requirements" in 15.5.3.3 [nullablepointer.requirements] as indicated:

    Table 25 — NullablePointer requirements
    Expression Return type Operational semantics
    […]
    a != b contextually convertible to bool !(a == b)
    a == np
    np == a
    contextually convertible to bool a == P()
    a != np
    np != a
    contextually convertible to bool !(a == np)
  2. Change Table 107 — "Input iterator requirements" in 22.3.5.2 [input.iterators] as indicated:

    Table 107 — Input iterator requirements (in addition to Iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    a != b contextually convertible to bool !(a == b) pre: (a, b) is in the domain of ==.
    […]
  3. Change Table 111 — "Random access iterator requirements" in 22.3.5.6 [random.access.iterators] as indicated:

    Table 111 — Random access iterator requirements (in addition to bidirectional iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    […]
    a < b contextually convertible to bool b - a > 0 < is a total ordering relation
    a > b contextually convertible to bool b < a > is a total ordering relation opposite to <.
    a >= b contextually convertible to bool !(a < b)
    a <= b contextually convertible to bool !(a > b)
  4. Change 24.1 [algorithms.general] p8+9 as indicated:

    -8- The Predicate parameter is used whenever an algorithm expects a function object (19.14 [function.objects]) that, when applied to the result of dereferencing the corresponding iterator, returns a value testable as true. In other words, if an algorithm takes Predicate pred as its argument and first as its iterator argument, it should work correctly in the construct pred(*first) implicitly or contextually converted to bool (Clause 7.3 [conv]). The function object pred shall not apply any non-constant function through the dereferenced iterator.

    -9- The BinaryPredicate parameter is used whenever an algorithm expects a function object that when applied to the result of dereferencing two corresponding iterators or to dereferencing an iterator and type T when T is part of the signature returns a value testable as true. In other words, if an algorithm takes BinaryPredicate binary_pred as its argument and first1 and first2 as its iterator arguments, it should work correctly in the construct binary_pred(*first1, *first2) implicitly or contextually converted to bool (Clause 7.3 [conv]). BinaryPredicate always takes the first iterator's value_type as its first argument, that is, in those cases when T value is part of the signature, it should work correctly in the construct binary_pred(*first1, value) implicitly or contextually converted to bool (Clause 7.3 [conv]). binary_pred shall not apply any non-constant function through the dereferenced iterators.

  5. Change 24.7 [alg.sorting] p2 as indicated:

    -2- Compare is a function object type (19.14 [function.objects]). The return value of the function call operation applied to an object of type Compare, when implicitly or contextually converted to bool (7.3 [conv]), yields true if the first argument of the call is less than the second, and false otherwise. Compare comp is used throughout for algorithms assuming an ordering relation. It is assumed that comp will not apply any non-constant function through the dereferenced iterator.

  6. Change 31.2.1 [thread.req.paramname] p2 as indicated:

    -2- If a parameter is Predicate, operator() applied to the actual template argument shall return a value that is convertible to boolPredicate is a function object type (19.14 [function.objects]). The return value of the function call operation applied to an object of type Predicate, when implicitly or contextually converted to bool (7.3 [conv]), yields true if the corresponding test condition is satisfied, and false otherwise.

[2014-05-20, Daniel suggests concrete wording based on STL's proposal]

The presented wording follows relatively closely STL's outline with the following notable exceptions:

  1. A reference to BooleanTestable in table "Return Type" specifications seemed very unusual to me and I found no "prior art" for this in the Standard. Instead I decided to follow the usual style to add a symbol with a specific meaning to a specific paragraph that specifies symbols and their meanings.

  2. STL's requirement IV suggested to directly refer to built-in operators && and ||. In my opinion this concrete requirement isn't needed if we simply require that two BooleanTestable operands behave equivalently to two those operands after conversion to bool (each of them).

  3. I couldn't find a good reason to require normatively that type bool meets the requirements of BooleanTestable: My assertion is that after having defined them, the result simply falls out of this. But to make this a bit clearer, I added also a non-normative note to these effects.

[2014-06-10, STL comments]

In the current wording I would like to see changed the suggested changes described by bullet #6:

  1. In 21.2.1 [container.requirements.general] p4 undo the suggested change

  2. Then change the 7 occurrences of "convertible to bool" in the denoted tables to "bool".

[2015-05-05 Lenexa]

STL: Alisdair wanted to do something here, but Daniel gave us updated wording.

[2015-07 Telecon]

Alisdair: Should specify we don't break short circuiting.
Ville: Looks already specified because that's the way it works for bool.
Geoffrey: Maybe add a note about the short circuiting.
B2/P2 is somewhat ambiguous. It implies that B has to be both implicitly convertible to bool and contextually convertible to bool.
We like this, just have nits.
Status stays Open.
Marshall to ping Daniel with feedback.

[2016-02-27, Daniel updates wording]

  1. The revised wording has been updated from N3936 to N4567.

  2. To satisfy the Kona 2015 committee comments, the wording in [booleantestable.requirements] has been improved to better separate the two different requirements of "can be contextually converted to bool" and "can be implicitly converted to bool. Both are necessary because it is possible to define a type that has the latter property but not the former, such as the following one:

    2016-08-07, Daniel: The below example has been corrected to reduce confusion about the performed conversions as indicated by the delta markers:

    using Bool = int;
    
    struct OddBoolean 
    {
      explicit operator bool() const = delete;
      operator Bool() const;
      OddBoolean(bool) = delete;
      OddBoolean(Bool){}
    } ob;
    
    bool b2 = ob; // OK
    bool b1(ob);  // Error
    OddBoolean b2 = true; // OK
    OddBoolean b1(true);  // Error
    
    
  3. In [booleantestable.requirements] a note has been added to ensure that an implementation is not allowed to break any short-circuiting semantics.

  4. I decided to separate LWG 2587/2588 from this issue. Both these issues aren't exactly the same but depending on the committee's position, their resolution might benefit from the new vocabulary introduced here.

Proposed resolution:

This wording is relative to N4567.

  1. Change 15.5.3.1 [utility.arg.requirements] p1, Table 17 — "EqualityComparable requirements", and Table 18 — "LessThanComparable requirements" as indicated:

    -1- […] In these tables, T is an object or reference type to be supplied by a C++ program instantiating a template; a, b, and c are values of type (possibly const) T; s and t are modifiable lvalues of type T; u denotes an identifier; rv is an rvalue of type T; and v is an lvalue of type (possibly const) T or an rvalue of type const T; and BT denotes a type that meets the BooleanTestable requirements ([booleantestable.requirements]).

    […]

    Table 17 — EqualityComparable requirements [equalitycomparable]
    Expression Return type Requirement
    a == b convertible to
    bool
    BT
    == is an equivalence relation, that is, it has the following properties: […]

    […]

    Table 18 — LessThanComparable requirements [lessthancomparable]
    Expression Return type Requirement
    a < b convertible to
    bool
    BT
    < is a strict weak ordering relation (24.7 [alg.sorting])
  2. Between 15.5.3.2 [swappable.requirements] and 15.5.3.3 [nullablepointer.requirements] insert a new sub-clause as indicated:

    ?.?.?.? BooleanTestable requirements [booleantestable.requirements]

    -?- A BooleanTestable type is a boolean-like type that also supports conversions to bool. A type B meets the BooleanTestable requirements if the expressions described in Table ?? are valid and have the indicated semantics, and if B also satisfies all the other requirements of this sub-clause [booleantestable.requirements].

    An object b of type B can be implicitly converted to bool and in addition can be contextually converted to bool (Clause 4). The result values of both kinds of conversions shall be equivalent.

    [Example: The types bool, std::true_type, and std::bitset<>::reference are BooleanTestable types. — end example]

    For the purpose of Table ??, let B2 and Bn denote types (possibly both equal to B or to each other) that meet the BooleanTestable requirements, let b1 denote a (possibly const) value of B, let b2 denote a (possibly const) value of B2, and let t1 denote a value of type bool.

    [Note: These rules ensure what an implementation can rely on but doesn't grant it license to break short-circuiting behavior of a BooleanTestable type. — end note]

  3. Somewhere within the new sub-clause [booleantestable.requirements] insert the following new Table (?? denotes the assigned table number):

    Table ?? — BooleanTestable requirements [booleantestable]
    Expression Return type Operational semantics
    bool(b1) bool Remarks: bool(b1) == t1 for every value
    b1 implicitly converted to t1.
    !b1 Bn Remarks: bool(b1) == !bool(!b1) for
    every value b1.
    b1 && b2 bool bool(b1) && bool(b2)
    b1 || b2 bool bool(b1) || bool(b2)
  4. Change 15.5.3.3 [nullablepointer.requirements] p5 and Table 25 — "NullablePointer requirements" as indicated:

    […]

    -5- In Table 25, u denotes an identifier, t denotes a non-const lvalue of type P, a and b denote values of type (possibly const) P, and np denotes a value of type (possibly const) std::nullptr_t, and BT denotes a type that meets the BooleanTestable requirements ([booleantestable.requirements]).

    […]

    Table 25 — NullablePointer requirements [nullablepointer]
    Expression Return type Operational semantics
    a != b contextually convertible to boolBT […]
    a == np
    np == a
    contextually convertible to boolBT […]
    a != np
    np != a
    contextually convertible to boolBT […]
  5. Change 19.5.3.8 [tuple.rel] as indicated;

    template<class... TTypes, class... UTypes>
    constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
    

    -1- Requires: For all i, where 0 <= i and i < sizeof...(TTypes), get<i>(t) == get<i>(u) is a valid expression returning a type that is convertible to boolmeets the BooleanTestable requirements ([booleantestable.requirements]). sizeof...(TTypes) == sizeof...(UTypes).

    […]

    template<class... TTypes, class... UTypes>
    constexpr bool operator<(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
    

    -4- Requires: For all i, where 0 <= i and i < sizeof...(TTypes), get<i>(t) < get<i>(u) and get<i>(u) < get<i>(t) are valid expressions returning types that are convertible to boolmeet the BooleanTestable requirements ([booleantestable.requirements]). sizeof...(TTypes) == sizeof...(UTypes).

    […]

  6. Change 21.2.1 [container.requirements.general], Table 95 — "Container requirements", and Table 97 — "Optional container operations" as indicated:

    -4- In Tables 95, 96, and 97 X denotes a container class containing objects of type T, a and b denote values of type X, u denotes an identifier, r denotes a non-const value of type X, and rv denotes a non-const rvalue of type X, and BT denotes a type that meets the BooleanTestable requirements ([booleantestable.requirements]).

    Table 95 — Container requirements
    Expression Return type […]
    a == b convertible to
    bool
    BT
    […]
    a != b convertible to
    bool
    BT
    […]
    a.empty() convertible to
    bool
    BT
    […]

    […]

    Table 97 — Optional container requirements
    Expression Return type […]
    a < b convertible to
    bool
    BT
    […]
    a > b convertible to
    bool
    BT
    […]
    a <= b convertible to
    bool
    BT
    […]
    a >= b convertible to
    bool
    BT
    […]
  7. Change 22.3.1 [iterator.requirements.general], Table 106 — "Input iterator requirements", and Table 110 — "Random access iterator requirements" as indicated:

    -12- In the following sections, a and b denote values of type X or const X, difference_type and reference refer to the types iterator_traits<X>::difference_type and iterator_traits<X>::reference, respectively, n denotes a value of difference_type, u, tmp, and m denote identifiers, r denotes a value of X&, t denotes a value of value type T, o denotes a value of some type that is writable to the output iterator, and BT denotes a type that meets the BooleanTestable requirements ([booleantestable.requirements]).

    Table 106 — Input iterator requirements
    Expression Return type […]
    a != b contextually convertible to
    bool
    BT
    […]

    […]

    Table 110 — Random access iterator requirements
    Expression Return type […]
    a < b contextually convertible to
    bool
    BT
    […]
    a > b contextually convertible to
    bool
    BT
    […]
    a >= b contextually convertible to
    bool
    BT
    […]
    a <= b contextually convertible to
    bool
    BT
    […]
  8. Change 24.1 [algorithms.general] p8+p9 as indicated:

    [Drafting note: The wording changes below also fix (a) unusual wording forms used ("should work") which are unclear in which sense they are imposing normative requirements and (b) the problem, that the current wording seems to allow that the predicate may mutate a call argument, if that is not a dereferenced iterator. Upon applying the new wording it became obvious that the both the previous and the new wording has the effect that currently algorithms such as adjacent_find, search_n, unique, and unique_copy are not correctly described (because they have no iterator argument named first1), which could give raise to a new library issue. — end drafting note]

    -8- The Predicate parameter is used whenever an algorithm expects a function object (20.9) that, when applied to the result of dereferencing the corresponding iterator, returns a value testable as true. In other words, iIf an algorithm takes Predicate pred as its argument and first as its iterator argument, it should work correctly in the construct pred(*first) contextually converted to bool (Clause 4)the expression pred(*first) shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). The function object pred shall not apply any non-constant function through the dereferenced iteratorits argument.

    -9- The BinaryPredicate parameter is used whenever an algorithm expects a function object that when applied to the result of dereferencing two corresponding iterators or to dereferencing an iterator and type T when T is part of the signature returns a value testable as true. In other words, iIf an algorithm takes BinaryPredicate binary_pred as its argument and first1 and first2 as its iterator arguments, it should work correctly in the construct binary_pred(*first1, *first2) contextually converted to bool (Clause 4)the expression binary_pred(*first1, *first2) shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). BinaryPredicate always takes the first iterator's value_type as its first argument, that is, in those cases when T value is part of the signature, it should work correctly in the construct binary_pred(*first1, value) contextually converted to bool (Clause 4)the expression binary_pred(*first1, value) shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). binary_pred shall not apply any non-constant function through the dereferenced iteratorsany of its arguments.

  9. Change 24.7 [alg.sorting] p2 as indicated:

    […]

    -2- Compare is a function object type (20.9). The return value of the function call operation applied to an object of type Compare, when contextually converted to bool(Clause 4), yields true if the first argument of the call is less than the second, and false otherwise. Compare comp is used throughout for algorithms assuming an ordering relation. Let a and b denote two argument values whose types depend on the corresponding algorithm. Then the expression comp(a, b) shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). The return value of comp(a, b), converted to bool, yields true if the first argument a is less than the second argument b, and false otherwise. It is assumed that comp will not apply any non-constant function through the dereferenced iteratorany of its arguments.

    […]

  10. Change 28.5.4.2 [fpos.operations] and Table 126 — "Position type requirements" as indicated:

    -1- Operations specified in Table 126 are permitted. In that table,

    • P refers to an instance of fpos,

    • […]

    • o refers to a value of type streamoff,

    • BT refers to a type that meets the BooleanTestable requirements ([booleantestable.requirements]),

    • […]

    Table 126 — Position type requirements
    Expression Return type […]
    p == q convertible to boolBT […]
    p != q convertible to boolBT […]
  11. Change 31.2.1 [thread.req.paramname] p1 as indicated:

    -1- Throughout this Clause, the names of template parameters are used to express type requirements. If a template parameter is named Predicate, operator() applied to the template argument shall return a value that is convertible to boolPredicate is a function object type (19.14 [function.objects]). Let pred denote an lvalue of type Predicate. Then the expression pred() shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). The return value of pred(), converted to bool, yields true if the corresponding test condition is satisfied, and false otherwise.


2115(i). Undefined behaviour for valarray assignments with mask_array index?

Section: 25.7.8 [template.mask.array] Status: Open Submitter: Thomas Plum Opened: 2011-12-10 Last modified: 2017-02-03

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 25.7.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, 25.7.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 *this array 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 *this array.

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 *this array is assigned the value of the corresponding element of the argument array. If the length of v is not equal to the length of *this, resizes *this to make the two arrays the same length, as if by calling resize(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 HERE

refer to mask_array assignment 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 sized valarray and I perfectly agree with you.

But, as already stated, this is not a simple valarray assignment but a mask_array assignment (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
[....]

  1. This template is a helper template used by the mask subscript operator: mask_array<T> valarray<T>::operator[](const valarray<bool>&).

  2. 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 of b to the masked elements in a (those for which the corresponding element in mask is 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:


2116(i). is_nothrow_constructible and destructors

Section: 19.15.4.3 [meta.unary.prop] Status: Open Submitter: Dave Abrahams Opened: 2011-12-09 Last modified: 2018-11-11

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 X is defined as deleted (9.4.3 [dcl.fct.def.delete]) if X has:
[…]
— 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

Ville would like "an evolution group" to take a look at this issue.

Proposed resolution:


2117(i). ios_base manipulators should have showgrouping/noshowgrouping

Section: 27.4.2.2.2 [facet.num.put.virtuals], 28.5.3.1.2 [ios::fmtflags], 28.5.6.1 [fmtflags.manip] Status: Open Submitter: Benjamin Kosnik Opened: 2011-12-15 Last modified: 2017-02-03

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 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]

Do we think this feature should exist?
SFFNASA
2 4100

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.

  1. Insert in 27.4.2.2.2 [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));
    
  2. 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);
      […]
    }
    
  3. Change class ios_base synopsis, 28.5.3 [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 ;
        […]
      };
    }
    
  4. Add a new entry to Table 122 — "fmtflags effects" as indicated:

    Table 122 — fmtflags effects
    Element Effect(s) if set
    […]
    showpoint generates a decimal-point character unconditionally in generated floatingpoint output
    showgrouping generates grouping characters unconditionally in generated output
    […]
  5. After 28.5.3.1.2 [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:


2136(i). Postconditions vs. exceptions

Section: 15.4.1 [structure] Status: Open Submitter: Jens Maurer Opened: 2012-03-08 Last modified: 2017-02-03

Priority: 3

View all issues with Open status.

Discussion:

The front matter in clause 17 should clarify that postconditions will not hold if a standard library function exits via an exception. Postconditions or guarantees that apply when an exception is thrown (beyond the basic guarantee) are described in an "Exception safety" section.

[ 2012-10 Portland: Move to Open ]

Consensus that we do not clearly say this, and that we probably should. A likely location to describe the guarantees of postconditions could well be a new sub-clause following 15.5.4.11 [res.on.required] which serves the same purpose for requires clauses. However, we need such wording before we can make progress.

Also, see 2137 for a suggestion that we want to see a paper resolving both issues together.

[2015-05-06 Lenexa: EirkWF to write paper addressing 2136 and 2137]

MC: Idea is to replace all such "If no exception" postconditions with "Exception safety" sections.

Proposed resolution:


2137(i). Misleadingly constrained post-condition in the presence of exceptions

Section: 29.8.2 [re.regex.assign] Status: Open Submitter: Jonathan Wakely Opened: 2012-03-08 Last modified: 2017-02-03

Priority: 3

View all other issues in [re.regex.assign].

View all issues with Open status.

Discussion:

The post-conditions of basic_regex<>::assign 29.8.2 [re.regex.assign] p16 say:

If no exception is thrown, flags() returns f and mark_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), 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. 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 is a paper providing the clause 17 wording that gives the guarantee for postcondition paragaraphs, and then reviews clauses 18-30 to apply that guarantee consistently. We do not want to pick up these issues piecemeal, as we risk openning many issues in an ongoing process.

[2015-05-06 Lenexa: EirkWF to write paper addressing 2136 and 2137]

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 in f. If an exception is thrown, *this is unchanged.

-16- Postconditions: If no exception is thrown, flags() returns f and mark_count() returns the number of marked sub-expressions within the expression.


2146(i). Are reference types Copy/Move-Constructible/Assignable or Destructible?

Section: 15.5.3.1 [utility.arg.requirements] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-03-23 Last modified: 2018-06-11

Priority: 2

View all other issues in [utility.arg.requirements].

View all issues with Open status.

Discussion:

According to 15.5.3.1 [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, T is an object or reference type to be supplied by a C++ program instantiating a template; a, b, and c are values of type (possibly const) T; s and t are modifiable lvalues of type T; u denotes an identifier; rv is an rvalue of type T; and v is an lvalue of type (possibly const) T or an rvalue of type const 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?

  1. #include <iostream>
    #include <utility>
    
    int main()
    {
      int x = 1;
      int y = 2;
      std::swap<int &&>(x, y); // undefined?
      std::cout << x << " " << y << std::endl;
    }
    
  2. #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: F and each Ti in Args shall satisfy the MoveConstructible requirements.

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, some overlap was found between these two issues. Daniel suggested to take responsibility for surveying LWG 2146 and opined that the P/R of LWG 2716 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.

Proposed resolution:


2152(i). Instances of standard container types are not swappable

Section: 15.5.3.2 [swappable.requirements], 21.2.1 [container.requirements.general] Status: LEWG Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2018-06-22

Priority: 3

View all other issues in [swappable.requirements].

View all issues with LEWG status.

Discussion:

Sub-clause 15.5.3.2 [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 21.2.1 [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 19.2.2 [utility.swap] p4, 19.4.2 [pairs.pair] p31, 19.5.3.3 [tuple.swap] p2, 24.6.3 [alg.swap] p2, and 24.6.3 [alg.swap] p6, which use the binary definition). This renders many of the mutating sequence algorithms of sub-clause 24.6 [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:

  1. Modify the requirements of algorithms from sub-clause 24.6 [alg.modifying.operations], and all other places that reference the unary "swappable" definition, to instead use the binary "swappable with" definition (over a domain appropriate to the context). The unary definition of "swappable" could then be removed from the standard.
  2. Modify sub-clause 21.2.1 [container.requirements.general] such that objects of standard container types are "swappable" by the unary definition.

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.


2153(i). Narrowing of the non-member swap contract

Section: 19.2.2 [utility.swap], 15.5.3.2 [swappable.requirements], 21.2.1 [container.requirements.general] Status: LEWG Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2018-06-22

Priority: 2

View all other issues in [utility.swap].

View all issues with LEWG status.

Discussion:

Sub-clause 19.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 15.5.3.2 [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 15.5.3.2 [swappable.requirements] is as a guide to the "right" semantics to provide for a non-member swap function (called in the context defined by 15.5.3.2 [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 19.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 telocon, and there is no proposed resolution.

[2013-03-15 Issues Teleconference]

Moved to Open.

This topic deserves more attention than can be given in the telocon, 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.

Proposed resolution:

Apply P0178.

2154(i). What exactly does compile-time complexity imply?

Section: 25.6.2.3 [rand.req.urng] Status: New Submitter: John Salmon Opened: 2012-04-26 Last modified: 2017-02-03

Priority: 4

View other active issues in [rand.req.urng].

View all other issues in [rand.req.urng].

View all issues with New status.

Discussion:

The expressions G::min() and G::max() in Table 116 in 25.6.2.3 [rand.req.urng] are specified as having "compile-time" complexity.

It is not clear what, exactly, this requirement implies. If a URNG has a method:

static int min();

then is the method required to have a constexpr qualifier? I believe the standard would benefit from clarification of this point.

Proposed resolution:


2155(i). Macro __bool_true_false_are_defined should be removed

Section: 16.12 [support.runtime] Status: Open Submitter: Thomas Plum Opened: 2012-04-30 Last modified: 2017-03-13

Priority: 4

View all other issues in [support.runtime].

View all issues with Open status.

Discussion:

Since C99, the C standard describes a macro named __bool_true_false_are_defined.

In the process of harmonizing C++11 with C99, this name became part of the C++ standard.

I propose that all mention of this name should be removed from the C and C++ standards.

Here's the problem: The name was originally proposed as a transition tool, so that the headers for a project could contain lines like the following.

#if !defined(__bool_true_false_are_defined)
#define bool int /* or whatever */
#define true 1
#define false 0
#endif

Then when the project was compiled by a "new" compiler that implemented bool as defined by the evolving C++98 or C99 standards, those lines would be skipped; but when compiled by an "old" compiler that didn't yet provide bool, true, and false, then the #define's would provide a simulation that worked for most purposes.

It turns out that there is an unfortunate ambiguity in the name. One interpretation is as shown above, but a different reading says "bool, true, and false are #define'd", i.e. that the meaning of the macro is to assert that these names are macros (not built-in) ... which is true in C, but not in C++.

In C++11, the name appears in parentheses followed by a stray period, so some editorial change is needed in any event:

16.12 [support.runtime] para 1:

Headers <csetjmp> (nonlocal jumps), <csignal> (signal handling), <cstdalign> (alignment), <cstdarg> (variable arguments), <cstdbool> (__bool_true_false_are_defined). <cstdlib> (runtime environment getenv(), system()), and <ctime> (system clock clock(), time()) provide further compatibility with C code.

However, para 2 says

"The contents of these headers are the same as the Standard C library headers <setjmp.h>, <signal.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stdlib.h>, and <time.h>, respectively, with the following changes:",

and para 8 says

"The header <cstdbool> and the header <stdbool.h> shall not define macros named bool, true, or false."

Thus para 8 doesn't exempt the C++ implementation from the arguably clear requirement of the C standard, to provide a macro named __bool_true_false_are_defined defined to be 1.

Real implementations of the C++ library differ, so the user cannot count upon any consistency; furthermore, the usefulness of the transition tool has faded long ago.

That's why my suggestion is that both C and C++ standards should eliminate any mention of __bool_true_false_are_defined. In that case, the name belongs to implementers to provide, or not, as they choose.

[2013-03-15 Issues Teleconference]

Moved to Open.

While not strictly necessary, the clean-up look good.

We would like to hear from our C liaison before moving on this issue though.

[2015-05 Lenexa]

LWG agrees. Jonathan provides wording.

[2017-03-04, Kona]

The reference to <cstdbool> in p2 needs to be struck as well. Continue the discussion on the reflector once the DIS is available.

Proposed resolution:

This wording is relative to N4296.

  1. Edit the footnote on 15.5.1.2 [headers] p7:

    176) In particular, including any of the standard headers <stdbool.h>, <cstdbool>, <iso646.h> or <ciso646> has no effect.

  2. Edit 16.12 [support.runtime] p1 as indicated (and remove the index entry for __bool_true_false_are_defined):

    -1- Headers <csetjmp> (nonlocal jumps), <csignal> (signal handling), <cstdalign> (alignment), <cstdarg> (variable arguments), <cstdbool>, (__bool_true_false_are_defined). <cstdlib> (runtime environment getenv(), system()), and <ctime> (system clock clock(), time()) provide further compatibility with C code.

  3. Remove Table 38 — Header <cstdbool> synopsis [tab:support.hdr.cstdbool] from 16.12 [support.runtime]

    Table 38 — Header <cstdbool> synopsis
    TypeName(s)
    Macro:__bool_true_false_are_defined

2157(i). How does std::array<T,0> initialization work when T is not default-constructible?

Section: 21.3.7.5 [array.zero] Status: Open Submitter: Daryle Walker Opened: 2012-05-08 Last modified: 2018-09-01

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 21.3.7.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 T is 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 21.3.7.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 21.3.7.5 [array.zero] p1 and p2:

-1- array shall provide support for the special case N == 0.

-?- 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 T is not default-constructible.

-2- In the case that N == 0, begin() == end() == unique value. The return value of data() is unspecified.

-3- The effect of calling front() or back() for a zero-sized array is undefined.

-4- Member function swap() shall have a noexcept-specification which is equivalent to noexcept(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 21.3.7.5 [array.zero] as indicated:

-1- array shall provides support for the special case of a zero-sized array that is always empty, i.e. N == 0, with the properties described in this subclause.

-?- A zero-sized array type is an aggregate that meets the DefaultConstructible (Table 22) and CopyConstructible (Table 24) requirements. There is a single element of the aggregate, of an unspecified DefaultConstructible type. [Note: This allows initialization of the form array<T, 0> a = {{}};. There is no requirement for T to be DefaultConstructible. — end note]

-2- In the case that N == 0, begin() == end() == unique valuebegin() and end() return non-dereferenceable iterators such that begin() == end() and a.begin() != b.begin() where a and b are distinct objects of the same zero-sized array type. The return value of data() is unspecified.

-3- The effect of calling front() or back() 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 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.

Proposed resolution:

This wording is relative to N4762.

Modify 21.3.7.5 [array.zero] as indicated:

-1- array shall provides support for the special case of a zero-sized array that is always empty, i.e. N == 0, with the properties described in this subclause.

-?- A zero-sized array type 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 form array<T, 0> a = {{}};. There is no requirement for T to be Cpp17DefaultConstructible. — end note]

-2- In the case that N == 0, begin() == end() == unique valuebegin() and end() return non-dereferenceable iterators such that begin() == end(). When a and b are distinct objects of the same zero-sized array type, a.begin() and b.begin() are not iterators over the same underlying sequence. [Note: Therefore begin() does not return a value-initialized iterator — end note]. The return value of data() is unspecified.

-3- The effect of calling front() or back() for a zero-sized array is undefined.

-4- Member function swap() shall havehas constant complexity and a non-throwing exception specification.


2158(i). Conditional copy/move in std::vector

Section: 21.3.11.3 [vector.capacity] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-05-08 Last modified: 2018-08-27

Priority: 3

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 – 21.3.11.3 [vector.capacity]/2:

Requires: T shall be MoveInsertable into *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:

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.

21.2.1 [container.requirements.general] Table 80 — Container requirements
Expression Return type Operational semantics Assertion/note/pre-/post-condition Complexity
X(a) Requires: T is CopyInsertable into X (see below)., otherwise this expression shall be ill-formed.
post: a == X(a).
linear
X u(a)
X u = a;
Requires: T is CopyInsertable into X (see below)., otherwise this expression shall be ill-formed.
post: u == a.
linear
... ... ... ... ...
r = a X& Requires: T is CopyInsertable into X and CopyAssignable, otherwise this expression shall be ill-formed.
post: r == a.
linear

21.2.1 [container.requirements.general] Table 83 — Allocator-aware container requirements
Expression Return type Operational semantics Assertion/note/pre-/post-condition Complexity
a = t X& Requires: T is CopyInsertable into X and CopyAssignable., 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.

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.

21.2.1 [container.requirements.general] Table 77 — Container requirements
Expression Return type Operational semantics Assertion/note/pre-/post-condition Complexity
X(a) Mandates: Syntactic requirements of T
is Cpp17CopyInsertable into X (see below).

Requires: T is Cpp17CopyInsertable into X (see below).
post: a == X(a).
linear
X u(a)
X u = a;
Mandates: Syntactic requirements of T
is Cpp17CopyInsertable into X (see below).

Requires: T is Cpp17CopyInsertable into X (see below).
post: u == a.
linear
... ... ... ... ...
r = a X& Mandates: Syntactic requirements of T
is Cpp17CopyInsertable into X (see below) and CopyAssignable.
Requires: T is Cpp17CopyInsertable into X and CopyAssignable.
post: r == a.
linear

21.2.1 [container.requirements.general] Table 80 — Allocator-aware container requirements
Expression Return type Operational semantics Assertion/note/pre-/post-condition Complexity
a = t X& Mandates: Syntactic requirements of T is
Cpp17CopyInsertable into X and CopyAssignable.
Requires: T is Cpp17CopyInsertable into X and CopyAssignable.
post: r == a.
linear


2173(i). The meaning of operator + in the description of the algorithms

Section: 24 [algorithms] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-08-01 Last modified: 2018-06-11

Priority: 4

View other active issues in [algorithms].

View all other issues in [algorithms].

View all issues with Open status.

Discussion:

According to 24.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 of a+n is the same as that of

X 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 from first and proceeding to last. For each non-negative integer n < (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.

  1. Change 24.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 of a+n is the same as that of

    X tmp = a;
    advance(tmp, n);
    return tmp;
    

    when X meets the input iterator requirements (22.3.5.2 [input.iterators]), otherwise it is the same as that of

    X 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 of b-a is the same as of

    return distance(a, b);
    

2189(i). Throwing swap breaks unordered containers' state

Section: 21.2.7.1 [unord.req.except] Status: Open Submitter: Alisdair Meredith Opened: 2012-09-23 Last modified: 2017-02-03

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.

21.2.7.1 [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.

Proposed resolution:


2191(i). Incorrect specification of match_results(match_results&&)

Section: 29.10.1 [re.results.const] Status: New Submitter: Pete Becker Opened: 2012-10-02 Last modified: 2017-02-03

Priority: 4

View all other issues in [re.results.const].

View all issues with New status.

Discussion:

29.10.1 [re.results.const]/3: "Move-constructs an object of class match_results satisfying the same postconditions as Table 141."

Table 141 lists various member functions and says that their results should be the results of the corresponding member function calls on m. But m has been moved from, so the actual requirement ought to be based on the value that m had before the move construction, not on m itself.

In addition to that, the requirements for the copy constructor should refer to Table 141.

Ganesh:

Also, the requirements for move-assignment should refer to Table 141. Further it seems as if in Table 141 all phrases of "for all integers n < m.size()" should be replaced by "for all unsigned integers n < m.size()".

Proposed resolution:


2195(i). Missing constructors for match_results

Section: 29.10 [re.results] Status: Open Submitter: Daniel Krügler Opened: 2012-10-06 Last modified: 2017-02-03

Priority: 3

View all other issues in [re.results].

View all issues with Open status.

Discussion:

The requirement expressed in 29.10 [re.results] p2

The class template match_results shall satisfy the requirements of an allocator-aware container and of a sequence container, as specified in 21.2.3 [sequence.reqmts], except that only operations defined for const-qualified sequence containers are supported.

can be read to require the existence of the described constructors from as well, but they do not exist in the synopsis.

The missing sequence constructors are:

match_results(initializer_list<value_type>);
match_results(size_type, const value_type&);
template<class InputIterator> match_results(InputIterator, InputIterator);

The missing allocator-aware container constructors are:

match_results(const match_results&, const Allocator&);
match_results(match_results&&, const Allocator&);

It should be clarified, whether (a) constructors are an exception of above mentioned operations or (b) whether at least some of them (like those accepting a match_results value and an allocator) should be added.

As visible in several places of the standard (including the core language), constructors seem usually to be considered as "operations" and they certainly can be invoked for const-qualified objects.

The below given proposed resolution applies only the minimum necessary fix, i.e. it excludes constructors from above requirement.

[2013-04-20, Bristol]

Check current implementations to see what they do and, possibly, write a paper.

[2013-09 Chicago]

Ask Daniel to update the proposed wording to include the allocator copy and move constructors.

[2014-01-18 Daniel changes proposed resolution]

Previous resolution from Daniel [SUPERSEDED]:

  1. Change 29.10 [re.results] p2 as indicated:

    The class template match_results shall satisfy the requirements of an allocator-aware container and of a sequence container, as specified in 21.2.3 [sequence.reqmts], except that only operations defined for const-qualified sequence containers that are not constructors are supported.

[2015-05-06 Lenexa]

MC passes important knowledge to EF.

VV, RP: Looks good.

TK: Second form should be conditionally noexcept

JY: Sequence constructors are not here, but mentioned in the issue writeup. Why?

TK: That would have been fixed by the superseded wording.

JW: How does this interact with Mike Spertus' allocator-aware regexes? [...] Perhaps it doesn't.

JW: Can't create match_results, want both old and new resolution.

JY: It's problematic that users can't create these, but not this issue.

VV: Why conditional noexcept?

MC: Allocator move might throw.

JW: Update superseded wording to "only non-constructor operations that are"?

MC: Only keep superseded, but append "and the means of constructing match_results are limited to [...]"?

JY: Bullet 4 paragraph 2 needs to address the allocator constructor.

Assigned to JW for drafting.

[2015-10, Kona Saturday afternoon]

STL: I want Mike Spertus to be aware of this issue.

Proposed resolution:

This wording is relative to N3936.

  1. Change 29.10 [re.results] p4, class template match_results synopsis, as indicated:

    […]
    // 28.10.1, construct/copy/destroy:
    explicit match_results(const Allocator& a = Allocator());
    match_results(const match_results& m);
    match_results(const match_results& m, const Allocator& a);
    match_results(match_results&& m) noexcept;
    match_results(match_results&& m, const Allocator& a) noexcept;
    […]
    
  2. Change 29.10.1 [re.results.const] as indicated: [Drafting note: Paragraph 6 as currently written, makes not much sense, because the noexcept does not allow any exception to propagate. Further-on, the allocator requirements do not allow for throwing move constructors. Deleting it seems to be near to editorial — end drafting note]

    match_results(const match_results& m);
    match_results(const match_results& m, const Allocator& a);
    

    -4- Effects: Constructs an object of class match_results, as a copy of m.

    match_results(match_results&& m) noexcept;
    match_results(match_results&& m, const Allocator& a) noexcept;
    

    -5- Effects: Move-constructs an object of class match_results from m satisfying the same postconditions as Table 142. AdditionallyFor the first form, the stored Allocator value is move constructed from m.get_allocator().

    -6- Throws: Nothing if the allocator's move constructor throws nothing.


2198(i). max_load_factor(z) makes no strong guarantees, but bans useful behavior

Section: 21.2.7 [unord.req] Status: Open Submitter: Alisdair Meredith Opened: 2012-10-09 Last modified: 2017-02-03

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() to max(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 current load_factor() that can be done by first reducing the current load_factor() by either increasing bucket_count() (via rehash or reserve), or decreasing size() (e.g. erase), and then changing max_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 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."
Note that the current spec says that there is constant complexity without any precondition. So, rehashing to keep the invariant would violate the spec (which is probably not be the intention).

This issue is related to LWG 2199.

Proposed resolution:

Modify Table 87 as follows:

Table 87 — Unordered associative container requirements
Expression Return type Assertion/note pre-/post-condition Complexity
a.max_load_factor(z) void

Pre: z shall be positive. May change the container's maximum load factor, uing z as a hint.

Post: a.load_factor() <= a.max_load_factor()

Note: a.load_factor() is not modified by this operation.

Constant

2202(i). Missing allocator support by async

Section: 31.6.9 [futures.async] Status: Deferred Submitter: Detlef Vollmann Opened: 2012-10-19 Last modified: 2017-02-03

Priority: 4

View all other issues in [futures.async].

Discussion:

promise, packaged_task, and async are the only places where a shared state is actually supposed to be allocated. Accordingly, promise and packaged_task are "allocator-aware". But function template async provides no way to provide an allocator.

[2013-09 Chicago]

Matt: deprecate async

Nico: read my paper

Alisdair: defer issues to wait for polymorphic allocators

Alisdair: defer, active topic of research Deferred

[2014-02-20 Re-open Deferred issues as Priority 4]

[2015-05 Lenexa, SG1 response]

We want whatever status approximates: "will not fix; we're working on a replacement facility and don't want to add features to a broken one"

Proposed resolution:


2206(i). Inaccuracy in initializer_list constructor requirements

Section: 21.2.3 [sequence.reqmts], 21.2.6 [associative.reqmts], 21.2.7 [unord.req], 25.6.2.2 [rand.req.seedseq] Status: Open Submitter: Jeffrey Yasskin Opened: 2012-10-21 Last modified: 2017-02-03

Priority: 3

View other active issues in [sequence.reqmts].

View all other issues in [sequence.reqmts].

View all issues with Open status.

Discussion:

In 21.2.3 [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 21.2.6 [associative.reqmts], 21.2.7 [unord.req], and 25.6.2.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.

Proposed resolution:


2214(i). Clarify basic_ios::init call restrictions

Section: 28.5.5.2 [basic.ios.cons] Status: Open Submitter: Andrey Semashev Opened: 2012-11-09 Last modified: 2017-02-03

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 (28.5.5.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 (28.5.5.2 [basic.ios.cons]) there is this sentence:

Effects: Constructs an object of class basic_ios (28.5.3.7 [ios.base.cons]) leaving its member objects uninitialized. The object shall be initialized by calling basic_ios::init before 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 (28.5.3 [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 (28.5.3.7 [ios.base.cons]) initializing its member objects to unspecified state, only suitable for basic_ios destruction. The object shall be initialized by calling basic_ios::init before 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.

Proposed resolution:

This wording is relative to N3485.

  1. Edit 28.5.5.2 [basic.ios.cons] as indicated:

    basic_ios();
    

    -2- Effects: Constructs an object of class basic_ios (28.5.3.7 [ios.base.cons]) leaving its member objects uninitializedinitializing its member objects to unspecified state, only suitable for basic_ios destruction. The object shall be initialized by calling basic_ios::init before its first use or 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.


2215(i). (unordered) associative container functors should be CopyConstructible

Section: 21.2.6 [associative.reqmts], 21.2.7 [unord.req] Status: Open Submitter: Alisdair Meredith Opened: 2012-11-14 Last modified: 2017-02-03

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
X(n, hf, eq)
X a(n, hf, eq)
Requires: hasher and key_equal are CopyConstructible.
X(n, hf)
X a(n, hf)
Requires: hasher is CopyConstructible and key_equal is DefaultConstructible.
X(n)
X a(n)
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 addresses.

[2015-10-20, Daniel comments]

The revised resolution of LWG 2227 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.


2216(i). regex_replace(basic_string) allocator handling

Section: 29.11.4 [re.alg.replace] Status: New Submitter: Jeffrey Yasskin Opened: 2012-11-26 Last modified: 2017-02-03

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 calls regex_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:


2220(i). Under-specification of operator== for regex_token_iterator

Section: 29.12.2.2 [re.tokiter.comp] Status: Open Submitter: Pete Becker Opened: 2012-11-21 Last modified: 2018-08-27

Priority: 3

View all issues with Open status.

Discussion:

Consider the following example:

std::string str0("x");
std::regex rg0("a");
std::regex_token_iterator it0(str0.begin(), str0.end(), rg0, -1); // points at "x" in str0
std::string str1("x");
std::regex rg1("b");
std::regex_token_iterator it1(str1.begin(), str1.end(), rg1, -1); // points at "x" in str1

29.12.2.2 [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.

Previous resolution [SUPERSEDED]:

This wording is relative to N4762.

[2018-08-23 Casey revises the P/R in response to LWG feedback]

Proposed resolution:

This wording is relative to N4762.


2226(i). wstring_convert methods do not take allocator instance

Section: D.14.1 [depr.conversions.string] Status: Tentatively NAD Submitter: Glen Fernandes Opened: 2012-12-14 Last modified: 2018-06-24

Priority: Not Prioritized

View other active issues in [depr.conversions.string].

View all other issues in [depr.conversions.string].

View all issues with Tentatively NAD status.

Discussion:

The wstring_convert class template, described in D.14.1 [depr.conversions.string], does not support custom stateful allocators. It only supports custom stateless allocators.

The to_bytes member function returns basic_string<char, char_traits<char>, Byte_alloc> but it does not take an instance of Byte_alloc to pass to the constructor of the basic_string.

Similarly the from_bytes member function returns basic_string<Elem, char_traits<Elem>, Wide_alloc> but it does not take an instance of Wide_alloc to pass to the constructor of the basic_string.

This makes these two member functions and the wstring_convert class template not usable when Wide_alloc or Byte_alloc are stateful allocators.

[2013-01-22, Glen provides wording]

[2013-03-15 Issues Teleconference]

Moved to NAD Future.

This is clearly an extension that the LEWG may want to take a look at, once we have more experience with appropriate use of allocators with the C++11 model.

[LEWG Kona 2017]

Recommend NAD: Does this follow the pattern? Should be discussed as a group. Do we have the experience with the C++11 allocator model to know that this is the addition to make?

Should to_string() also take an allocator? substr()? Any function that returns a string?

This suggests a larger change.

Proposed resolution:

This wording is relative to N3485.

  1. In D.14.1 [depr.conversions.string]/2 and /6 "Class template wstring_convert synopsis" change the overloads of the member function from_bytes() so that all four overloads take an additional parameter which is an instance of Wide_alloc:

    wide_string from_bytes(char byte, const Wide_alloc& alloc = Wide_alloc());
    wide_string from_bytes(const char *ptr, const Wide_alloc& alloc = Wide_alloc());
    wide_string from_bytes(const byte_string& str, const Wide_alloc& alloc = Wide_alloc());
    wide_string from_bytes(const char *first, const char *last, const Wide_alloc& alloc = Wide_alloc());
    
  2. In D.14.1 [depr.conversions.string] /8 specify that this Wide_alloc allocator parameter is used to construct the wide_string object returned from the function:

    -7- Effects: The first member function shall convert the single-element sequence byte to a wide string. The second member function shall convert the null-terminated sequence beginning at ptr to a wide string. The third member function shall convert the sequence stored in str to a wide string. The fourth member function shall convert the sequence defined by the range [first, last) to a wide string.

    -8- In all cases:

  3. In D.14.1 [depr.conversions.string]/2 and /12 "Class template wstring_convert synopsis" change the overloads of the member function to_bytes() so that all four overloads take an additional parameter which is an instance of Byte_alloc:

    byte_string to_bytes(Elem wchar, const Byte_alloc& alloc = Byte_alloc());
    byte_string to_bytes(const Elem *wptr, const Byte_alloc& alloc = Byte_alloc());
    byte_string to_bytes(const wide_string& wstr, const Byte_alloc& alloc = Byte_alloc());
    byte_string to_bytes(const Elem *first, const Elem *last, const Byte_alloc& alloc = Byte_alloc());
    
  4. In D.14.1 [depr.conversions.string] /13 specify that this Byte_alloc allocator parameter is used to construct the byte_string object returned from the function:

    -12- Effects: The first member function shall convert the single-element sequence wchar to a byte string. The second member function shall convert the null-terminated sequence beginning at wptr to a byte string. The third member function shall convert the sequence stored in wstr to a byte string. The fourth member function shall convert the sequence defined by the range [first, last) to a byte string.

    -13- In all cases:


2227(i). Stateful comparison objects in associative containers

Section: 21.2.6 [associative.reqmts] Status: Open Submitter: Juan Soulie Opened: 2012-12-19 Last modified: 2017-02-03

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 21.2.6 [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, 21.2.1 [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 21.2.1 [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 21.2.1 [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:

[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.

  1. 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 = b X& Copy assignment operator. In addition to
    the requirements of Table 96, copies the
    comparison object.
    Linear in a.size() and b.size()
    a.key_comp() X::key_compare rReturns thea'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 21.6.5.3 [priqueue.cons.alloc] p7:

template <class Alloc> priority_queue(priority_queue&& q, const Alloc& a);

-7- Effects: Initializes c with std::move(q.c) as the first argument and a as the second argument, and initializes comp with std::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 21.2.6 [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:

  1. 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).

  2. 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.

  3. 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:

  1. Visual Studio 2015 uses copy construction and copy assignment for the two copy operations but uses swap operations for the move operations.

  2. GCC's libstdc++ performs copy construction and copy assignment for the two copy operations and for the two move operations, respectively

  3. 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 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.

  1. Change 21.2.6 [associative.reqmts] p8 as indicated:

    -8- In Table 101, X denotes an associative container class, a denotes a value of type X, b denotes a possibly const value of type X, rv denotes a non-const rvalue of type X, u denotes the name of a variable being declared, […]

  2. 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_compare Compare Requires: Compare is CopyConstructible.
    defaults to less<key_type>
    compile time
    X(c)
    X u(c);
    Requires: key_compare is CopyConstructible.
    Effects: Constructs an empty container.
    Uses a copy of c as a comparison object.
    […]
    X(i,j,c)
    X u(i,j,c);
    Requires: key_compare is CopyConstructible.
    value_type is EmplaceConstructible into X from *i.
    Effects: Constructs an empty container and inserts elements
    from the range [i, j) into it; uses c as 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 of a from
    the comparison object of b.
    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 of a from
    the comparison object of rv.
    constant
    a = b X& (In addition to the requirements of Table 95 and Table 98)
    Requires: key_compare is CopyAssignable.
    Effects: Copy assigns the comparison object of b
    to the comparison object of a.
    Linear in a.size() and b.size()
    a = rv X& (In addition to the requirements of Table 95 and Table 98)
    Requires: key_compare is MoveAssignable.
    Effects: Move assigns from the comparison object of rv
    to the comparison object of a.
    Linear
    a.key_comp() X::key_compare rReturns thea's comparison object
    out of which a was constructed.
    constant
  3. Change 21.2.6 [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.

  4. Change 21.2.7 [unord.req] p11 as indicated:

    -11- In Table 102: X denotes an unordered associative container class, a denotes a value of type X, b denotes a possibly const value of type X, rv denotes a non-const rvalue of type X, […]

  5. 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::hasher Hash Requires: Hash is CopyConstructible.
    Hash shall be a unary function object type
    such that the expression hf(k) has type std::size_t.
    compile time
    X::key_equal Pred Requires: Pred is CopyConstructible.
    Pred shall be a binary predicate that takes
    two arguments of type Key.
    Pred is an equivalence relation.
    compile time
    X(n, hf, eq)
    X a(n, hf, eq)
    X Requires: hasher and key_equal are CopyConstructible.
    Effects: […]
    […]
    X(n, hf)
    X a(n, hf)
    X Requires: hasher is CopyConstructible and
    key_equal is DefaultConstructible.
    Effects: […]
    […]
    X(i, j, n, hf, eq)
    X a(i, j, n, hf, eq)
    X Requires: hasher and key_equal are CopyConstructible.
    value_type is EmplaceConstructible into X from *i.
    Effects: […]
    […]
    X(i, j, n, hf)
    X a(i, j, n, hf)
    X Requires: hasher is CopyConstructible and
    key_equal is DefaultConstructible.
    value_type is EmplaceConstructible into X from *i.
    Effects: […]
    […]
    X(b)
    X a(b)
    X Copy constructor. In addition
    to the requirements of Table 95,
    copies the hash function,
    predicate, and maximum load
    factor.
    (In addition to the requirements of Table 95)
    Effects: Copy constructs the hash function, predicate, and maximum load factor
    of a from the corresponding objects of b.
    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
    of a from the corresponding objects of rv.
    constant
    a = b X& Copy assignment operator. In
    addition to the requirements of
    Table 95, copies the hash
    function, predicate, and
    maximum load factor.
    (In addition to the requirements of Table 95 and Table 98)
    Requires: hasher and key_equal are CopyAssignable.
    Effects: Copy assigns the hash function, predicate, and maximum load factor
    of b to the corresponding objects of a.
    Average case linear in
    b.size(),
    worst case quadratic.
    a = rv X& (In addition to the requirements of Table 95 and Table 98)
    Requires: hasher and key_equal are MoveAssignable.
    Effects: Move assigns the hash function, predicate, and maximum load factor
    from rv to the corresponding objects of a.
    Linear

[2016-08-07]

Daniel removes the previously proposed wording to work on revised wording.

Proposed resolution:


2236(i). kill_dependency unconditionally noexcept

Section: 30.2 [atomics.syn], 30.4 [atomics.order] Status: SG1 Submitter: Daniel Krügler Opened: 2013-01-19 Last modified: 2017-02-03

Priority: Not Prioritized

View all other issues in [atomics.syn].

View all issues with SG1 status.

Discussion:

The "magic" kill_dependency function is a function without any constraints on the template parameter T and is specified as

template <class T>
T kill_dependency(T y) noexcept;

-14- Effects: The argument does not carry a dependency to the return value (1.10).

-15- Returns: y.

I wonder whether the unconditional noexcept is really intended here: Assume we have some type U that has a potentially throwing move constructor (or it has a potentially throwing copy constructor and no move constructor), for any "normal" function template with the same signature and the same effects (modulo the dependency magic) this would mean that it cannot safely be declared noexcept because of the return statement being part of the complete function call affected by noexcept (The by-value function argument is irrelevant in this context). In other words it seems that a function call such as

struct S {
  ...
  S(const S& r) { if(some condition) throw Something(); }
  ...
};

int main() {
  S s1 = ...;
  S s2 = std::kill_dependency(s1);
}

would be required to call std::terminate if the copy constructor of S throws during the return of std::kill_dependency.

To require copy elision for this already magic function would look like a low-hanging fruit to solve this problem, but this case is not covered by current copy elision rules see 12.8 p31 b1:

"— in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value".

Some options come into my mind:

  1. Make the exception-specification a constrained one in regard via std::is_nothrow_move_constructible:

    template <class T>
    T kill_dependency(T y) noexcept(see below);
    

    This is similar to the approach taken for function templates such as std::swap.

  2. Use perfect forwarding (This needs further wording to correct the effects):

    template <class T>
    T&& kill_dependency(T&& y) noexcept;
    
  3. Impose constraints on the template arguments in regard to throwing exceptions while copying/moving.

  4. Keep the state as it is but possibly add a note about a call of std::terminate in above scenario.

A second problem is that the current wording is not clear whether it is well-defined to call the function with types that are reference types, such as in the following example:

#include <atomic>

int main()
{
  int a = 12;
  int& b = std::kill_dependency<int&>(a);
}

It is unclear what kind of dependency is killed here. This is presumably a core language problem, but could affect the possible resolutions of the problem.

[2014-11 Urbana]

Recommend using a revised example:

int lookup(class D* p) 
{
  class E* q = p->a.load(memory_order_consume);
  int y = std::kill_dependency(q->y);
}

[2015-02 Cologne]

Handed over to SG1.

Proposed resolution:


2237(i). <cuchar> macros

Section: 20.5 [c.strings] Status: New Submitter: Jason Merrill Opened: 2013-01-29 Last modified: 2017-02-03

Priority: 4

View other active issues in [c.strings].

View all other issues in [c.strings].

View all issues with New status.

Discussion:

Apparently C1X changes __STDC_UTF_16__ and __STDC_UTF_32__ from macros defined in uchar.h (and reflected in C++ by Table 79) to be predefined by the compiler. Do we want to do the same?

Proposed resolution:


2238(i). Problematic iterator-pair constructor of containers

Section: 20.5 [c.strings] Status: Open Submitter: Johannes Schaub Opened: 2013-02-02 Last modified: 2017-02-03

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:


2248(i). numeric_limits::is_iec559 misnamed

Section: 16.3.4 [numeric.limits] Status: New Submitter: Pete Becker Opened: 2013-03-08 Last modified: 2018-11-11

Priority: 4

View other active issues in [numeric.limits].

View all other issues in [numeric.limits].

View all issues with New status.

Discussion:

This member should probably be named "is_ieee754". Or at least the standard should explain that IEC-559 no longer exists, and that it's been superseded by IEEE-754.

[2016-06, Oulu]

The ISO version of the standard is ISO/IEC/IEEE 60559:2011, which C11 Annex F refers to as IEC 60559 (although C still refers to it as IEC 559 in the __STDC_IEC_559__ macro).

Proposed resolution:


2262(i). Requirement for unique_ptr<T>::get_deleter()(p) to be able to destroy the unique_ptr

Section: 19.11.1.2 [unique.ptr.single] Status: Open Submitter: Rob Desbois Opened: 2013-05-15 Last modified: 2017-05-31

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 19.11.1.2.5 [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 *this since this->get() is no longer a valid expression."

It seems this wording was created to resolve 998 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:

19.11.1.2 [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 19.11.1.2 [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 19.11.1.2.5 [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 19.11.1.2.5 [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:

  1. Edit 19.11.1.2 [unique.ptr.single] p1 as indicated:

    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.

  2. Edit 19.11.1.2.5 [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 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]

    -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]

Previous resolution [SUPERSEDED]:

This wording is relative to N3691.

  1. Edit 19.11.1.2 [unique.ptr.single] p1 as indicated:

    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. d(ptr) shall be valid even if it triggers the destruction of d or (if D is an lvalue reference to function object type) the function object that d refers 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 object d after it executes ptr->~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, 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.

  1. Edit 19.11.1.2 [unique.ptr.single] p1 as indicated:

    The default type for the template parameter D is default_delete. A client-supplied template argument D shall be a function object type (20.9), 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. The expression d(ptr), if it destructs the object referred to by ptr, shall not refer to the object d after it destructs *ptr. [Note: The object being destructed may control the lifetime of d. — end note]


2265(i). 29.3p9 appears to rule out some acceptable executions

Section: 30.4 [atomics.order] Status: Open Submitter: Brian Demsky Opened: 2013-06-17 Last modified: 2017-02-03

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 30.4 [atomics.order] p9 that justifies the store to z? It seems that 30.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 30.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 30.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:

  1. 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.

  2. 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:

  1. the evaluation y observes a value computed by the evaluation x or

  2. 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:


2267(i). partial_sort_copy underspecified for ranges of two different types

Section: 24.7.1.4 [partial.sort.copy] Status: New Submitter: Matt Austern Opened: 2013-06-26 Last modified: 2017-02-03

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:


2269(i). Container iterators and argument-dependent lookup

Section: 21.2.1 [container.requirements.general] Status: New Submitter: Matt Austern Opened: 2013-06-26 Last modified: 2017-02-03

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:


2286(i). stringbuf::underflow() underspecified

Section: 28.8.2.4 [stringbuf.virtuals] Status: Open Submitter: Sergey Zubkov Opened: 2013-08-29 Last modified: 2018-06-11

Priority: 4

View other active issues in [stringbuf.virtuals].

View all other issues in [stringbuf.virtuals].

View all issues with Open status.

Discussion:

In 28.8.2.4 [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 D.8.2.3 [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.

  1. Change 28.8.2.4 [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, returns traits::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) != 0 and if the write next pointer pptr() is not null and is greater than the current read end pointer egptr(). To make a read position available, the function alters the read end pointer egptr() to equal pptr().


2289(i). constexpr guarantees of defaulted functions still insufficient

Section: 19.4.2 [pairs.pair], 19.5.3.1 [tuple.cnstr], 26.5 [time.duration] Status: Open Submitter: Daniel Krügler Opened: 2013-09-09 Last modified: 2017-02-03

Priority: 3

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 constexpr function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a constexpr function.

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 overlaps considerably and both should be resolved together.

Proposed resolution:

This wording is relative to N3691.

  1. Change 19.4.2 [pairs.pair] p2 as indicated:

    -2- The defaulted move and copy constructor, respectively, of pair shall be a constexpr function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a constexpr functionAn invocation of the move or copy constructor of pair shall 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 of pair shall be a constant initializer for that pair object ( [basic.start.init]) if all required element-wise initializations would be constant initializers for the respective subobjects.

  2. Change 19.5.3.1 [tuple.cnstr] p2 as indicated:

    -2- The defaulted move and copy constructor, respectively, of tuple shall be a constexpr function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a constexpr function. The defaulted move and copy constructor of tuple<> shall be constexpr functionsAn invocation of the move or copy constructor of tuple shall 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 of tuple shall be a constant initializer for that tuple object ( [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 of tuple<> shall be a constant expression, or a constant initializer for that tuple<> object, respectively, if the function argument would be constant expression.

  3. Change 26.5 [time.duration] p7 as indicated:

    -7- Remarks: The defaulted copy constructor of duration shall be a constexpr function if and only if the required initialization of the member rep_ for copy and move, respectively, would satisfy the requirements for a constexpr function.An invocation of the copy constructor of duration shall be a constant expression (7.7 [expr.const]) if the required initialization of the member rep_ would be a constant expression. An invocation of the copy constructor of duration shall be a constant initializer for that duration object ( [basic.start.init]) if the required initialization of the member rep_ would be constant initializers for this subobject.


2290(i). Top-level "SFINAE"-based constraints should get a separate definition in Clause 17

Section: 19.15 [meta] Status: Open Submitter: Daniel Krügler Opened: 2013-09-02 Last modified: 2017-02-03

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 to T and U. 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 and 1391 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 19.14.16.2 [func.wrap.func] p2, most functions in 19.10.9.2 [allocator.traits.members], and the **Insertable, EmplaceConstructible, and Erasable definitions in 21.2.1 [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.

  1. Add the following new definition to 15.3 [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 (12.9.2 [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].

  2. 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>() =
    declval<U>()
    is valid in a
    contrived argument deduction context
    ([defns.valid.contr.context]) for types
    T and U.
    well-formed when treated
    as an unevaluated operand
    (Clause 5). Access
    checking is performed as if
    in a context unrelated to T
    and U. 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]
    […]
  3. Change 19.15.4.3 [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 definition would be well-formed for some invented variable t would be valid in a contrived argument deduction context ([defns.valid.contr.context]) for types T and Args...:

    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 to T and any of the Args. 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]

  4. 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>(),
    declval<ArgTypes>()...)
    is
    valid in a contrived argument deduction
    context ([defns.valid.contr.context]) for types
    Fn and ArgTypes...
    well
    formed when treated as an
    unevaluated operand (Clause 5)
    , the
    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 to Fn and
    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]
  5. Change 19.15.6 [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 be well-formedvalid in a contrived argument deduction context ([defns.valid.contr.context]) for types To and From, 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, void types, array types, and function types. — end note] Access checking is performed as if in a context unrelated to To and From. 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]


2292(i). Find a better phrasing for "shall not participate in overload resolution"

Section: 15.4.1.4 [structure.specifications] Status: New Submitter: Jeffrey Yasskin Opened: 2013-09-03 Last modified: 2017-02-03

Priority: 3

View other active issues in [structure.specifications].

View all other issues in [structure.specifications].

View all issues with New status.

Discussion:

The C++14 CD has 25 sections including the phrase "X shall not participate in overload resolution ...". Most of these uses are double negatives, which are hard to interpret. "shall not ... unless" tends to be the easiest to read, since the condition is true when the function is available, but we also have a lot of "if X is not Y, then Z shall not participate", which actually means "You can call Z if X is Y." The current wording is also clumsy and long-winded. We should find a better and more concise phrasing.

As an initial proposal, I'd suggest using "X is enabled if and only if Y" in prose and adding an "Enabled If: ..." element to 15.4.1.4 [structure.specifications].

Daniel:

I suggest to name this new specification element for 15.4.1.4 [structure.specifications] as "Template Constraints:" instead, because the mentioned wording form was intentionally provided starting with LWG 1237 to give implementations more freedom to realize the concrete constraints. Instead of the original std::enable_if-based specifications we can use better forms of "SFINAE" constraints today and it eases the path to possible language-based constraints in the future.

Proposed resolution:


2295(i). Locale name when the provided Facet is a nullptr

Section: 27.3.1.2 [locale.cons] Status: New Submitter: Juan Soulie Opened: 2013-09-04 Last modified: 2017-02-03

Priority: 3

View all issues with New status.

Discussion:

27.3.1.2 [locale.cons] p14 ends with:

"[…] If f is null, the resulting object is a copy of other."

but the next line p15 says:

"Remarks: The resulting locale has no name."

But both can't be true when other has a name and f is null.

I've tried it on two implementations (MSVC,GCC) and they are inconsistent with each other on this.

Daniel Krügler:

As currently written, the Remarks element applies unconditionally for all cases and thus should "win". The question arises whether the introduction of this element by LWG 424 had actually intended to change the previous Note to a Remarks element. In either case the wording should be improved to clarify this special case.

Proposed resolution:


2303(i). Explicit instantiation of std::vector<UserType> broken?

Section: 16.6.2.3 [new.delete.placement] Status: New Submitter: Daniel Krügler Opened: 2013-09-18 Last modified: 2017-02-03

Priority: 3

View all other issues in [new.delete.placement].

View all issues with New status.

Discussion:

The library gives explicit permission in 15.5.4.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 15.5.3.1 [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:

  1. As indicated in LWG 2292, those function could get an explicit "Template Constraints:" element, albeit this promises more than needed to solve this issue.

  2. 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 could be resolved by a similar solution as indicated in this issue here.

Proposed resolution:


2307(i). Should the Standard Library use explicit only when necessary?

Section: 21 [containers] Status: LEWG Submitter: Zhihao Yuan Opened: 2013-09-26 Last modified: 2018-11-11

Priority: 2

View other active issues in [containers].

View all other issues in [containers].

View all issues with LEWG status.

Discussion:

LWG 2193 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:


2321(i). Moving containers should (usually) be required to preserve iterators

Section: 21.2.1 [container.requirements.general] Status: Open Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2018-08-27

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:

21.2.1 [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

Proposed resolution:

This wording is relative to N3691.

  1. In 21.2.1 [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::value is true) of a container (except for array) invalidates any references, pointers, or iterators referring to the elements of the source container. [Note: The end() 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: The end() iterator does not refer to any element, so it may be invalidated. — end note]


2331(i). regex_constants::collate's effects are inaccurately summarized

Section: 29.5.1 [re.synopt] Status: Open Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2017-02-03

Priority: 3

View all other issues in [re.synopt].

View all issues with Open status.

Discussion:

The table in 29.5.1 [re.synopt]/1 says that regex_constants::collate "Specifies that character ranges of the form "[a-b]" shall be locale sensitive.", but 29.13 [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 29.5.1 [re.synopt]/1's table should be left alone, while 29.13 [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.

  1. In 29.5.1 [re.synopt]/1, Table 138 — "syntax_option_type effects", change as indicated:

    Table 138 — syntax_option_type effects
    Element Effect(s) if set
    collate Specifies that character ranges of the form "[a-b]"comparisons and character range comparisons shall be locale sensitive.

2334(i). atomic's default constructor requires "uninitialized" state even for types with non-trivial default-constructor

Section: 30.7.1 [atomics.types.operations] Status: SG1 Submitter: Daniel Krügler Opened: 2013-10-03 Last modified: 2017-07-16

Priority: Not Prioritized

View all other issues in [atomics.types.operations].

View all issues with SG1 status.

Discussion:

According to 99 [atomics.types.operations.req] p4,

A ::A () noexcept = default;

Effects: leaves the atomic object in an uninitialized state. [Note: These semantics ensure compatibility with C. — end note]

This implementation requirement is OK for POD types, like int, but 30.7 [atomics.types.generic] p1 intentionally allows template arguments of atomic with a non-trivial default constructor ("The type of the template argument T shall be trivially copyable (3.9)"), so this wording can be read in a way that makes the behaviour of the following code undefined:

#include <atomic>
#include <iostream>

struct S {
  S() noexcept : v(42) {}
  int v;
};

int main() {
  std::atomic<S> as; // Default-initialization
  std::cout << as.load().v << std::endl; // ?
}

For a user-defined emulation of atomic the expected outcome would be defined and the program would output "42", but existing implementations differ and the result value is a "random number" for at least one implementation. This seems very surprising to me.

To realize that seemingly existing requirement, an implementation is either required to violate normal language rules internally or to perform specific bit-randomization-techniques after the normal default-initialization that called the default constructor of S.

According to my understanding, the non-normative note in 99 [atomics.types.operations.req] p4 is intended to refer to types that are valid C-types, but the example type S is not such a type.

To make the mental model of atomic's default constructor more intuitive for user-code, I suggest to clarify the wording to have the effects of default-initialization instead. The current state seems more like an unintended effect of imprecise language used here and has some similarities to wording that was incorrectly used to specify atomic_flag initialization as described by LWG 2159.

[2014-05-17, Daniel comments and provides alternative wording]

The current wording was considered controversial as expressed by reflector discussions. To me, the actual problem is not newly introduced by that wording, but instead is already present in basically all paragraphs specifying semantics of atomic types, since the wording never clearly distinguishes the value of the actual atomic type A and the value of the "underlying", corresponding non-atomic type C. The revised proposed wording attempts to improve the current ambiguity of these two kinds of values.

Previous resolution from Daniel [SUPERSEDED]:

This wording is relative to N3691.

  1. Modify 99 [atomics.types.operations.req] p4 as indicated: [Editorial note: There is no exposition-only member in atomic, which makes it a bit hard to specify what actually is initialized, but the usage of the term "value" seems consistent with similar wording used to specify the effects of the atomic load functions]

    A ::A () noexcept = default;
    

    -4- Effects: leaves the atomic object in an uninitialized stateThe value of the atomic object is default-initialized (9.3 [dcl.init]). [Note: These semantics ensure compatibility with C. — end note]

[2015-02 Cologne]

Handed over to SG1.

[2017-07 Toronto]

SG1 reviewed the PR below:

Previous resolution [SUPERSEDED]:

This wording is relative to N3936.

  1. Modify 99 [atomics.types.operations.req] p2 as indicated: [Editorial note: This is a near-to editorial change not directly affecting this issue, but atomic_address does no longer exist and the pointed to definition is relevant in the context of this issue resolution.]

    -2- In the following operation definitions:

    • an A refers to one of the atomic types.

    • a C refers to its corresponding non-atomic type. The atomic_address atomic type corresponds to the void* non-atomic type.

    • […]

  2. Modify 99 [atomics.types.operations.req] p4 and the following as indicated: [Editorial note: There is no exposition-only member in atomic, which makes it a bit hard to specify what actually is initialized, but the introductory wording of 99 [atomics.types.operations.req] p2 b2 defines: "a C refers to its corresponding non-atomic type." which helps to specify the semantics in terms of "the C value referred to by the atomic object"]

    A::A() noexcept = default;
    

    -4- Effects: leaves the atomic object in an uninitialized stateDefault-initializes (9.3 [dcl.init]) the C value referred to by the atomic object. [Note: These semantics ensure compatibility with C. — end note]

    constexpr A::A(C desired) noexcept;
    

    -5- Effects: Direct-iInitializes the C value referred to by the atomic object with the value desired. Initialization is not an atomic operation (1.10). […]

    […]

    void atomic_init(volatile A* object, C desired) noexcept;
    void atomic_init(A* object, C desired) noexcept;
    

    -8- Effects: Non-atomically initializes the C value referred to by *object with value desired. […]

    void atomic_store(volatile A* object, C desired) noexcept;
    […]
    void A::store(C desired, memory_order order = memory_order_seq_cst) noexcept;
    

    -9- […]

    -10- Effects: Atomically replaces the C value pointed to by object or by this with the value of desired. […]

    […]

    C atomic_load(const volatile A* object) noexcept;
    […]
    C A::load(memory_order order = memory_order_seq_cst) const noexcept;
    

    -13- […]

    -14- […]

    -15- Returns: Atomically returns the C value pointed to by object or by this.

    […]

    C atomic_exchange(volatile A* object, C desired) noexcept;
    […]
    C A::exchange(C desired, memory_order order = memory_order_seq_cst) noexcept;
    

    -18- Effects: Atomically replaces the C value pointed to by object or by this with desired. […]

    -19- Returns: Atomically returns the C value pointed to by object or by this immediately before the effects.

    […]

    C atomic_fetch_key(volatile A* object, M operand) noexcept;
    […]
    C A::fetch_key(M operand, memory_order order = memory_order_seq_cst) noexcept;
    

    -28- Effects: Atomically replaces the C value pointed to by object or by this with the result of the computation applied to the C value pointed to by object or by this and the given operand. […]

    -29- Returns: Atomically, returns the C value pointed to by object or by this immediately before the effects.

    […]

  3. Modify 30.9 [atomics.flag] p5 and the following as indicated:

    bool atomic_flag_test_and_set(volatile atomic_flag* object) noexcept;
    […]
    bool atomic_flag::test_and_set(memory_order order = memory_order_seq_cst) noexcept;
    

    -5- Effects: Atomically sets the bool value pointed to by object or by this to true. […]

    -6- Returns: Atomically, returns the bool value of thepointed to by object or by this immediately before the effects.

    void atomic_flag_clear(volatile atomic_flag* object) noexcept;
    […]
    void atomic_flag::clear(memory_order order = memory_order_seq_cst) noexcept;
    

    -7- […]

    -8- Effects: Atomically sets the bool value pointed to by object or by this to false. […]

SG1 also reviewed another PR from Lawrence Crowl. Lawrence's feedback was that turning atomic<T> into a container of T was a mistake, even if we allow the implementation of atomic to contain a T. SG1 agreed with Lawrence, but his PR (http://wiki.edg.com/bin/view/Wg21toronto2017/DefaultInitNonContainer) had massive merge conflicts caused by the adoption of P0558. Billy O'Neal supplied a new PR, which SG1 agreed to and which LWG looked at informally. This change also makes it clearer that initialization of an atomic is not an atomic operation in all forms, changes the C compatibility example to actually be compatible with C, and removes "initialization-compatible" which is not defined anywhere.

SG1 considered moving ATOMIC_VAR_INIT into Annex D, as their understanding at this time is that WG14 is considering removal of that macro. However, consensus was that moving things between clauses would require a paper, and that we should wait to remove that until WG14 actually does so.

Proposed resolution:

This wording is relative to N4659.

Modify 30.7.1 [atomics.types.operations] as indicated:

-?- Initialization of an atomic object is not an atomic operation (6.8.2 [intro.multithread]). [Note: It is possible to have an access to an atomic object A race with its construction, for example by communicating the address of the just-constructed object A via a memory_order_relaxed operations on a suitable atomic pointer variable, and then immediately accessing A in the recieving thread. This results in undefined behavior. — end note]

-1- [Note: Many operations are volatile-qualified. The "volatile as device register" semantics have not changed in the standard. This qualification means that volatility is preserved when applying these operations to volatile objects. It does not mean that operations on non-volatile objects become volatile. — end note]

atomic() noexcept = default;

-2- Effects: Leaves the atomic object in an uninitialized state. [Note: These semantics ensure compatibility with C. — end note]Initializes the atomic object with a default-initialized (9.3 [dcl.init]) value of type T. [Note: The default-initialized value may not be pointer-interconvertible with the atomic object. — end note]

constexpr atomic(T desired) noexcept;

-3- Effects: Initializes the atomic object with the value desired. Initialization is not an atomic operation (6.8.2 [intro.multithread]). [Note: It is possible to have an access to an atomic object A race with its construction, for example by communicating the address of the just-constructed object A to another thread via memory_order_relaxed operations on a suitable atomic pointer variable, and then immediately accessing A in the receiving thread. This results in undefined behavior — end note]

#define ATOMIC_VAR_INIT(value) see below{value}

-4- The macro expands to a token sequence suitable for constant initialization of an atomic variable of static storage duration of a type that is initialization-compatible with value. [Note: This operation may need to initialize locks. — end note] Concurrent access to the variable being initialized, even via an atomic operation, constitutes a data race. [Note: This macro ensures compatibility with C. — end note]
[Example:
atomic<int>atomic_int v = ATOMIC_VAR_INIT(5);
end example]


2335(i). array<array<int, 3>, 4> should be layout-compatible with int[4][3]

Section: 21.3.7 [array] Status: New Submitter: Jeffrey Yasskin Opened: 2013-10-04 Last modified: 2017-02-03

Priority: 3

View all other issues in [array].

View all issues with New status.

Discussion:

In order to replace some uses of C arrays with std::array, we need it to be possible to cast from a std::array<> to an equivalent C array. Core wording doesn't appear to be in quite the right state to allow casting, but if we specify that appropriate types are layout-compatible, we can at least write:

union {
  array<array<array<int, 2>, 3>, 4> arr;
  int carr[4][3][2];
};

to view memory as the other type: C++14 CD [class.mem]p18.

I believe it's sufficient to add "array<T, N> shall be layout-compatible (6.7 [basic.types]) with T[N]." to 21.3.7.1 [array.overview], but we might also need some extension to 10.3 [class.mem] to address the possibility of layout-compatibility between struct and array types.

I checked that libc++ on MacOS already implements this, although it would be good for someone else to double-check; I haven't checked any other standard libraries.

Proposed resolution:


2338(i). §[re.traits]/7 expects of locale facets something not guaranteed by [locale.facet]/4

Section: 29.7 [re.traits], 27.3.1.1.2 [locale.facet] Status: Open Submitter: Sergey Zubkov Opened: 2013-10-15 Last modified: 2017-02-03

Priority: 3

View all other issues in [re.traits].

View all issues with Open status.

Discussion:

29.7 [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, 29.7 [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 27.3.1.1.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.

Proposed resolution:

This wording is relative to N3691.

  1. Modify 27.3.1.1.2 [locale.facet]/4 as indicated:

    For some standard facets a standard "..._byname" class, derived from it, implements the virtual function semantics equivalent toprovided by that facet of the locale constructed by locale(const char*) with the same name. Each such facet provides a constructor that takes a const char* argument, which names the locale, and a refs argument, which is passed to the base class constructor. Each such facet also provides a constructor that takes a string argument str and a refs argument, which has the same effect as calling the first constructor with the two arguments str.c_str() and refs. If there is no "..._byname" version of a facet, the base class implements named locale semantics itself by reference to other facets. For any locale loc constructed by locale(const char*) and facet Facet that has a corresponding standard Facet_byname class, typeid(use_facet<Facet>(loc)) == typeid(Facet_byname).

  2. Modify 29.7 [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 by collate_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.


2342(i). User conversion to wchar_t const* or to wchar_t not invoked for operator<<

Section: 28.7.5.1 [ostream] Status: New Submitter: Alf P. Steinbach Opened: 2013-10-29 Last modified: 2017-02-03

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.

  1. Modify 28.7.5.1 [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*);
    […]
    }
    
    
  2. Modify 28.7.5.2.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 (28.7.5.2.1 [ostream.formatted.reqmts]) of out. Constructs a character sequence seq. If c has type char and the character type of the stream is not char, then seq consists of out.widen(c); otherwise seq consists of c. Determines padding for seq as described in 28.7.5.2.1 [ostream.formatted.reqmts]. Inserts seq into out. 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: s shall not be a null pointer.

    -4- Effects: Behaves like a formatted inserter (as described in 28.7.5.2.1 [ostream.formatted.reqmts]) of out. Creates a character sequence seq of n characters starting at s, each widened using out.widen() (27.5.5.3), where n is 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 type const charT*,

      • and also for the overload where the first argument is of type basic_ostream<char, traits>& and the second is of type const char*,

      • where the first argument is of type basic_ostream<wchar_t, traits>& and the second is of type const wchar_t*,

    • std::char_traits<char>::length(s) for the overload where the first argument is of type basic_ostream<charT, traits>& and the second is of type const char*,

    • traits::length(reinterpret_cast<const char*>(s)) for the other two overloads.

    Determines padding for seq as described in 28.7.5.2.1 [ostream.formatted.reqmts]. Inserts seq into out. Calls out.width(0).

    -5- Returns: out.


2348(i). charT('1') is not the wide equivalent of '1'

Section: 19.9.2 [template.bitset], 28.7.8 [quoted.manip] Status: Open Submitter: Zhihao Yuan Opened: 2013-12-02 Last modified: 2017-02-03

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 other `charT`s like `unsigned char` just don't compile without supplying those arguments? I hope so). — end drafting note]
  1. Modify 19.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);
         […]
      };
      […]
    }
    
  2. Modify 19.9.2.1 [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 zero and one compare equal to the character literals 0 and 1 of type charT, respectively.

    -3- Requires:: pos <= str.size().

    […]


2349(i). Clarify input/output function rethrow behavior

Section: 28.7.4.2.1 [istream.formatted.reqmts] Status: Open Submitter: Zhihao Yuan Opened: 2013-12-06 Last modified: 2017-02-03

Priority: 3

View all other issues in [istream.formatted.reqmts].

View all issues with Open status.

Discussion:

The formatted input function requirement says in 28.7.4.2.1 [istream.formatted.reqmts]:

"If an exception is thrown during input then ios::badbit is turned on in *this's error state. If (exceptions()&badbit) != 0 then the exception is rethrown."

while some formatted function may throw an exception from basic_ios::clear, for example in 19.9.4 [bitset.operators] p6:

"If no characters are stored in str, calls is.setstate(ios_base::failbit) (which may throw ios_base::failure)"

So should this exception be considered as "an exception [...] thrown during input"? And here is an implementation divergence (or you can read the following as "a bug libc++ only has" :)

cin.exceptions(ios_base::failbit);
bitset<N> b;
try {
  cin >> b;  // type 'a' and return
} catch (...)
{}

Now cin.rdstate() is just failbit in libstdc++ (and Dinkumware, by PJ), but failbit & badbit libc++. Similar difference found in other places, like eofbit & badbid after std::getline.

PJ and Matt both agree that the intention (of badbit + rethrow) is "to signify an exception arising in user code, not the iostreams package".

In addition, I found the following words in unformatted input function's requirements (28.7.4.3 [istream.unformatted]):

If an exception is thrown during input then ios::badbit is turned on in *this's error state. (Exceptions thrown from basic_ios<>::clear() are not caught or rethrown.) If (exceptions()&badbit) != 0 then the exception is rethrown.

The content within the parenthesis is added by LWG defect 61, and does fix the ambiguity. However, it only fixed the 1 of 4 requirements, and it lost some context (the word "rethrown" is not seen before this sentence within this section).

[Lenexa 2015-05-07: Marshall to research and report]

Proposed resolution:

This wording is relative to N3797.

[Drafting note: The editor is kindly asked to introduce additional spaces at the following marked occurrences of operator&end drafting note]
  1. Modify 28.7.4.2.1 [istream.formatted.reqmts] p1 as indicated:

    -1- Each formatted input function begins execution by constructing an object of class sentry with the noskipws (second) argument false. If the sentry object returns true, when converted to a value of type bool, the function endeavors to obtain the requested input. If an exception, other than the ones thrown from clear(), if any, is thrown during input then ios::badbit is turned on[Footnote 314] in *this's error state. If (exceptions() & badbit) != 0 then the exception is rethrown. In any case, the formatted input function destroys the sentry object. If no exception has been thrown, it returns *this.

  2. Modify 28.7.5.2.1 [ostream.formatted.reqmts] p1 as indicated:

    -1- Each formatted output function begins execution by constructing an object of class sentry. If this object returns true when converted to a value of type bool, the function endeavors to generate the requested output. If the generation fails, then the formatted output function does setstate(ios_base::failbit), which might throw an exception. If an exception, other than the ones thrown from clear(), if any, is thrown during output, then ios::badbit is turned on[Footnote 327] in *this's error state. If (exceptions() & badbit) != 0 then the exception is rethrown. Whether or not an exception is thrown, the sentry object is destroyed before leaving the formatted output function. If no exception is thrown, the result of the formatted output function is *this.

  3. Modify 28.7.5.3 [ostream.unformatted] p1 as indicated:

    -1- Each unformatted output function begins execution by constructing an object of class sentry. If this object returns true, while converting to a value of type bool, the function endeavors to generate the requested output. If an exception, other than the ones thrown from clear(), if any, is thrown during output, then ios::badbit is turned on[Footnote 330] in *this's error state. If (exceptions() & badbit) != 0 then the exception is rethrown. In any case, the unformatted output function ends by destroying the sentry object, then, if no exception was thrown, returning the value specified for the unformatted output function.

  4. Modify 28.7.4.3 [istream.unformatted] p1 as indicated:

    -1- Each unformatted input function begins execution by constructing an object of class sentry with the default argument noskipws (second) argument true. If the sentry object returns true, when converted to a value of type bool, the function endeavors to obtain the requested input. Otherwise, if the sentry constructor exits by throwing an exception or if the sentry object returns false, when converted to a value of type bool, the function returns without attempting to obtain any input. In either case the number of extracted characters is set to 0; unformatted input functions taking a character array of non-zero size as an argument shall also store a null character (using charT()) in the first location of the array. If an exception, other than the ones thrown from clear(), if any, is thrown during input then ios::badbit is turned on[Footnote 317] in *this's error state. (Exceptions thrown from basic_ios<>::clear() are not caught or rethrown.) If (exceptions() & badbit) != 0 then the exception is rethrown. It also counts the number of characters extracted. If no exception has been thrown it ends by storing the count in a member object and returning the value specified. In any event the sentry object is destroyed before leaving the unformatted input function.


2352(i). Is a default-constructed std::seed_seq intended to produce a predictable .generate()?

Section: 25.6.7.1 [rand.util.seedseq] Status: New Submitter: Thomas Plum Opened: 2013-12-02 Last modified: 2018-01-22

Priority: 2

View all other issues in [rand.util.seedseq].

View all issues with New status.

Discussion:

With respect to class seed_seq 25.6.7.1 [rand.util.seedseq], is a default-constructed std::seed_seq intended to produce a predictable .generate() sequence?

Implementations differ.

[2014-02-10]

Priority set to 2

Proposed resolution:


2358(i). Apparently-bogus definition of is_empty type trait

Section: 19.15.4.3 [meta.unary.prop] Status: Open Submitter: Richard Smith Opened: 2014-02-01 Last modified: 2017-02-03

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:

"T is 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 class B for which is_empty<B>::value is 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 10.3.9 [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:

"T is a class type, but not a union type, with no non-static data members other than, no unnamed bit-fields of non-zero length 0, no virtual member functions, no virtual base classes, and no base class B for which is_empty<B>::value is false."

and if the above type should be treated as empty, then this might be appropriate:

"T is a class type, but not a union type, with no (named) non-static data members other than bit-fields of length 0, no virtual member functions, no virtual base classes, and no base class B for which is_empty<B>::value is 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_empty as follows:

T is a non-union class type with no non-static data members other than, no unnamed bit-fields of non-zero length 0, no virtual member functions, no virtual base classes, and no base class B for which is_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:

T is a class type, but not a union type,is a non-union class type with no non-static data members other than, no unnamed bit-fields of non-zero length 0, no virtual member functions, no virtual base classes, and no base class B for which is_empty_v<B> is false.


2362(i). unique, associative emplace() should not move/copy the mapped_type constructor arguments when no insertion happens

Section: 21.2.6 [associative.reqmts], 21.2.7 [unord.req] Status: New Submitter: Jeffrey Yasskin Opened: 2014-02-15 Last modified: 2017-02-03

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 t constructed with
std::forward<Args>(args)... if and only if there is no element in the
container with key equivalent to the key of t. The bool component 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 of t.

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 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:


2363(i). Defect in 30.4.1.4.1 [thread.sharedtimedmutex.class]

Section: 31.4.3.5.1 [thread.sharedtimedmutex.class] Status: Open Submitter: Richard Smith Opened: 2014-02-16 Last modified: 2018-08-27

Priority: 2

View all issues with Open status.

Discussion:

31.4.3.5.1 [thread.sharedtimedmutex.class] paragraph 2:

The class shared_timed_mutex shall satisfy all of the SharedTimedMutex requirements (30.4.1.4). It shall be a standard layout class (Clause 9).

There's no SharedTimedMutex requirements; this name doesn't appear anywhere else in the standard. (Prior to N3891, this was SharedMutex, which was equally undefined.)

I assume this concept should be defined somewhere?

Also, n3891 changes 31.4.3.5 [thread.sharedtimedmutex.requirements] from defining "shared mutex type" to defining "shared timed mutex type", but its paragraph 2 still talks about "shared mutex type". Is that OK? I think you could argue that it's clear enough what it means, but presumably it should use the term that paragraph 1 defined.

31.4.4.4 [thread.lock.shared] paragraph 1 talks about the "shared mutex requirements", which again is a term that isn't defined, and presumably means "the requirements on a shared timed mutex type" or similar (maybe if SharedMutex or SharedTimedMutex were defined it could be reused here).

[2014-05-22, Daniel comments]

As for SharedTimedMutex, there exists a similar problem in regard to TimedMutex referred to in 31.4.3.3.1 [thread.timedmutex.class] p2 and in 31.4.3.3.2 [thread.timedmutex.recursive] p2, but nowhere defined.

Another problem is, that according to 31.4.3.2.1 [thread.mutex.class] p3, "The class mutex shall satisfy all the Mutex requirements (31.4.3 [thread.mutex.requirements]).", but there are no concrete Mutex requirements, 31.4.3 [thread.mutex.requirements] — titled as "Mutex requirements" — describes mutex types, timed mutex types, and shared timed mutex types.

[2014-06-08, Daniel comments and provides wording]

The presented wording adds to the existing mutex types, timed mutex types, and shared timed mutex types terms a new set of corresponding MutexType, TimedMutexType, and SharedTimedMutexType requirements.

The reason for the change of requirement names is two-fold: First, the new name better matches the intention to have a concrete name for the requirements imposed on the corresponding mutex types (This kind of requirement deviate from the more general Lockable requirements, which are not restricted to a explicitly enumerated set of library types). Second, using **MutexType over **Mutex provides the additional advantage that it reduces the chances of confusing named requirements from template parameters named Mutex (such as for unique_lock or shared_lock).

Nonetheless the here presented wording has one unfortunate side-effect: Once applied it would have the effect that types used to instantiate std::shared_lock cannot be user-defined shared mutex types due to 31.4.4.4 [thread.lock.shared]. The reason is based on the currently lack of an existing SharedLockable requirement set, which would complete the existing BasicLockable and Lockable requirements (which are "real" requirements). This restriction is not actually a problem introduced by the provided resolution but instead one that existed before but becomes more obvious now.

[2015-02 Cologne]

Handed over to SG1.

[2015-05 Lenexa, SG1 response]

Thanks to Daniel, and please put it in SG1-OK status. Perhaps open another issue for the remaining problem Daniel points out?

[2015-10 pre-Kona]

SG1 hands this over to LWG for wording review

[2015-10-21 Kona, Daniel comments and adjusts wording to to untimed shared mutex types]

The new wording reflects the addition of the new shared mutex types. The approach used for shared_lock is similar to the one used for unique_lock: The template argument Mutex has a reduced requirement set that is not sufficient for all operations. Only those members that require stronger requirements of SharedTimedMutexType specify that additionally in the Requires element of the corresponding prototype specifications.

The proposed wording could be more general if we would introduce more fundamental requirements set for SharedLockable and SharedTimedLockable types which could be satisfied by user-provided types as well, because the SharedMutexType and SharedTimedMutexType requirements are essentially restricted to an enumerated set of types provided by the Standard Library. But this extension seemed too large for this issue and can be easily fixed later without any harm.

Previous resolution [SUPERSEDED]:

This wording is relative to N3936.

  1. Change 31.4.3.2 [thread.mutex.requirements.mutex] as indicated:

    -1- The mutex types are the standard library types std::mutex, std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex, and std::shared_timed_mutex. They shall meet the MutexType requirements set out in this section. In this description, m denotes an object of a mutex type.

  2. Change 31.4.3.2.1 [thread.mutex.class] as indicated:

    -3- The class mutex shall satisfy all the MutexType requirements (31.4.3.2 [thread.mutex.requirements.mutex]31.4.3 [thread.mutex.requirements]). It shall be a standard-layout class (Clause 9).

  3. Change 31.4.3.2.2 [thread.mutex.recursive] as indicated:

    -2- The class recursive_mutex shall satisfy all the MutexMutexType requirements (31.4.3.2 [thread.mutex.requirements.mutex]31.4.3 [thread.mutex.requirements]). It shall be a standard-layout class (Clause 9).

  4. Change 31.4.3.3 [thread.timedmutex.requirements] as indicated:

    -1- The timed mutex types are the standard library types std::timed_mutex, std::recursive_timed_mutex, and std::shared_timed_mutex. They shall meet the TimedMutexType requirements set out below. In this description, m denotes an object of a mutex type, rel_time denotes an object of an instantiation of duration (20.12.5), and abs_time denotes an object of an instantiation of time_point (20.12.6).

  5. Change 31.4.3.3.1 [thread.timedmutex.class] as indicated:

    -2- The class timed_mutex shall satisfy all of the TimedMutexType requirements (31.4.3.3 [thread.timedmutex.requirements]). It shall be a standard-layout class (Clause 9).

  6. Change 31.4.3.3.2 [thread.timedmutex.recursive] as indicated:

    -2- The class recursive_timed_mutex shall satisfy all of the TimedMutexType requirements (31.4.3.3 [thread.timedmutex.requirements]). It shall be a standard-layout class (Clause 9).

  7. Change 31.4.3.5 [thread.sharedtimedmutex.requirements] as indicated: [Drafting note: The reference to the timed mutex types requirements has been moved after introducing the new requirement set to ensure that SharedTimedMutexType refine TimedMutexType.]

    -1- The standard library type std::shared_timed_mutex is a shared timed mutex type. Shared timed mutex types shall meet the SharedTimedMutexType requirements of timed mutex types (31.4.3.3 [thread.timedmutex.requirements]), and additionally shall meet the requirements set out below. In this description, m denotes an object of a mutex type, rel_type denotes an object of an instantiation of duration (20.12.5), and abs_time denotes an object of an instantiation of time_point (20.12.6).

    -?- The shared timed mutex types shall meet the TimedMutexType requirements (31.4.3.3 [thread.timedmutex.requirements]).

  8. Change 31.4.3.5.1 [thread.sharedtimedmutex.class] as indicated:

    -2- The class shared_timed_mutex shall satisfy all of the SharedTimedMutexType requirements (31.4.3.5 [thread.sharedtimedmutex.requirements]). It shall be a standard-layout class (Clause 9).

  9. Change 31.4.4.4 [thread.lock.shared] as indicated: [Drafting note: Once N3995 has been applied, the following reference should be changed to the new SharedMutexType requirements ([thread.sharedmutex.requirements]) or even better to some new SharedLockable requirements (to be defined) — end drafting note]

    -1- […] The supplied Mutex type shall meet the shared mutexSharedTimedMutexType requirements (31.4.3.5 [thread.sharedtimedmutex.requirements]).

    -2- [Note: shared_lock<Mutex> meets the TimedLockable requirements (30.2.5.4). — end note]

[2016-02 Jacksonville]

Marshall to review wording.

[2018-08-23 Batavia Issues processing]

Tim to redraft.

Proposed resolution:

This wording is relative to N4527.

  1. Change 31.4.3.2 [thread.mutex.requirements.mutex] as indicated:

    -1- The mutex types are the standard library types std::mutex, std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex, std::shared_mutex, and std::shared_timed_mutex. They shall meet the MutexType requirements set out in this section. In this description, m denotes an object of a mutex type.

    -2- The mutex types shall meet the Lockable requirements (31.2.5.3 [thread.req.lockable.req]).

  2. Change 31.4.3.2.1 [thread.mutex.class] as indicated:

    -3- The class mutex shall satisfy all the MutexType requirements (31.4.3.2 [thread.mutex.requirements.mutex]31.4.3 [thread.mutex.requirements]). It shall be a standard-layout class (Clause 9).

  3. Change 31.4.3.2.2 [thread.mutex.recursive] as indicated:

    -2- The class recursive_mutex shall satisfy all the MutexMutexType requirements (31.4.3.2 [thread.mutex.requirements.mutex]31.4.3 [thread.mutex.requirements]). It shall be a standard-layout class (Clause 9).

  4. Change 31.4.3.3 [thread.timedmutex.requirements] as indicated:

    -1- The timed mutex types are the standard library types std::timed_mutex, std::recursive_timed_mutex, and std::shared_timed_mutex. They shall meet the TimedMutexType requirements set out below. In this description, m denotes an object of a mutex type, rel_time denotes an object of an instantiation of duration (20.12.5), and abs_time denotes an object of an instantiation of time_point (20.12.6).

    -2- The timed mutex types shall meet the TimedLockable requirements (31.2.5.4 [thread.req.lockable.timed]).

  5. Change 31.4.3.3.1 [thread.timedmutex.class] as indicated:

    -2- The class timed_mutex shall satisfy all of the TimedMutexType requirements (31.4.3.3 [thread.timedmutex.requirements]). It shall be a standard-layout class (Clause 9).

  6. Change 31.4.3.3.2 [thread.timedmutex.recursive] as indicated:

    -2- The class recursive_timed_mutex shall satisfy all of the TimedMutexType requirements (31.4.3.3 [thread.timedmutex.requirements]). It shall be a standard-layout class (Clause 9).

  7. Change 31.4.3.4 [thread.sharedmutex.requirements] as indicated: [Drafting note: The reference to the mutex types requirements has been moved after introducing the new requirement set to ensure that SharedMutexType refines MutexType.]

    -1- The standard library types std::shared_mutex and std::shared_timed_mutex are shared mutex types. Shared mutex types shall meet the SharedMutexType requirements of mutex types (31.4.3.2 [thread.mutex.requirements.mutex]), and additionally shall meet the requirements set out below. In this description, m denotes an object of a shared mutex type.

    -?- The shared mutex types shall meet the MutexType requirements (31.4.3.2 [thread.mutex.requirements.mutex]).

  8. Change 31.4.3.4.1 [thread.sharedmutex.class] as indicated:

    -2- The class shared_mutex shall satisfy all of the SharedMutexType requirements for shared mutexes (31.4.3.4 [thread.sharedmutex.requirements]). It shall be a standard-layout class (Clause 9).

  9. Change 31.4.3.5 [thread.sharedtimedmutex.requirements] as indicated: [Drafting note: The reference to the timed mutex types requirements has been moved after introducing the new requirement set to ensure that SharedTimedMutexType refines TimedMutexType and SharedMutexType.]

    -1- The standard library type std::shared_timed_mutex is a shared timed mutex type. Shared timed mutex types shall meet the SharedTimedMutexType requirements of timed mutex types (31.4.3.3 [thread.timedmutex.requirements]), shared mutex types (31.4.3.4 [thread.sharedmutex.requirements]), and additionally shall meet the requirements set out below. In this description, m denotes an object of a shared timed mutex type, rel_type denotes an object of an instantiation of duration (20.12.5), and abs_time denotes an object of an instantiation of time_point (20.12.6).

    -?- The shared timed mutex types shall meet the TimedMutexType requirements (31.4.3.3 [thread.timedmutex.requirements]) and the SharedMutexType requirements (31.4.3.4 [thread.sharedmutex.requirements]).

  10. Change 31.4.3.5.1 [thread.sharedtimedmutex.class] as indicated:

    -2- The class shared_timed_mutex shall satisfy all of the SharedTimedMutexType requirements for shared timed mutexes (31.4.3.5 [thread.sharedtimedmutex.requirements]). It shall be a standard-layout class (Clause 9).

  11. Change 31.4.4.4 [thread.lock.shared] as indicated:

    -1- […] The supplied Mutex type shall meet the shared mutexSharedMutexType requirements (31.4.3.5 [thread.sharedtimedmutex.requirements]31.4.3.4 [thread.sharedmutex.requirements]).

    -2- [Note: shared_lock<Mutex> meets the TimedLockable requirements (30.2.5.4). — end note]

  12. Change 31.4.4.4.1 [thread.lock.shared.cons] as indicated:

    template <class Clock, class Duration>
      shared_lock(mutex_type& m,
                  const chrono::time_point<Clock, Duration>& abs_time);
    

    -14- Requires: The supplied Mutex type shall meet the SharedTimedMutexType requirements (31.4.3.5 [thread.sharedtimedmutex.requirements]). The calling thread does not own the mutex for any ownership mode.

    -15- Effects: Constructs an object of type shared_lock and calls m.try_lock_shared_until(abs_time).

    […]

    template <class Rep, class Period>
      shared_lock(mutex_type& m,
                  const chrono::duration<Rep, Period>& rel_time);
    

    -17- Requires: The supplied Mutex type shall meet the SharedTimedMutexType requirements (31.4.3.5 [thread.sharedtimedmutex.requirements]). The calling thread does not own the mutex for any ownership mode.

    -18- Effects: Constructs an object of type shared_lock and calls m.try_lock_shared_for(rel_time).

    […]

  13. Change 31.4.4.4.2 [thread.lock.shared.locking] as indicated:

    template <class Clock, class Duration>
      bool
      try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
    

    -?- Requires: The supplied Mutex type shall meet the SharedTimedMutexType requirements (31.4.3.5 [thread.sharedtimedmutex.requirements]).

    -8- Effects: pm->try_lock_shared_until(abs_time).

    […]

    template <class Rep, class Period>
      bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
    

    -?- Requires: The supplied Mutex type shall meet the SharedTimedMutexType requirements (31.4.3.5 [thread.sharedtimedmutex.requirements]).

    -12- Effects: pm->try_lock_shared_for(rel_time).

    […]


2366(i). istreambuf_iterator end-of-stream equality

Section: 22.6.3 [istreambuf.iterator] Status: New Submitter: Hyman Rosen Opened: 2014-02-19 Last modified: 2017-02-03

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).

22.6.3 [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(). 99 [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.

Proposed resolution:


2375(i). Is [iterator.requirements.general]/9 too broadly applied?

Section: 22.3.1 [iterator.requirements.general] Status: New Submitter: Marshall Clow Opened: 2014-03-25 Last modified: 2017-02-03

Priority: 3

View other active issues in [iterator.requirements.general].

View all other issues in [iterator.requirements.general].

View all issues with New status.

Discussion:

22.3.1 [iterator.requirements.general] p9 says:

Destruction of an iterator may invalidate pointers and references previously obtained from that iterator.

But the resolution of LWG issue 2360 specifically advocates returning *--temp; where temp is a local variable.

And 22.3.5.4 [forward.iterators] p6 says:

If a and b are both dereferenceable, then a == b if and only if *a and *b are bound to the same object.

which disallows "stashing" iterators (i.e, iterators that refer to data inside themselves).

So, I suspect that the restriction in p9 should only apply to input iterators, and can probably be moved into 22.3.5.2 [input.iterators] instead of 22.3.1 [iterator.requirements.general].

[2014-05-22, Daniel comments]

Given that forward iterators (and beyond) are refinements of input iterator, moving this constraint to input iterators won't help much because it would still hold for all refined forms.

Proposed resolution:


2381(i). Inconsistency in parsing floating point numbers

Section: 27.4.2.1.2 [facet.num.get.virtuals] Status: Open Submitter: Marshall Clow Opened: 2014-04-30 Last modified: 2018-08-27

Priority: 2

View all other issues in [facet.num.get.virtuals].

View all issues with Open status.

Discussion:

In 27.4.2.1.2 [facet.num.get.virtuals] we have:

Stage 3: The sequence of chars accumulated in stage 2 (the field) is converted to a numeric value by the rules of one of the functions declared in the header <cstdlib>:

This implies that for many cases, this routine should return true:

bool is_same(const char* p) 
{
  std::string str{p};
  double val1 = std::strtod(str.c_str(), nullptr);
  std::stringstream ss(str);
  double val2;
  ss >> val2;
  return std::isinf(val1) == std::isinf(val2) &&                 // either they're both infinity
         std::isnan(val1) == std::isnan(val2) &&                 // or they're both NaN
         (std::isinf(val1) || std::isnan(val1) || val1 == val2); // or they're equal
}

and this is indeed true, for many strings:

assert(is_same("0"));
assert(is_same("1.0"));
assert(is_same("-1.0"));
assert(is_same("100.123"));
assert(is_same("1234.456e89"));

but not for others

assert(is_same("0xABp-4")); // hex float
assert(is_same("inf"));
assert(is_same("+inf"));
assert(is_same("-inf"));
assert(is_same("nan"));
assert(is_same("+nan"));
assert(is_same("-nan"));

assert(is_same("infinity"));
assert(is_same("+infinity"));
assert(is_same("-infinity"));

These are all strings that are correctly parsed by std::strtod, but not by the stream extraction operators. They contain characters that are deemed invalid in stage 2 of parsing.

If we're going to say that we're converting by the rules of strtold, then we should accept all the things that strtold accepts.

[2016-04, Issues Telecon]

People are much more interested in round-tripping hex floats than handling inf and nan. Priority changed to P2.

Marshall says he'll try to write some wording, noting that this is a very closely specified part of the standard, and has remained unchanged for a long time. Also, there will need to be a sample implementation.

[2016-08, Chicago]

Zhihao provides wording

The src array in Stage 2 does narrowing only. The actual input validation is delegated to strtold (independent from the parsing in Stage 3 which is again being delegated to strtold) by saying:

[...] If it is not discarded, then a check is made to determine if c is allowed as the next character of an input field of the conversion specifier returned by Stage 1.

So a conforming C++11 num_get is supposed to magically accept an hexfloat without an exponent

0x3.AB

because we refers to C99, and the fix to this issue should be just expanding the src array.

Support for Infs and NaNs are not proposed because of the complexity of nan(n-chars).

[2016-08, Chicago]

Tues PM: Move to Open

[2016-09-08, Zhihao Yuan comments and updates proposed wording]

Examples added.

[2018-08-23 Batavia Issues processing]

Needs an Annex C entry. Tim to write Annex C.

Proposed resolution:

This wording is relative to N4606.

  1. Change 27.4.2.1.2 [facet.num.get.virtuals]/3 Stage 2 as indicated:

    static const char src[] = "0123456789abcdefpxABCDEFPX+-";

  2. Append the following examples to 27.4.2.1.2 [facet.num.get.virtuals]/3 Stage 2 as indicated:

    [Example:

    Given an input sequence of "0x1a.bp+07p",

    • if Stage 1 returns %d, "0" is accumulated;

    • if Stage 1 returns %i, "0x1a" are accumulated;

    • if Stage 1 returns %g, "0x1a.bp+07" are accumulated.

    In all cases, leaving the rest in the input.

    — end example]


2383(i). Overflow cannot be ill-formed for chrono::duration integer literals

Section: 26.5.8 [time.duration.literals] Status: Open Submitter: Jonathan Wakely Opened: 2014-05-16 Last modified: 2017-02-03

Priority: 3

View all issues with Open status.

Discussion:

26.5.8 [time.duration.literals] p3 says:

If any of these suffixes are applied to an integer literal and the resulting chrono::duration value 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]

Proposed resolution:


2392(i). "character type" is used but not defined

Section: 15.3.17 [defns.ntcts], 27.3.1.1.1 [locale.category], 28.2.2 [iostreams.limits.pos], 28.7.5.2.1 [ostream.formatted.reqmts], 28.7.5.2.4 [ostream.inserters.character] Status: New Submitter: Jeffrey Yasskin Opened: 2014-06-01 Last modified: 2017-05-31

Priority: 3

View all issues with New status.

Discussion:

The term "character type" is used in 15.3.17 [defns.ntcts], 27.3.1.1.1 [locale.category], 28.2.2 [iostreams.limits.pos], 28.7.5.2.1 [ostream.formatted.reqmts], and 28.7.5.2.4 [ostream.inserters.character], but the core language only defines "narrow character types" (6.7.1 [basic.fundamental]).

"wide-character type" is used in D.13 [depr.locale.stdcvt], but the core language only defines a "wide-character set" and "wide-character literal".

Proposed resolution:


2398(i). type_info's destructor shouldn't be required to be virtual

Section: 16.7.2 [type.info] Status: Open Submitter: Stephan T. Lavavej Opened: 2014-06-14 Last modified: 2017-02-03

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.

  1. Change 16.7.2 [type.info] as indicated:

    namespace std {
      class type_info {
      public:
        virtualsee below ~type_info();
        […]
      };
    }
    

    -1- The class type_info describes 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() is virtual is implementation-defined.


2413(i). assert macro is overconstrained

Section: 18.3 [assertions] Status: New Submitter: David Krauss Opened: 2014-06-25 Last modified: 2017-02-03

Priority: 4

View other active issues in [assertions].

View all other issues in [assertions].

View all issues with New status.

Discussion:

When NDEBUG is defined, assert must expand exactly to the token sequence ((void)0), with no whitespace (C99 §7.2/1 and also C11 §7.2/1). This is a lost opportunity to pass the condition along to the optimizer.

The user may observe the token sequence using the stringize operator or discriminate it by making a matching #define directive. There is little chance of practical code doing such things. It's reasonable to allow any expansion that is a void expression with no side effects or semantic requirements, for example, an extension keyword or an attribute-specifier finagled into the context.

Conforming optimizations would still be limited to treating the condition as hint, not a requirement. Nonconformance on this point is quite reasonable though, given user preferences. Anyway, it shouldn't depend on preprocessor quirks.

As for current practice, Darwin OS <assert.h> provides a GCC-style compiler hint __builtin_expect but only in debug mode. Shouldn't release mode preserve hints?

Daniel:

The corresponding resolution should take care not to conflict with the intention behind LWG 2234.

Proposed resolution:


2414(i). Member function reentrancy should be implementation-defined

Section: 15.5.5.8 [reentrancy] Status: Open Submitter: Stephan T. Lavavej Opened: 2014-07-01 Last modified: 2017-11-12

Priority: 3

View all other issues in [reentrancy].

View all issues with Open status.

Discussion:

N3936 15.5.5.8 [reentrancy]/1 talks about "functions", but that doesn't address the scenario of calling different member functions of a single object. Member functions often have to violate and then re-establish invariants. For example, vectors often have "holes" during insertion, and element constructors/destructors/etc. shouldn't be allowed to observe the vector while it's in this invariant-violating state. The [reentrancy] Standardese should be extended to cover member functions, so that implementers can either say that member function reentrancy is universally prohibited, or selectively allowed for very specific scenarios.

(For clarity, this issue has been split off from LWG 2382.)

[2014-11-03 Urbana]

AJM confirmed with SG1 that they had no special concerns with this issue, and LWG should retain ownership.

AM: this is too overly broad as it also covers calling the exact same member function on a different object
STL: so you insert into a map, and copying the value triggers another insertion into a different map of the same type
GR: reentrancy seems to imply the single-threaded case, but needs to consider the multi-threaded case

Needs more wording.

Move to Open

[2015-07 Telecon Urbana]

Marshall to ping STL for updated wording.

[2016-05 email from STL]

I don't have any better suggestions than my original PR at the moment.

Proposed resolution:

This wording is relative to N3936.

  1. Change 15.5.5.8 [reentrancy] p1 as indicated:

    -1- Except where explicitly specified in this standard, it is implementation-defined which functions (including different member functions called on a single object) in the Standard C++ library may be recursively reentered.


2417(i). [fund.ts.v2] std::experimental::optional::operator< and LessThanComparable requirement

Section: 5.7 [fund.ts.v2::optional.relops], 5.9 [fund.ts.v2::optional.comp_with_t] Status: Tentatively NAD Submitter: Daniel Krügler Opened: 2014-06-20 Last modified: 2018-06-24

Priority: Not Prioritized

View all issues with Tentatively NAD status.

Discussion:

Addresses: fund.ts.v2

Currently, std::experimental::optional::operator== imposes the EqualityComparable requirement which provides two guarantees: It ensures that operator!= can rely on the equivalence-relation property and more importantly, that the BooleanTestable requirements suggested by issue 2114 are automatically implied.

std::experimental::optional::operator< doesn't provide a LessThanComparable requirement, but there was quite an historic set of changes involved with that family of types: As of N3527 this operator was defined in terms of operator< of the contained type T and imposed the LessThanComparable requirement. In the final acceptance step of optional by the committee, the definition was expressed in terms of std::less and the LessThanComparable requirement had been removed.

The inconsistency between operator== and operator< should be removed. One possible course of action would be to add the LessThanComparable to std::experimental::optional::operator<. The EqualityComparable requirement of operator== could also be removed, but in that case both operators would at least need to require the BooleanTestable requirements (see 2114) for the result type of T's operator== and operator<.

Arguably, corresponding operators for pair and tuple do not impose LessThanComparable (nor EqualityComparable), albeit the definition of the "derived" relation functions depend on properties ensured by LessThanComparable. According to the SGI definition, the intention was to imposed both EqualityComparable and LessThanComparable. If this is not intended, the standard should clarify this position.

[2015-02 Cologne]

VV, DK, JY discuss why and when LessThanComparable was removed. AM: Move to LEWG. Please tell LWG when you look at it.

[2016-11-08, Issaquah]

Not adopted during NB comment resolution

[LEWG Kona 2017]

Recommend NAD: We've done a lot of work getting the C++17 semantics we want in this area; we're not going to change them 3 days from DIS or change the TSv2 behavior to be different from '17.

Proposed resolution:


2421(i). Non-specification of handling zero size in std::align [ptr.align]

Section: 19.10.6 [ptr.align] Status: New Submitter: Melissa Mears Opened: 2014-08-06 Last modified: 2017-02-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; 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:

  1. The behavior is undefined because the size is invalid.

  2. The behavior is implementation-defined. This seems to be the status quo, with current implementations using #3.

  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.

  4. 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:


2423(i). Missing specification slice_array, gslice_array, mask_array, indirect_array copy constructor

Section: 25.7.5 [template.slice.array], 25.7.7 [template.gslice.array], 25.7.8 [template.mask.array], 25.7.9 [template.indirect.array] Status: New Submitter: Akira Takahashi Opened: 2014-08-12 Last modified: 2017-02-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:

Proposed resolution:

  1. Before 25.7.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.

  2. Before 25.7.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.

  3. Before 25.7.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.

  4. Before 25.7.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.


2431(i). Missing regular expression traits requirements

Section: 29.3 [re.req] Status: New Submitter: Jonathan Wakely Opened: 2014-09-30 Last modified: 2017-02-03

Priority: 3

View all issues with New status.

Discussion:

The requirements on the traits class in 29.3 [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.

Proposed resolution:


2432(i). initializer_list assignability

Section: 16.10 [support.initlist] Status: EWG Submitter: David Krauss Opened: 2014-09-30 Last modified: 2017-02-03

Priority: 2

View other active issues in [support.initlist].

View all other issues in [support.initlist].

View all issues with EWG status.

Discussion:

std::initializer_list::operator= 16.10 [support.initlist] is horribly broken and it needs deprecation:

std::initializer_list<foo> a = {{1}, {2}, {3}};
a = {{4}, {5}, {6}};
// New sequence is already destroyed.

Assignability of initializer_list isn't explicitly specified, but most implementations supply a default assignment operator. I'm not sure what 15.4 [description] says, but it probably doesn't matter.

[Lenexa 2015-05-05: Send to EWG as discussed in Telecon]

Proposed resolution:

  1. Edit 16.10 [support.initlist] p1, class template initializer_list synopsis, as indicated:

    namespace std {
      template<class E> class initializer_list {
      public:
        […]
        constexpr initializer_list() noexcept;
      
        initializer_list(const initializer_list&) = default;
        initializer_list(initializer_list&&) = default;
        initializer_list& operator=(const initializer_list&) = delete;
        initializer_list& operator=(initializer_list&&) = delete;
        
        constexpr size_t size() const noexcept;
        […]
      };
      […]
    }
    

2452(i). is_constructible, etc. and default arguments

Section: 19.15 [meta] Status: Core Submitter: Hubert Tong Opened: 2014-11-04 Last modified: 2017-02-03

Priority: 3

View other active issues in [meta].

View all other issues in [meta].

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:


2453(i). §[iterator.range] and now [iterator.container] aren't available via <initializer_list>

Section: 16.10 [support.initlist], 22.7 [iterator.range], 22.8 [iterator.container] Status: New Submitter: Richard Smith Opened: 2014-11-11 Last modified: 2017-02-03

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:


2457(i). std::begin() and std::end() do not support multi-dimensional arrays correctly

Section: 22.7 [iterator.range] Status: New Submitter: Janez Žemva Opened: 2014-11-16 Last modified: 2017-02-03

Priority: 3

View all other issues in [iterator.range].

View all issues with New status.

Discussion:

The following code:

#include <algorithm>
#include <iterator>
#include <iostream>
#include <cassert>

int main() 
{
  int a[2][3][4] = { { { 1,  2,  3,  4}, { 5,  6,  7,  8}, { 9, 10, 11, 12} },
                     { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } };
  int b[2][3][4];

  assert(std::distance(std::begin(a), std::end(a)) == 2 * 3 * 4);
  std::copy(std::begin(a), std::end(a), std::begin(b));
  std::copy(std::begin(b), std::end(b), std::ostream_iterator<int>(std::cout, ","));
}

does not compile.

A possible way to remedy this would be to add the following overloads of begin, end, rbegin, and rend to 22.7 [iterator.range], relying on recursive evaluation:

namespace std {

  template <typename T, size_t M, size_t N>
  constexpr remove_all_extents_t<T>*
  begin(T (&array)[M][N])
  {
    return begin(*array);
  }
  
  template <typename T, size_t M, size_t N>
  constexpr remove_all_extents_t<T>*
  end(T (&array)[M][N])
  {
    return end(array[M - 1]);
  }

  template <typename T, size_t M, size_t N>
  reverse_iterator<remove_all_extents_t<T>*>
  rbegin(T (&array)[M][N])
  {
    return decltype(rbegin(array))(end(array[M - 1]));
  }
  
  template <typename T, size_t M, size_t N>
  reverse_iterator<remove_all_extents_t<T>*>
  rend(T (&array)[M][N])
  {
    return decltype(rend(array))(begin(*array));
  }

}

Proposed resolution:


2461(i). Interaction between allocators and container exception safety guarantees

Section: 15.5.3.5 [allocator.requirements], 21.3.11.3 [vector.capacity], 21.3.11.5 [vector.modifiers] Status: New Submitter: dyp Opened: 2014-12-06 Last modified: 2017-02-03

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 15.5.3.5 [allocator.requirements]:

Table 28 — Allocator requirements
Expression Return type Assertion/note
pre-/post-condition
Default
a.construct(c, args) (not used) Effect: Constructs an object of type C at c ::new ((void*)c) C(forward<Args>(args)...)

and from 15.5.3.5 [allocator.requirements] p9:

An allocator may constrain the types on which it can be instantiated and the arguments for which its construct member may be called. If a type cannot be used with a particular allocator, the allocator class or the call to construct may fail to instantiate.

I conclude the following from the wording:

  1. 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 21.2.1 [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.

  2. 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 21.3.11.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 T or by any InputIterator operation there are no effects. If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructible<T>::value is true, there are no effects. Otherwise, if an exception is thrown by the move constructor of a non-CopyInsertable T, 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), 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 21.3.11.3 [vector.capacity] p14:

Remarks: If an exception is thrown other than by the move constructor of a non-CopyInsertable T there 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 21.3.11.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) 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 21.2.3 [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:

  1. 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.

  2. 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 21.2.1 [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:

  1. allocator_traits<Allocator>::construct needs to tell its caller whether or not the construction was successful, in case of an exception.

  2. 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 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:

As consequence the requirement proposed in the Solution 1:

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.

Will no longer hold for the std::allocator.

Proposed resolution:


2471(i). copy_n's number of InputIterator increments unspecified

Section: 24.6.1 [alg.copy] Status: Open Submitter: Jonathan Wakely Opened: 2015-01-28 Last modified: 2018-06-21

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:


2472(i). Heterogeneous comparisons in the standard library can result in ambiguities

Section: 19.5.3.8 [tuple.rel], 19.10.10.2 [allocator.globals], 19.11.1.5 [unique.ptr.special], 19.11.3.7 [util.smartptr.shared.cmp], 26.5.6 [time.duration.comparisons], 26.6.6 [time.point.comparisons], 19.13.5 [scoped.adaptor.operators], 99 [reverse.iter.op==], 22.5.3.7 [move.iter.op.comp] Status: New Submitter: Richard Smith Opened: 2015-02-07 Last modified: 2017-02-03

Priority: 3

View other active issues in [tuple.rel].

View all other issues in [tuple.rel].

View all issues with New status.

Discussion:

The standard library specifies a lot of heterogeneous comparison operators. For instance:

template<class... TTypes, class... UTypes>
constexpr bool operator!=(const tuple<TTypes...>&, const tuple<UTypes...>&);

This has an unfortunate consequence:

#include <tuple>
#include <utility>

using namespace std::rel_ops;
std::tuple<int> a(0);
bool b = a != a;

The last line here is ill-formed due to ambiguity: it might be rel_ops::operator!=, and it might be the heterogeneous tuple operator!=. These are not partially ordered, because they have different constraints: rel_ops requires the types to match, whereas the tuple comparison requires both types to be tuples (but not to match). The same thing happens for user code that defines its own unconstrained 'template<typename T> operator!=(const T&, const T&)' rather than using rel_ops.

One straightforward fix would be to add a homogeneous overload for each heterogeneous comparison:

template<class... TTypes>
constexpr bool operator!=(const tuple<TTypes...>&, const tuple<TTypes...>&);

This is then unambiguously chosen over the other options in the preceding case. FWIW, libstdc++ already does this in some cases.

Proposed resolution:


2478(i). Unclear how wstring_convert uses cvtstate

Section: D.14.1 [depr.conversions.string] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2017-05-31

Priority: 4

View other active issues in [depr.conversions.string].

View all other issues in [depr.conversions.string].

View all issues with New status.

Discussion:

How do wstring_convert::from_bytes and wstring_convert::to_bytes use the cvtstate member?

Is it passed to the codecvt member functions? Is a copy of it passed to the member functions? "Otherwise it shall be left unchanged" implies a copy is used, but if that's really what's intended there are simpler ways to say so.

Proposed resolution:


2479(i). Unclear how wbuffer_convert uses cvtstate

Section: D.14.2 [depr.conversions.buffer] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2017-05-31

Priority: 4

View other active issues in [depr.conversions.buffer].

View all other issues in [depr.conversions.buffer].

View all issues with New status.

Discussion:

How does wbuffer_convert use the cvtstate member?

Is the same conversion state object used for converting both the get and put areas? That means a read which runs out of bytes halfway through a multibyte character will leave some shift state in cvtstate, which would then be used by a following write, even though the shift state of the get area is unrelated to the put area.

Proposed resolution:


2480(i). Error handling of wbuffer_convert unclear

Section: D.14.2 [depr.conversions.buffer] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2017-05-31

Priority: 4

View other active issues in [depr.conversions.buffer].

View all other issues in [depr.conversions.buffer].

View all issues with New status.

Discussion:

If a codecvt conversion returns codecvt_base::error should that be treated as EOF? An exception? Should all the successfully converted characters before a conversion error be available to the users of the wbuffer_convert and/or the internal streambuf, or does a conversion error lose information?

Proposed resolution:


2481(i). wstring_convert should be more precise regarding "byte-error string" etc.

Section: D.14.1 [depr.conversions.string] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2017-05-31

Priority: 4

View other active issues in [depr.conversions.string].

View all other issues in [depr.conversions.string].

View all issues with New status.

Discussion:

Paragraph 4 of D.14.1 [depr.conversions.string] introduces byte_err_string as "a byte string to display on errors". What does display mean? The string is returned on error, it's not displayed anywhere.

Paragraph 14 says "Otherwise, if the object was constructed with a byte-error string, the member function shall return the byte-error string." The term byte-error string is not used anywhere else.

Paragraph 17 talks about storing "default values in byte_err_string". What default value? Is "Hello, world!" allowed? If it means default-construction it should say so. If paragraph 14 says it won't be used what does it matter how it's initialized? The end of the paragraph refers to storing "byte_err in byte_err_string". This should be more clearly related to the wording in paragraph 14.

It might help if the constructor (and destructor) was specified before the other member functions, so it can more formally define the difference between being "constructed with a byte-error string" and not.

All the same issues apply to the wide_err_string member.

Proposed resolution:


2490(i). <regex> needs lots of noexcept

Section: 29 [re] Status: New Submitter: Stephan T. Lavavej Opened: 2015-03-27 Last modified: 2017-02-03

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:


2491(i). std::less<T*> in constant expression

Section: 19.14.7 [comparisons] Status: New Submitter: Agustín K-ballo Bergé Opened: 2015-04-01 Last modified: 2017-02-03

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.

Proposed resolution:

This wording is relative to N4618.

  1. Add at the end of 19.14.7 [comparisons]:

    -2- For templates less, greater, less_equal, and greater_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.


2493(i). initializer_list supports incomplete classes

Section: 16.10 [support.initlist] Status: New Submitter: David Krauss Opened: 2015-04-27 Last modified: 2017-02-03

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 15.5.4.8 [res.on.functions]/2.5 because initializer_list doesn't specifically allow incomplete types (as do for example std::unique_ptr (19.11.1 [unique.ptr]/5) and std::enable_shared_from_this (19.11.6 [util.smartptr.enab]/2)).

A resolution would be to copy-paste the relevant text from such a paragraph.

Proposed resolution:


2496(i). Certain hard-to-avoid errors not in the immediate context are not allowed to be triggered by the evaluation of type traits

Section: 19.15.4.3 [meta.unary.prop] Status: New Submitter: Hubert Tong Opened: 2015-05-07 Last modified: 2017-02-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 19.15.4.3 [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:


2497(i). Use of uncaught_exception()

Section: 28.7.5.1.3 [ostream::sentry] Status: New Submitter: Roger Orr Opened: 2015-05-08 Last modified: 2017-02-03

Priority: 3

View all other issues in [ostream::sentry].

View all issues with New status.

Discussion:

In the current 28.7.5.1.3 [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, calls os.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.

Proposed resolution:


2498(i). operator>>(basic_istream&&, T&&) returns basic_istream&, but should probably return basic_istream&&

Section: 28.7.4.5 [istream.rvalue] Status: New Submitter: Richard Smith Opened: 2015-05-08 Last modified: 2017-02-03

Priority: 3

View all other issues in [istream.rvalue].

View all issues with New status.

Discussion:

Consider:

auto& is = make_istream() >> x; // oops, istream object is already gone

With a basic_istream&& return type, the above would be ill-formed, and generally we'd preserve the value category properly.

[2015-06, Telecon]

JW: think this needs proper consideration, it would make

stream() >> x >> y >> z
go from 3 operator>> calls to 6 operator>> calls, and wouldn't prevent dangling references (change the example to auto&&)

Proposed resolution:


2504(i). basic_streambuf is not an abstract class

Section: 28.6.3 [streambuf] Status: New Submitter: Jonathan Wakely Opened: 2015-05-28 Last modified: 2017-02-03

Priority: 3

View all other issues in [streambuf].

View all issues with New status.

Discussion:

28.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 (10.6.3 [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:


2506(i). Underspecification of atomics

Section: 6.8.2 [intro.multithread], 30.7 [atomics.types.generic], 16.12 [support.runtime] Status: SG1 Submitter: Geoffrey Romer Opened: 2015-05-29 Last modified: 2018-03-18

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.8.2 [intro.multithread]. In particular:

6.8.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.8.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:

[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:


2507(i). codecvt_mode should be a bitmask type

Section: D.13 [depr.locale.stdcvt] Status: New Submitter: Jonathan Wakely Opened: 2015-06-08 Last modified: 2017-05-31

Priority: 3

View all other issues in [depr.locale.stdcvt].

View all issues with New status.

Discussion:

The enumeration type codecvt_mode is effectively a bitmask type (15.4.2.1.4 [bitmask.types]) with three elements, but isn't defined as such.

This harms usability because bitmask types are required to work well with bitwise operators, but codecvt_mode doesn't have overloaded operators, making it very inconvenient to combine values:

std::codecvt_utf16<char32_t, 0x10FFFF,
  static_cast<std::codecvt_mode>(std::little_endian|std::generate_header)>
cvt;

The static_cast harms readability and should not be necessary.

I suggest that either codecvt_mode is specified to be a bitmask type, or as a minimal fix we provide an overloaded operator| that returns the right type.

Proposed resolution:


2508(i). §[new.delete.dataraces] wording needs to be updated

Section: 16.6.2.4 [new.delete.dataraces] Status: New Submitter: Hans Boehm Opened: 2015-06-09 Last modified: 2017-02-03

Priority: 3

View all other issues in [new.delete.dataraces].

View all issues with New status.

Discussion:

16.6.2.4 [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:


2512(i). Y2K bites; what is an "unambiguous year identifier"?

Section: 27.4.5.1.2 [locale.time.get.virtuals] Status: Open Submitter: Hubert Tong Opened: 2015-06-19 Last modified: 2017-10-15

Priority: 4

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).

27.4.5.1.2 [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 27.4.5.1 [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:


2513(i). Missing requirements for basic_string::value_type

Section: 20.1 [strings.general] Status: New Submitter: Jonathan Wakely Opened: 2015-06-26 Last modified: 2017-02-03

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:


2524(i). generate_canonical can occasionally return 1.0

Section: 25.6.8.4.2 [rand.dist.pois.exp] Status: Open Submitter: Michael Prähofer Opened: 2015-08-20 Last modified: 2018-08-27

Priority: 2

View all issues with Open status.

Discussion:

Original title was: exponential_distribution<float> sometimes returns inf.

The random number distribution class template exponential_distribution<float> may return "inf" as can be seen from the following example program:

// compiled with
// g++ -std=c++11 Error_exp_distr.cpp

#include <iostream>
#include <random>
#include <bitset>

int main(){
  unsigned long long h;
  std::mt19937_64 mt1(1);
  std::mt19937_64 mt2(1);
  mt1.discard(517517);
  mt2.discard(517517);
  std::exponential_distribution<float> dis(1.0);
  h = mt2();
  std::cout << std::bitset<64>(h) << " " << (float) -log(1 - h/pow(2, 64)) << " " 
            << -log(1 - (float) h/pow(2, 64)) << " " << dis(mt1) << std::endl;
  h = mt2();
  std::cout << std::bitset<64>(h) << " " << (float) -log(1 - h/pow(2, 64)) << " " 
            << -log(1 - (float) h/pow(2, 64)) << " " << dis(mt1) << std::endl;
}

output:

0110010110001001010011000111000101001100111110100001110011100001 0.505218 0.505218 0.505218
1111111111111111111111111101010011000110011110011000110101100110 18.4143 inf inf

The reason seems to be that converting a double x in the range [0, 1) to float may result in 1.0f if x is close enough to 1. I see two possibilities to fix that:

  1. use internally double (or long double?) and then convert the result at the very end to float.

  2. take only 24 random bits and convert them to a float x in the range [0, 1) and then return -log(1 - x).

I have not checked if std::exponential_distribution<double> has the same problem: For float on the average 1 out of 224 (~107) draws returns "inf", which is easily confirmed. For double on the average 1 out of 253 (~1016) draws might return "inf", which I have not tested.

Marshall:
I don't think the problem is in std::exponential_distribution; but rather in generate_canonical.

Consider:

std::mt19937_64 mt2(1);
mt2.discard(517517);
std::cout << std::hexfloat << std::generate_canonical<float, std::numeric_limits<float>::digits>(mt2) << std::endl;
std::cout << std::hexfloat << std::generate_canonical<float, std::numeric_limits<float>::digits>(mt2) << std::endl;
std::cout << std::hexfloat << std::generate_canonical<float, std::numeric_limits<float>::digits>(mt2) << std::endl;

which outputs:

0x1.962532p-2
0x1p+0
0x1.20d0cap-3
but generate_canonical is defined to return a result in the range [0, 1).

[2015-10, Kona Saturday afternoon]

Options:

WEB: The one thing we cannot tolerate is any output range other than [0, 1).

WEB: I believe there may be a documented algorithm for the generator, and perhaps it's possible to discover en-route that the algorithm produces the wrong result and fix it.

MC: No. I analyzed this once, and here it is: the algorithm is in [rand.util.canonical], and it's all fine until p5. The expression S/R^k is mathematically less than one, but it may round to one.

GR: Could we change the rounding mode for the computation?

HH: No, because the rounding mode is global, not thread-local.

AM: SG1 wants to get rid of the floating point environment.

STL: The problem is that the standard specifies the implementation, and the implementation doesn't work.

MC: I'm not sure if nudging it down will introduce a subtle bias.

EF: I worry about how the user's choice of floating point environment affects the behaviour.

MS offers to run the topic past colleagues.

MC: Will set the status to open. STL wants to rename the issue. WEB wants to be able to find the issue by its original name still.

Mike Spertus to run the options past his mathematical colleagues, and report back.

[2017-11 Albuquerque Wednesday issue processing]

Choice: Rerun the algorithm if it gets 1.0.

Thomas K to provide wording; Marshall and STL to review.

[2018-08 Batavia Monday issue discussion]

Davis has a paper P0952 which resolves this.

Proposed resolution:


2528(i). Order of std::tuple construction unspecified

Section: 19.5.3.1 [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 19.5.3.1 [tuple.cnstr] p3 (emphasize mine):

-3- In the constructor descriptions that follow, let i be in the range [0,sizeof...(Types)) in order, Ti be the ith type in Types, and Ui be the ith type in a template parameter pack named UTypes, 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:


2530(i). Clarify observable side effects of releasing a shared state

Section: 31.6.5 [futures.state] Status: Open Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2017-02-03

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 31.6.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]

This is related to 2532

Fri AM: Moved to Open

Proposed resolution:


2532(i). Satisfying a promise at thread exit

Section: 31.6.6 [futures.promise] Status: Open Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2017-02-03

Priority: 3

View other active issues in [futures.promise].

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]

This is related to 2530

[2016-08-03, Billy O'Neal suggests concrete wording]

Fri AM: Moved to Open

Proposed resolution:

This wording is relative to N4606.

  1. Change 31.6.5 [futures.state] p7 as indicated:

    -7- When an asynchronous provider is said to abandon its shared state, it means:

    1. (7.1) — first, if that state is not ready or scheduled to be made ready at thread exit, the provider

      1. (7.1.1) — stores an exception object of type future_error with an error condition of broken_promise within its shared state; and then

      2. (7.1.2) — makes its shared state ready;

  2. Change 31.6.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.6.4.2 [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]


2533(i). [concurr.ts] Constrain threads where future::then can run a continuation

Section: 99 [concurr.ts::futures.unique_future] Status: SG1 Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2017-03-05

Priority: Not Prioritized

View all other issues in [concurr.ts::futures.unique_future].

View all issues with SG1 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:

  1. If the future is not ready when .then() is called, the .then argument may be run on the execution agent that fulfills the promise.

  2. 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).

Proposed resolution:


2546(i). Implementability of locale-sensitive UnicodeEscapeSequence matching

Section: 29.13 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2015-10-08 Last modified: 2017-02-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 29.13 [re.grammar] paragraph 2:

basic_regex member 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 (29.3 [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.

Proposed resolution:


2547(i). Container requirements (and other library text) should say "strict total order", not just "total order"

Section: 19.14.7 [comparisons], 21.2.1 [container.requirements.general], 31.3.2.1 [thread.thread.id] Status: New Submitter: Matt Austern Opened: 2015-10-08 Last modified: 2017-02-03

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 19.14.7 [comparisons]/14, the Optional container requirements in 21.2.1 [container.requirements.general], and 31.3.2.1 [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 24.7 [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:


2561(i). [fund.ts.v2] Incorrect exception specifications for 'swap' in C++ Extensions for Library Fundamentals

Section: 5.3.4 [fund.ts.v2::optional.object.swap], 3.7.8 [fund.ts.v2::propagate_const.modifiers] Status: New Submitter: Daniel Krügler Opened: 2015-11-14 Last modified: 2017-02-03

Priority: 3

View all issues with New status.

Discussion:

Addresses: fund.ts.v2

As pointed out in N4511, the Library fundamentals are affected by a similar problem as described in LWG 2456. First, it is caused by optional's member swap (5.3.4 [fund.ts.v2::optional.object.swap]):

void swap(optional<T>& rhs) noexcept(see below);

with

The expression inside noexcept is equivalent to:

is_nothrow_move_constructible_v<T> && noexcept(swap(declval<T&>(), declval<T&>()))

Again, the unqualified lookup for swap finds the member swap instead of the result of a normal argument-depending lookup, making this ill-formed.

A second example of such a problem recently entered the arena with the addition of the propagate_const template with another member swap (3.7.8 [fund.ts.v2::propagate_const.modifiers]):

constexpr void swap(propagate_const& pt) noexcept(see below);

-2- The constant-expression in the exception-specification is noexcept(swap(t_, pt.t_)).

A working approach is presented in N4511. By adding a new trait to the standard library and referencing this by the library fundamentals (A similar approach had been applied in the file system specification where the quoted manipulator from C++14 had been referred to, albeit the file system specification is generally based on the C++11 standard), optional's member swap exception specification could be rephrased as follows:

The expression inside noexcept is equivalent to:

is_nothrow_move_constructible_v<T> && is_nothrow_swappable_v<T>noexcept(swap(declval<T&>(), declval<T&>()))

and propagate_const's member swap exception specification could be rephrased as follows:

constexpr void swap(propagate_const& pt) noexcept(see below);

-2- The constant-expression in the exception-specification is is_nothrow_swappable_v<T>noexcept(swap(t_, pt.t_)).

[2016-02-20, Ville comments]

Feedback from an implementation:

libstdc++ already applies the proposed resolution for propagate_const, but not for optional.

[2016-02-20, Daniel comments]

A recent paper update has been provided: P0185R0.

[2016-03, Jacksonville]

Add a link to 2456

[2016-11-08, Issaquah]

Not adopted during NB comment resolution

Proposed resolution:


2564(i). [fund.ts.v2] std::experimental::function constructors taking allocator arguments may throw exceptions

Section: 4.2 [fund.ts.v2::func.wrap.func] Status: New Submitter: Tim Song Opened: 2015-12-05 Last modified: 2017-02-03

Priority: 3

View all other issues in [fund.ts.v2::func.wrap.func].

View all issues with New status.

Discussion:

Addresses: fund.ts.v2

[This is essentially LWG 2370, but deals with the fundamentals TS version rather than the one in the standard]

In 4.2 [fund.ts.v2::func.wrap.func] of library fundamentals TS, the constructors

template<class A> function(allocator_arg_t, const A&) noexcept;
template<class A> function(allocator_arg_t, const A&, nullptr_t) noexcept;

must type-erase and store the provided allocator, since the operator= specification requires using the "allocator specified in the construction of" the std::experimental::function object. This may require a dynamic allocation and so cannot be noexcept. Similarly, the following constructors

template<class A> function(allocator_arg_t, const A&, const function&); 
template<class A> function(allocator_arg_t, const A&, function&&);
template<class F, class A> function(allocator_arg_t, const A&, F);

cannot satisfy the C++14 requirement that they "shall not throw exceptions if [the function object to be stored] is a callable object passed via reference_wrapper or a function pointer" if they need to type-erase and store the allocator.

[2016-11-08, Issaquah]

Not adopted during NB comment resolution

Proposed resolution:

This wording is relative to N4562.

  1. Edit 4.2 [fund.ts.v2::func.wrap.func], class template function synopsis, as follows:

    namespace std {
      namespace experimental {
      inline namespace fundamentals_v2 {
    
        […]
    
        template<class R, class... ArgTypes>
        class function<R(ArgTypes...)> {
        public:    
          […]
          template<class A> function(allocator_arg_t, const A&) noexcept;
          template<class A> function(allocator_arg_t, const A&,
            nullptr_t) noexcept;
          […]
        };
    
        […]
    
      } // namespace fundamentals_v2
      } // namespace experimental
    
      […]
    
    } // namespace std
    
  2. Insert the following paragraphs after 4.2.1 [fund.ts.v2::func.wrap.func.con]/1:

    [Drafting note: This just reproduces the wording from C++14 with the "shall not throw exceptions for reference_wrapper/function pointer" provision deleted. — end drafting note]

    -1- When a function constructor that takes a first argument of type allocator_arg_t is invoked, the second argument is treated as a type-erased allocator (8.3). If the constructor moves or makes a copy of a function object (C++14 §20.9), including an instance of the experimental::function class template, then that move or copy is performed by using-allocator construction with allocator get_memory_resource().

    template <class A> function(allocator_arg_t, const A& a);
    template <class A> function(allocator_arg_t, const A& a, nullptr_t);
    

    -?- Postconditions: !*this.

    template <class A> function(allocator_arg_t, const A& a, const function& f);
    

    -?- Postconditions: !*this if !f; otherwise, *this targets a copy of f.target().

    -?- Throws: May throw bad_alloc or any exception thrown by the copy constructor of the stored callable object. [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f's target is an object holding only a pointer or reference to an object and a member function pointer. — end note]

    template <class A> function(allocator_arg_t, const A& a, function&& f);
    

    -?- Effects: If !f, *this has no target; otherwise, move-constructs the target of f into the target of *this, leaving f in a valid state with an unspecified value.

    template <class F, class A> function(allocator_arg_t, const A& a, F f);
    

    -?- Requires: F shall be CopyConstructible.

    -?- Remarks: This constructor shall not participate in overload resolution unless f is Callable (C++14 §20.9.11.2) for argument types ArgTypes... and return type R.

    -?- Postconditions: !*this if any of the following hold:

    • f is a null function pointer value.

    • f is a null member pointer value.

    • F is an instance of the function class template, and !f.

    -?- Otherwise, *this targets a copy of f initialized with std::move(f). [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f's target is an object holding only a pointer or reference to an object and a member function pointer. — end note]

    -?- Throws: May throw bad_alloc or any exception thrown by F's copy or move constructor.

    -2- In the following descriptions, let ALLOCATOR_OF(f) be the allocator specified in the construction of function f, or allocator<char>() if no allocator was specified.

    […]


2592(i). Require that chrono::duration_casts from smaller durations to larger durations do not overflow

Section: 26.2 [time.syn] Status: New Submitter: Andy Giese Opened: 2016-02-05 Last modified: 2017-02-03

Priority: 4

View all other issues in [time.syn].

View all issues with New status.

Discussion:

Currently 26.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:

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.

Proposed resolution:

This wording is relative to N4567.

  1. Change 26.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 4645 bits,       milli> milliseconds;
    typedef duration<signed integer type of at least 3735 bits             > seconds;
    typedef duration<signed integer type of at least 3229 bits, ratio<  60>> minutes;
    typedef duration<signed integer type of at least 2723 bits, ratio<3600>> hours;
    
    […]
    

2594(i). Contradicting definition of empty shared_ptr on shared_ptr(nullptr, d)

Section: 19.11.3 [util.smartptr.shared] Status: New Submitter: Kazutoshi Satoda Opened: 2016-02-20 Last modified: 2017-02-03

Priority: 3

View other active issues in [util.smartptr.shared].

View all other issues in [util.smartptr.shared].

View all issues with New status.

Discussion:

Latest draft (N4567) 19.11.3 [util.smartptr.shared] p1 says:

A shared_ptr object 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.

19.11.3.1 [util.smartptr.shared.const] p8 says about the effect of shared_ptr(nullptr_t p, D d):

Effects: Constructs a shared_ptr object that owns the object p and the deleter d.

Please note that it says "owns the object". This was intentionally changed from "the pointer" as a part of resolution for LWG defect 758, 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 19.11.3 [util.smartptr.shared] p1.

These cause a contradiction:

19.11.3.1 [util.smartptr.shared.const] p9 sets a postcondition use_count() == 1 on shared_ptr(nullptr, d). But 19.11.3.5 [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 19.11.3 [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 19.11.3 [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.

  1. Change 19.11.3 [util.smartptr.shared] p1 as indicated:

    -1- The shared_ptr class template stores a pointer, usually obtained via new. shared_ptr implements 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. A shared_ptr object is empty if it does not own an objecta pointer.


2595(i). reverse_iterator::operator[]'s return type revisited

Section: 22.5.1.1 [reverse.iterator], 99 [reverse.iter.opindex] Status: New Submitter: Robert Haberlach Opened: 2016-02-28 Last modified: 2017-02-03

Priority: 3

View all other issues in [reverse.iterator].

View all issues with New status.

Discussion:

Issue 386 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 22.5.1.1 [reverse.iterator]'s synopsis and 99 [reverse.iter.opindex] to use reference instead of unspecified.

Proposed resolution:

This wording is relative to N4582.

  1. Edit 22.5.1.1 [reverse.iterator], class template synopsis, as indicated:

    namespace std {
      template <class Iterator>
      class reverse_iterator {
      public:
        […]
        typedef typename iterator_traits<Iterator>::reference reference;
        […]
        constexpr referenceunspecified operator[](difference_type n) const;
        […]
      };
    }
    
  2. Change 99 [reverse.iter.opindex] before p1 as indicated:

    constexpr referenceunspecified operator[](
      typename reverse_iterator<Iterator>::difference_type n) const;
    

2599(i). Library incomplete type permission phrase is unclear

Section: 19.2.6 [declval], 19.11.1 [unique.ptr], 19.11.1.1.1 [unique.ptr.dltr.general], 19.11.3 [util.smartptr.shared], 19.11.4 [util.smartptr.weak], 19.11.6 [util.smartptr.enab] Status: New Submitter: Zhihao Yuan Opened: 2016-03-08 Last modified: 2017-02-03

Priority: 3

View all issues with New status.

Discussion:

Currently the phrase to grant this permission is:

The template parameter T of LibraryTemplate may be an incomplete type.

Two problems:

  1. The timing is unclear. We always allow specializations like LibraryTemplate<Incomp>* p;

  2. 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 parameter T.

as shown here.

Or, to copy N4510's wording:

An incomplete type T may be used when instantiating LibraryTemplate.

Proposed resolution:


2600(i). ios_base must store inaccessible iostate flags

Section: 28.5.3.5 [ios.base.storage] Status: Tentatively NAD Submitter: David Krauss Opened: 2016-03-14 Last modified: 2018-06-21

Priority: Not Prioritized

View all other issues in [ios.base.storage].

View all issues with Tentatively NAD status.

Discussion:

DR 41, "Ios_base needs clear(), exceptions()" stopped short of providing the interface suggested in its title, but it did require the underlying state to be stored in ios_base. Because rdstate() is also missing, ios_base manipulators relying on iword and pword cannot detect failure. The only safe alternative is to manipulate a derived class, which must be a template.

libc++ already provides the interface as a nonconforming extension. libstdc++ implements the internal state but leaves it frustratingly inaccessible, as specified. Any conforming implementation should be able to provide the interface without ABI problems.

[2016-04, Issues Telecon]

This is really a request for an (feature) API. Passing to LEWG.

[LEWG Kona 2017]

Recommend NAD: iostreams aren't used in this way enough to spend committee time on it. However, a paper could change our minds.

Proposed resolution:


2675(i). register_callback can fail

Section: 28.5.3.6 [ios.base.callback] Status: New Submitter: David Krauss Opened: 2016-03-14 Last modified: 2017-02-03

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.

Proposed resolution:


2690(i). invoke<R>

Section: 19.14.4 [func.invoke] Status: LEWG Submitter: Zhihao Yuan Opened: 2016-03-25 Last modified: 2018-08-27

Priority: Not Prioritized

View other active issues in [func.invoke].

View all other issues in [func.invoke].

View all issues with LEWG status.

Discussion:

In N4169 the author dropped the invoke<R> support by claiming that it's an unnecessary cruft in TR1, obsoleted by C++11 type inference. But now we have some new business went to *INVOKE*(f, t1, t2, ..., tN, R), that is to discard the return type when R is void. This form is very useful, or possible even more useful than the basic form when implementing a call wrapper. Also note that the optional R support is already in std::is_callable and std::is_nothrow_callable.

[2016-07-31, Tomasz Kamiński comments]

The lack of invoke<R> was basically a result of the concurrent publication of the never revision of the paper and additional special semantics of INVOKE(f, args..., void).

In contrast to existing std::invoke function, the proposed invoke<R> version is not SFINAE friendly, as elimination of the standard version of invoke is guaranteed by std::result_of_t in the result type that is missing for proposed invoke<R> version. To provide this guarantee, following remarks shall be added to the specification:

Remarks: This function shall not participate in overload resolution unless is_callable_v<F(Args...), R> is true.

[2016-08-01, Tomasz Kamiński and Zhihao Yuan update the proposed wording]

Previous resolution [SUPERSEDED]:

This wording is relative to N4606.

  1. Modify 19.14 [function.objects]/2, header <functional> synopsis, as indicated:

    namespace std {
      // 20.12.3, invoke:
      template <class F, class... Args> result_of_t<F&&(Args&&...)> invoke(F&& f, Args&&... args);
      template <class R, class F, class... Args> R invoke(F&& f, Args&&... args);
    
  2. Add the following sequence of paragraphs after 19.14.4 [func.invoke]/1 as indicated:

    template <class R, class F, class... Args> R invoke(F&& f, Args&&... args);
    

    -?- Returns: INVOKE(std::forward<F>(f), std::forward<Args>(args)..., R) (19.14.3 [func.require]).

    -?- Remarks: This function shall not participate in overload resolution unless is_callable_v<F(Args...), R> is true.

[2016-09-04, Tomasz Kamiński comments and improves wording]

The usage of is_callable_v<F(Args...), R> causes problem in situation when either F or Args is an abstract type and the function type F(Args...) cannot be formed or when one of the args is cv-qualified, as top-level cv-qualification for function parameters is dropped by language rules. It should use is_callable_v<F&&(Args&&...), R> instead.

Previous resolution [SUPERSEDED]:

This wording is relative to N4606.

  1. Modify 19.14 [function.objects]/2, header <functional> synopsis, as indicated:

    namespace std {
      // 20.12.3, invoke:
      template <class F, class... Args> result_of_t<F&&(Args&&...)> invoke(F&& f, Args&&... args);
      template <class R, class F, class... Args> R invoke(F&& f, Args&&... args);
    
  2. Add the following sequence of paragraphs after 19.14.4 [func.invoke]/1 as indicated:

    template <class R, class F, class... Args> R invoke(F&& f, Args&&... args);
    

    -?- Returns: INVOKE(std::forward<F>(f), std::forward<Args>(args)..., R) (19.14.3 [func.require]).

    -?- Remarks: This function shall not participate in overload resolution unless is_callable_v<F&&(Args&&...), R> is true.

[2018-08-22, Zhihao Yuan provides improved wording]

Proposed resolution:

This wording is relative to N4762.

  1. Modify 19.14.1 [functional.syn], header <functional> synopsis, as indicated:

    namespace std {
      // 19.14.4 [func.invoke], invoke
      template<class F, class... Args>
        invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
          noexcept(is_nothrow_invocable_v<F, Args...>);
      template <class R, class F, class... Args>
        R invoke(F&& f, Args&&... args)
          noexcept(is_nothrow_invocable_r_v<R, F, Args...>);
    
  2. Add the following sequence of paragraphs after 19.14.4 [func.invoke]/1 as indicated:

    template <class R, class F, class... Args>
      R invoke(F&& f, Args&&... args)
        noexcept(is_nothrow_invocable_r_v<R, F, Args...>);
    

    -?- Constraints: is_invocable_r_v<R, F, Args...>.

    -?- Returns: INVOKE<R>(std::forward<F>(f), std::forward<Args>(args)...) (19.14.3 [func.require]).


2691(i). money_base::space and do_put: U+0020 versus fill

Section: 27.4.6.3 [locale.moneypunct] Status: New Submitter: Hubert Tong Opened: 2016-04-12 Last modified: 2017-02-03

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 (27.4.6.2.2 [locale.money.put.virtuals]), it is not clear that

  1. "the number of characters generated for the specified format" (excluding fill padding) includes exactly one character for money_base::space (if present), and

  2. 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.

  1. Change 27.4.6.3 [locale.moneypunct] paragraph 2 as indicated:

    -2- Where none or space appears, white space is permitted in the format, except where none appears at the end, in which case no white space is permitted. For input, the value space indicates that at least one space is required at that position. For output, the value space indicates one instance of the fill character (27.4.6.2.2 [locale.money.put.virtuals]).The value space indicates that at least one space is required at that position. Where symbol appears, the sequence of characters returned by curr_symbol() is permitted, and can be required. Where sign appears, the first (if any) of the sequence of characters returned by positive_sign() or negative_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. Where value appears, the absolute numeric monetary value is required.


2695(i). "As if" unclear in [member.functions]

Section: 15.5.5.5 [member.functions] Status: New Submitter: Hubert Tong Opened: 2016-04-15 Last modified: 2017-02-03

Priority: 3

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.

Proposed resolution:


2702(i). num_put::do_put(..., bool) performs ill-formed do_put call

Section: 27.4.2.2.2 [facet.num.put.virtuals] Status: New Submitter: Hubert Tong Opened: 2016-05-07 Last modified: 2017-02-03

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 27.4.2.2.2 [facet.num.put.virtuals] paragraph 6 cannot select a best viable function in overload resolution given the overloads listed for do_put in 27.4.2.2 [locale.nm.put].

There is implementation divergence:

It appears that the resolution to DR 359 attempted a fix; however, the relevant portion of the change was not applied to the WP.

Proposed resolution:


2703(i). No provision for fill-padding when boolalpha is set

Section: 27.4.2.2.2 [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 27.4.2.2.2 [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_putint, long, etc all are covered in 27.4.2.2.2 [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:


2705(i). Questionable precondition on Sequence containers a.assign(n, t)

Section: 21.2.3 [sequence.reqmts] Status: New Submitter: Kazutoshi Satoda Opened: 2016-05-08 Last modified: 2017-02-03

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 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 21.2.3 [sequence.reqmts] p3.

In Tables 107 and 108, X denotes a sequence container class, a denotes a value of X containing elements of type T, […] n denotes a value of X::size_type, […] t denotes an lvalue or a const rvalue of X::value_type, […]

[…]

Table 107 — Sequence container requirements (in addition to container)
Expression Return type Assertion/note
pre-/post-condition
[…]
a.assign(n, t) void Requires: T shall be CopyInsertable into X and CopyAssignable.
pre: t is not a reference into a.
Replaces elements in a with n copies of t.

I looked into the following implementations:

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 20.3.2.6.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. 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.

Proposed resolution:

This wording is relative to N4582.

  1. In 21.2.3 [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) void Requires: T shall be CopyInsertable into X and CopyAssignable.
    pre: t is not a reference into a.
    Replaces elements in a with n copies of t.

2708(i). recursive_directory_iterator::recursion_pending() is incorrectly specified

Section: 28.11.13.1 [fs.rec.dir.itr.members] Status: Open Submitter: Eric Fiselier Opened: 2016-05-09 Last modified: 2018-08-27

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 (28.11.13.1 [fs.rec.dir.itr.members]/24):

Returns: true if disable_recursion_pending() has not been called subsequent to the prior construction or increment operation, otherwise false.

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.

Previous resolution from Eric [SUPERSEDED]:

This wording is relative to N4582.

  1. Change 28.11.13.1 [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() == options for the signatures with a directory_options argument, otherwise options() == directory_options::none.

    • options() == options for the signatures with a directory_options argument, otherwise options() == directory_options::none.

    • recursion_pending() == true.

    […]

    [Drafting note: The following changes the specification of recursion_pending() seemingly recursive. Perhaps it would be easier to specify recursion_pending() in terms of a exposition only member in recursive_directory_iterator.]

    bool recursion_pending() const;
    

    […]

    -24- Returns: true if disable_recursion_pending() has not been called subsequent to the prior construction or increment operation, otherwise falsefalse if disable_recursion_pending() has been called subsequent to the prior construction or increment operation, otherwise the value of recursion_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.

  1. Change [fs.rec.dir.itr] as indicated:

        […]
    
        // other members as required by 22.3.5.2 [input.iterators], input iterators
    
      private:
        bool recurse_; // exposition-only
      };
    }
    
  2. Change 28.11.13.1 [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() == options for the signatures with a directory_options argument, otherwise options() == directory_options::none.

    • options() == options for the signatures with a directory_options argument, otherwise options() == 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(), and recursion_pending()recurse_ have the values that rhs.options(), rhs.depth(), and rhs.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(), and recursion_pending()recurse_ have the values that rhs.options(), rhs.depth(), and rhs.recursion_pending()rhs.recurse_, respectively, had before the function call.

    […]

    bool recursion_pending() const;
    

    -21- Returns: true if disable_recursion_pending() has not been called subsequent to the prior construction or increment operation, otherwise falserecurse_.

    […]

    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- Postconditions: 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 28.11.13 [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:

[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.

Proposed resolution:

This wording is relative to N4750.

  1. Change 28.11.13.1 [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, let options be directory_options::none.

    -2- Effects: […]

    -3- Postconditions: options() == options for the signatures with a directory_options argument, otherwise options() == directory_options::none.

    • this->options() == options

    • recursion_pending() == true

    […]

    recursive_directory_iterator(const recursive_directory_iterator& rhs);
    

    -7- Effects: Constructs an object of class recursive_directory_iterator iterator that denotes the same directory entry as rhs, if any..

    -8- Postconditions: If rhs is dereferenceable,

    […]

    recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;
    

    -9- Effects: Constructs an object of class recursive_directory_iterator iterator that denotes the directory entry denoted by rhs before the function call, if any..

    -10- Postconditions: If rhs is dereferenceable, […]

    recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);
    

    -11- Effects: If *this and rhs are the same object, the member has no effect. Causes *this to denote the same directory entry denoted by rhs, if any.

    -12- Postconditions: If rhs is dereferenceable,

    […]

    recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;
    

    -14- Effects: If *this and rhs are the same object, the member has no effect. Causes *this to denote the directory entry denoted by rhs before the function call, if any.

    -15- Postconditions: If rhs was dereferenceable before the function call, […]

    -16- Returns: *this.

    -x- Remarks: If *this and rhs do not refer to the same object, the resulting state of rhs is unspecified (15.5.5.15 [lib.types.movedfrom]).

    directory_options options() const;
    

    -17- Returns: The value of the argument passed to the constructor for the options parameter, if present, otherwise directory_options::none established by the most recently called member that has a postcondition for options().

    […]

    bool recursion_pending() const;
    

    -21- Returns: true if disable_recursion_pending() has not been called subsequent to the prior construction or increment operation, otherwise false. If disable_recursion_pending() has been called on a copy of *this, an unspecified value. Otherwise, the value established for recursion_pending() by the postcondition of the most recent construction, assignment, increment, or disable_recursion_pending operation.

    […]

    recursive_directory_iterator& operator++();
    recursive_directory_iterator& increment(error_code& ec);
    

    -23- Effects: As specified for the prefix increment operation of Input iterators (22.3.5.2 [input.iterators]), except that: […]

    -?- Postconditions: If *this is dereferenceable, recursion_pending() == true.

    […]

    void pop();
    void pop(error_code& ec);
    

    -26- Effects: If depth() == 0, set *this to recursive_directory_iterator(). […]

    -?- Postconditions: If *this is dereferenceable, recursion_pending() == true.

    […]


2713(i). More missing allocator-extended constructors for unordered containers

Section: 21.5 [unord] Status: New Submitter: Billy Robert O'Neal III Opened: 2016-05-20 Last modified: 2017-10-15

Priority: 3

View all other issues in [unord].

View all issues with New status.

Discussion:

The resolution of LWG 2210 missed constructors accepting a range or initializer list and allocator.

Previous resolution [SUPERSEDED]:

This wording is relative to N4582.

  1. Add to the synopsis in 21.5.4.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) { }
        […]
      };
    }
    
  2. Add to the synopsis in 21.5.5.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) { }
        […]
      };
    }
    
  3. Add to the synopsis in 21.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) { }
        […]
      };
    }
    
  4. Add to the synopsis in 21.5.7.1 [unord.multiset.overview] p3:

    namespace std {
      template <class Key,
        class Hash = hash<Key>,
        class Pred = std::equal_to<Key>,
        class Allocator = std::allocator<Key> > {
      class unordered_multiset {
      public:
        […]
        unordered_multiset(size_type n, const hasher& hf, const allocator_type& a)
          : unordered_multiset(n, hf, key_equal(), a) { }
        template <class InputIterator>
          unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a)
          : unordered_multiset(f, l, see below, hasher(), key_equal(), a) { }
        template <class InputIterator>
          unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a)
          : unordered_multiset(f, l, n, hasher(), key_equal(), a) { }
        template <class InputIterator>
          unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf,
                const allocator_type& a)
          : unordered_multiset(f, l, n, hf, key_equal(), a) { }
        unordered_multiset(initializer_list<value_type> il, const allocator_type& a)
          : unordered_multiset(il, see below, hasher(), key_equal(), a) { }
        unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a)
          : unordered_multiset(il, n, hasher(), key_equal(), a) { }
        […]
      };
    }
    

[2016-06, Oulu — Daniel comments and provides new wording]

During the LWG discussion of this issue it has been observed, that the interpretation of the embedded see below is not really clear and that we should split declaration and definition of the new overloads, so that we have a place that allows us to specify what "see below" stands for. In addition, the new wording wraps the "see below" as "size_type(see below)" to clarify the provided expression type, similar as we did for the default constructor of unordered_map.

[Oulu, 2016-06]

Alisdair to review wording.

Previous resolution [SUPERSEDED]:

This wording is relative to N4594.

  1. Add to the synopsis in 21.5.4.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) { }
        […]
      };
    }
    
  2. Insert the following new prototype specification just after 21.5.4.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.

  3. Add to the synopsis in 21.5.5.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) { }
        […]
      };
    }
    
  4. Insert the following new prototype specification just after 21.5.5.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.

  5. Add to the synopsis in 21.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) { }
        […]
      };
    }
    
  6. Insert the following new prototype specification just after 21.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.

  7. Add to the synopsis in 21.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) { }
        […]
      };
    }
    
  8. Insert the following new prototype specification just after 21.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_type argument is implementation-defined.

Proposed resolution:

This resolution is relative to N4687.

  1. Add to the synopsis in 21.5.4.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) { }
        […]
      };
    }
    
  2. Insert the following new prototype specification just after 21.5.4.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_type argument is implementation-defined.

  3. Add to the synopsis in 21.5.5.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) { }
        […]
      };
    }
    
  4. Insert the following new prototype specification just after 21.5.5.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_type argument is implementation-defined.

  5. Add to the synopsis in 21.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) { }
        […]
      };
    }
    
  6. Insert the following new prototype specification just after 21.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_type argument is implementation-defined.

  7. Add to the synopsis in 21.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) { }
        […]
      };
    }
    
  8. Insert the following new prototype specification just after 21.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_type argument is implementation-defined.


2714(i). complex stream extraction underspecified

Section: 25.4.6 [complex.ops] Status: New Submitter: Tim Song Opened: 2016-05-23 Last modified: 2018-11-11

Priority: 3

View all other issues in [complex.ops].

View all issues with New status.

Discussion:

The specification of operator>>(istream&, complex<T>&) is extremely short on details. It currently reads, in its entirety (25.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 x of the form: u, (u), or (u,v), where u is the real part and v is the imaginary part (28.7.4.2 [istream.formatted]).

Requires: The input values shall be convertible to T.

If bad input is encountered, calls is.setstate(ios_base::failbit) (which may throw ios::failure (28.5.5.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:

Previous resolution [SUPERSEDED]:

Drafting note: the following wording is based on:

This wording is relative to N4582.

  1. Replace 25.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.

    • If the character extracted is equal to is.widen('('), extracts an object u of type T from is, then extracts a character from is.
      • If this character is equal to is.widen(')'), then assigns complex<T>(u) to x.
      • Otherwise, if this character is equal to is.widen(','), extracts an object v of type T from is, then extracts a character from is. If this character is equal to is.widen(')'), then assigns complex<T>(u, v) to x; otherwise returns the character to is and the extraction fails.
      • Otherwise, returns the character to is and the extraction fails.
    • Otherwise, returns the character to is, extracts an object u of type T from is, and assigns complex<T>(u) to x.
    In the description above, characters are extracted from is as if by operator>> (28.7.4.2.3 [istream.extractors]), and returned to the stream as if by basic_istream::putback (28.7.4.3 [istream.unformatted]). Character equality is determined using traits::eq. An object t of type T is extracted from is as if by is >> 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 throw ios::failure (28.5.5.4 [iostate.flags])).

    -?- Returns: is.

    -?- [Note: This extraction is performed as a series of simpler extractions. Therefore, the skipping of whitespace is specified to be the same for each of the simpler extractions. — end note]

[2017-12-13 Tim Song adjusts the P/R to avoid relying on putback.]

Proposed resolution:

Drafting note: the following wording assumes that:

This wording is relative to N4778.

  1. Replace 25.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 (28.7.4.2.1 [istream.formatted.reqmts]) of is that returns the next character that would be extracted from is by operator>>. [Note: The sentry object is constructed and destroyed, but the returned character is not extracted from the stream. — end note]

    • If PEEK(is) is not equal to is.widen('('), extracts an object u of type T from is, and assigns complex<T>(u) to x.
    • Otherwise, extracts that character from is, then extracts an object u of type T from is, then:
      • If PEEK(is) is equal to is.widen(')'), then extracts that character from is and assigns complex<T>(u) to x.
      • Otherwise, if it is equal to is.widen(','), then extracts that character from is and then extracts an object v of type T from is, then:
        • If PEEK(is) is equal to is.widen(')'), then extracts that character from is and assigns complex<T>(u, v) to x.
        • Otherwise, the extraction fails.
      • Otherwise, the extraction fails.
    In the description above, characters are extracted from is as if by operator>> (28.7.4.2.3 [istream.extractors]), character equality is determined using traits::eq, and an object t of type T is extracted from is as if by is >> t.

    If any extraction operation fails, no further operation is performed and the whole extraction fails.

    On failure, assigns complex<T>() to x and calls is.setstate(ios_base::failbit) (which may throw ios::failure (28.5.5.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]


2730(i). numeric_limits primary template definition

Section: 16.3.4 [numeric.limits] Status: Open Submitter: Richard Smith Opened: 2016-06-09 Last modified: 2018-11-11

Priority: 3

View other active issues in [numeric.limits].

View all other issues in [numeric.limits].

View all issues with Open status.

Discussion:

I've received this report at the project editor mail alias, and it seems like it may be worthy of a LWG issue:

I recently had this problem:

This broke the sorting for me on different platforms, and it was quite difficult to determine why. If the default numeric_limits didn't default to 0s and false values (18.3.2.4 of N4582), and instead static_asserted, causing my code to not compile, I would have found the solution immediately.

I know that __uint128_t is 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 0 and false? 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 unspecified numeric_limits from 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.

Proposed resolution:


2731(i). Existence of lock_guard<MutexTypes...>::mutex_type typedef unclear

Section: 31.4.4.1 [thread.lock.guard] Status: Open Submitter: Eric Fiselier Opened: 2016-06-13 Last modified: 2018-03-18

Priority: 3

View all other issues in [thread.lock.guard].

View all issues with Open status.

Discussion:

In the synopsis of 31.4.4.2 [thread.lock.scoped] the mutex_type typedef is specified as follows:

template <class... MutexTypes>
class scoped_lock {
public:
  typedef Mutex mutex_type; // If MutexTypes... consists of the single type Mutex
  […]
};

The comment seems ambiguous as it could mean either:

  1. sizeof...(MutexTypes) == 1.
  2. sizeof...(MutexTypes) >= 1 and every type in MutexTypes... is the same type.

I originally took the language to mean (2), but upon further review it seems that (1) is the intended interpretation, as suggested in the LEWG discussion in Lenexa.

I think the language should be clarified to prevent implementation divergence.

[2016-07, Toronto Saturday afternoon issues processing]

General feeling that sizeof(MutexTypes...) == 1 is a better way to state the requirement.

Reworked the text to refer to scoped_lock instead of lock_guard

Marshall and Eric to reword and discuss on reflector. Status to Open

[2018-3-14 Wednesday evening issues processing; general agreement to adopt once the wording is updated.]

2018-03-18 Marshall provides updated wording.

Previous resolution: [SUPERSEDED]

This wording is relative to N4594.

  1. Edit 31.4.4.1 [thread.lock.guard]/1, class template lock_guard synopsis, as indicated:

    template <class... MutexTypes>
    class lock_guard {
    public:
      typedef Mutex mutex_type; // Only iIf MutexTypes... consists of theexpands to a single type Mutex
      […]
    };
    

Proposed resolution:

This wording is relative to N4727.

  1. Edit 31.4.4.1 [thread.lock.guard]/1, class template lock_guard synopsis, as indicated:

    template <class... MutexTypes>
    class scoped_lock {
    public:
      using mutex_type = Mutex; // Only iIf sizeof(MutexTypes...) == 1 MutexTypes... consists of the single type Mutex
      […]
    };
    

2737(i). Consider relaxing object size restrictions for single-object allocation functions

Section: 16.6.2.1 [new.delete.single] Status: New Submitter: Clark Nelson Opened: 2016-06-21 Last modified: 2017-02-03

Priority: 3

View other active issues in [new.delete.single].

View all other issues in [new.delete.single].

View all issues with New status.

Discussion:

It should be considered whether the description of the single-object allocation functions should say "or smaller", like the array allocation functions. For example, according to 16.6.2.1 [new.delete.single] p1 (emphasis mine):

The allocation function (3.7.4.1) called by a new-expression (5.3.4) to allocate size bytes of storage suitably aligned to represent any object of that size.

In contrast to this, 16.6.2.2 [new.delete.array] p1 says (emphasis mine):

The allocation function (3.7.4.1) called by the array form of a new-expression (5.3.4) to allocate size bytes of storage suitably aligned to represent any array object of that size or smaller. (footnote: It is not the direct responsibility of operator new[](std::size_t) or operator delete[](void*) to note the repetition count or element size of the array. Those operations are performed elsewhere in the array new and delete expressions. The array new expression, may, however, increase the size argument to operator new[](std::size_t) to obtain space to store supplemental information.)

Proposed resolution:


2741(i). is_partitioned requirements need updating

Section: 24.7.4 [alg.partitions] Status: Open Submitter: Jonathan Wakely Opened: 2016-07-06 Last modified: 2017-07-16

Priority: 3

View all other issues in [alg.partitions].

View all issues with Open status.

Discussion:

Requires: InputIterator's value type shall be convertible to Predicate's argument type.

This seems to date from the days of adaptable function objects with an argument_type typedef, but in modern C++ the predicate might not have an argument type. It could have a function template that accepts various arguments, so it doesn't make sense to state requirements in terms of a type that isn't well defined.

[2016-07, Toronto Saturday afternoon issues processing]

The proposed resolution needs to be updated because the underlying wording has changed. Also, since the sequence is homogeneous, we shouldn't have to say that the expression is well-formed for all elements in the range; that implies that it need not be well-formed if the range is empty.

Marshall and JW to reword. Status to Open

Proposed resolution:

This wording is relative to N4594.

  1. Edit 24.7.4 [alg.partitions] as indicated:

    template <class InputIterator, class Predicate>
    bool is_partitioned(InputIterator first, InputIterator last, Predicate pred);
    

    -1- Requires: InputIterator's value type shall be convertible to Predicate's argument typeThe expression pred(*i) shall be well-formed for all i in [first, last).

    […]

    template <class InputIterator, class OutputIterator1,
              class OutputIterator2, class Predicate>
      pair<OutputIterator1, OutputIterator2>
      partition_copy(InputIterator first, InputIterator last,
                     OutputIterator1 out_true, OutputIterator2 out_false,
                     Predicate pred);
    

    -12- Requires: InputIterator's value type shall be CopyAssignable, and shall be writable (22.3.1 [iterator.requirements.general]) to the out_true and out_false OutputIterators, and shall be convertible to Predicate's argument typethe expression pred(*i) shall be well-formed for all i in [first, last). The input range shall not overlap with either of the output ranges.

    […]

    template<class ForwardIterator, class Predicate>
      ForwardIterator partition_point(ForwardIterator first,
                                      ForwardIterator last,
                                      Predicate pred);
    

    -16- Requires: ForwardIterator's value type shall be convertible to Predicate's argument typeThe expression pred(*i) shall be well-formed for all i in [first, last). [first, last) shall be partitioned by pred, i.e. all elements that satisfy pred shall appear before those that do not.

    […]


2743(i). p0083r3 node_handle private members missing "exposition only" comment

Section: 21.2.4.1 [container.node.overview] Status: New Submitter: Richard Smith Opened: 2016-07-08 Last modified: 2017-02-03

Priority: 3

View all issues with New status.

Discussion:

The private members of node_handle are missing the usual "exposition only" comment. As a consequence, ptr_ and alloc_ now appear to be names defined by the library (so programs defining these names as macros before including a library header have undefined behavior).

Presumably this is unintentional and these members should be considered to be for exposition only.

It's also not clear whether the name node_handle is reserved for library usage or not; 21.2.4.1 [container.node.overview]/3 says the implementation need not provide a type with this name, but doesn't seem to rule out the possibility that an implementation will choose to do so regardless.

Daniel:

A similar problem seems to exist for the exposition-only type call_wrapper from p0358r1, which exposes a private data member named fd and a typedef FD.

[2016-07 Chicago]

Jonathan says that we need to make clear that the name node_handle is not reserved

Proposed resolution:


2746(i). Inconsistency between requirements for emplace between optional and variant

Section: 19.6.3 [optional.optional] Status: New Submitter: Richard Smith Opened: 2016-07-13 Last modified: 2017-02-03

Priority: 3

View other active issues in [optional.optional].

View all other issues in [optional.optional].

View all issues with New status.

Discussion:

Referring to N4604:

In [optional.object.assign]: emplace (normal form) has a Requires that the construction works.

Requires: is_constructible_v<T, Args&&...> is true.

emplace (initializer_list form) has a SFINAE condition:

Remarks: […] unless is_constructible_v<T, initializer_list<U>&, Args&&...> is true.

In 19.8.3.3 [any.modifiers]: emplace (normal form) has a Requires that the construction works:

Requires: is_constructible_v<T, Args...> is true.

emplace (initializer_list form) has a SFINAE condition:

Remarks: […] unless is_constructible_v<T, initializer_list<U>&, Args...> is true.

In 19.7.3.4 [variant.mod]: emplace (T, normal form) has a SFINAE condition:

Remarks: […] unless is_constructible_v<T, Args...> is true, and T occurs exactly once in Types....

emplace (Idx, normal form) has a both a Requires and a SFINAE condition:

Requires: I < sizeof...(Types)

Remarks: […] unless is_constructible_v<T, Args...> is true, and T occurs exactly once in Types....

emplace (T, initializer_list form) has a SFINAE condition:

Remarks: […] unless is_constructible_v<T, initializer_list<U>&, Args...> is true, and T occurs exactly once in Types....

emplace (Idx, initializer_list form) has a both a Requires and a SFINAE condition:

Requires: I < sizeof...(Types)

Remarks: […] unless is_constructible_v<T, Args...> is true, and T occurs exactly once in Types....

Why the inconsistency? Should all the cases have a SFINAE requirement?

I see that variant has an additional requirement (T occurs exactly once in Types...), but that only agues that it must be a SFINAE condition — doesn't say that the other cases (any/variant) should not.

map/multimap/unordered_map/unordered_multimap have SFINAE'd versions of emplace that don't take initializer_lists, but they don't have any emplace versions that take ILs.

Suggested resolution:

Add SFINAE requirements to optional::emplace(Args&&... args) and any::emplace(Args&&... args);

[2016-08 Chicago]

During issue prioritization, people suggested that this might apply to any as well.

Ville notes that 2746, 2754 and 2756 all go together.

Proposed resolution:


2751(i). shared_ptr deleter not specified to observe expired weak_ptr instances

Section: 19.11.3.2 [util.smartptr.shared.dest] Status: New Submitter: Aaron Jacobs Opened: 2016-07-21 Last modified: 2017-10-15

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: 19.11.3.2 [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.

See also 2262.

[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.

Proposed resolution:


2762(i). unique_ptr operator*() should be noexcept

Section: 19.11.1.2.4 [unique.ptr.single.observers] Status: Open Submitter: Ville Voutilainen Opened: 2016-08-04 Last modified: 2018-06-24

Priority: 3

View all other issues in [unique.ptr.single.observers].

View all issues with Open status.

Discussion:

See LWG 2337. Since we aren't removing noexcept from shared_ptr's operator*, we should consider adding noexcept to unique_ptr's operator*.

[2016-08 — Chicago]

Thurs PM: P3, and status to 'LEWG'

[2016-08-05 Chicago]

Ville provides an initial proposed wording.

[LEWG Kona 2017]

->Open: Believe these should be noexcept for consistency. We like these. We agree with the proposed resolution. operator->() already has noexcept.

Also adds optional::operator*

Alisdair points out that fancy pointers might intentionally throw from operator*, and we don't want to prohibit that.

Go forward with conditional noexcept(noexcept(*decltype())).

Proposed resolution:

This wording is relative to N4606.

[Drafting note: since this issue is all about consistency, optional's pointer-like operators are additionally included.]

  1. In 19.11.1.2 [unique.ptr.single] synopsis, edit as follows:

    add_lvalue_reference_t<T> operator*() const noexcept;
    
  2. Before 19.11.1.2.4 [unique.ptr.single.observers]/1, edit as follows:

    add_lvalue_reference_t<T> operator*() const noexcept;
    
  3. In 19.6.3 [optional.optional] synopsis, edit as follows:

    constexpr T const *operator->() const noexcept;
    constexpr T *operator->() noexcept;
    constexpr T const &operator*() const & noexcept;
    constexpr T &operator*() & noexcept;
    constexpr T &&operator*() && noexcept;
    constexpr const T &&operator*() const && noexcept;
    
  4. Before [optional.object.observe]/1, edit as follows:

    constexpr T const* operator->() const noexcept;
    constexpr T* operator->() noexcept;
    
  5. Before [optional.object.observe]/5, edit as follows:

    constexpr T const& operator*() const & noexcept;
    constexpr T& operator*() & noexcept;
    
  6. Before [optional.object.observe]/9, edit as follows:

    constexpr T&& operator*() && noexcept;
    constexpr const T&& operator*() const && noexcept;
    

2766(i). Swapping non-swappable types

Section: 19.4.3 [pairs.spec], 19.5.3.10 [tuple.special], 19.6.9 [optional.specalg], 19.7.10 [variant.specalg], 19.11.1.5 [unique.ptr.special], 21.3.7.4 [array.special], 21.6.4.5 [queue.special], 21.6.5.5 [priqueue.special], 21.6.6.5 [stack.special] Status: New Submitter: Agustín K-ballo Bergé Opened: 2016-08-15 Last modified: 2017-02-03

Priority: 3

View all issues with New status.

Discussion:

Related: 2748 swappable traits for optionals, 2749 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 15.5.3.2 [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.

Proposed resolution:

This wording is relative to N4606, except when otherwise noted.

  1. Modify 19.4.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 unless is_swappable_v<T1> is true and is_swappable_v<T2> is true.

  2. Modify 19.5.3.10 [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 unless is_swappable_v<Ti> is true for all i, where 0 <= i and i < sizeof...(Types). The expression inside noexcept is equivalent to:

    noexcept(x.swap(y))
    

    -2- Effects: As if by x.swap(y).

  3. Modify 19.11.1.5 [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 unless is_swappable_v<D> is true.

    -2- Effects: Calls x.swap(y).

  4. Modify 21.3.7.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 unless N == 0 or is_swappable_v<T> is true.

    -2- Effects: As if by x.swap(y).

    […]

  5. Modify 21.6.4.5 [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 unless is_swappable_v<Container> is true.

    -2- Effects: As if by x.swap(y).

  6. Modify 21.6.5.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 shall not participate in overload resolutionbe defined as deleted unless is_swappable_v<Container> is true and is_swappable_v<Compare> is true.

    -2- Effects: As if by x.swap(y).

  7. Modify 21.6.6.5 [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 unless is_swappable_v<Container> is true.

    -2- Effects: As if by x.swap(y).

  8. Modify 19.6.9 [optional.specalg] as indicated:

    This change should be performed if and only if LWG 2748 is accepted and is against the wording of 2748:

    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 unless is_move_constructible_v<T> is true and is_swappable_v<T> is true.

  9. Modify 19.7.10 [variant.specalg] as indicated:

    This change should be performed if and only if LWG 2749 is accepted and is against the wording of 2749:

    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 unless is_move_constructible_v<Ti> && is_swappable_v<Ti> is true for all i. The expression inside noexcept is equivalent to noexcept(v.swap(w)).


2774(i). std::function construction vs assignment

Section: 19.14.16.2.1 [func.wrap.func.con] Status: New Submitter: Barry Revzin Opened: 2016-09-14 Last modified: 2017-02-03

Priority: 3

View other active issues in [func.wrap.func.con].

View all other issues in [func.wrap.func.con].

View all issues with New status.

Discussion:

I think there's a minor defect in the std::function interface. The constructor template is:

template <class F> function(F f);

while the assignment operator template is

template <class F> function& operator=(F&& f);

The latter came about as a result of LWG 1288, but that one was dealing with a specific issue that wouldn't have affected the constructor. I think the constructor should also take f by forwarding reference, this saves a move in the lvalue/xvalue cases and is also just generally more consistent. Should just make sure that it's stored as std::decay_t<F> instead of F.

Is there any reason to favor a by-value constructor over a forwarding-reference constructor?

Proposed resolution:


2808(i). Requirements for fpos and stateT

Section: 28.5.4.2 [fpos.operations] Status: New Submitter: Great Britain Opened: 2016-11-09 Last modified: 2017-02-20

Priority: 4

View other active issues in [fpos.operations].

View all other issues in [fpos.operations].

View all issues with New status.

Discussion:

Addresses GB 60

The requirements on the stateT type used to instantiate class template fpos are not clear, and the following Table 108 — "Position type requirements" is a bit of a mess. This is old wording, and should be cleaned up with better terminology from the Clause 17 Requirements. For example, stateT might be require CopyConstructible?, CopyAssignable?, and Destructible. Several entries in the final column of the table appear to be post-conditions, but without the post markup to clarify they are not assertions or preconditions. They frequently refer to identifiers that do not apply to all entries in their corresponding Expression column, leaving some expressions without a clearly defined semantic. If stateT is a trivial type, is fpos also a trivial type, or is a default constructor not required/supported?

Proposed change:

Clarify the requirements and the table.

[Issues Telecon 16-Dec-2016]

Priority 4; no consensus for any concrete change

Proposed resolution:


2811(i). "Selected constructor" wording is incorrect for optional/variant/any

Section: 19.6.3.1 [optional.ctor], 19.6.3.3 [optional.assign], 19.7.3.1 [variant.ctor], 19.8.3.1 [any.cons], 19.8.3.3 [any.modifiers] Status: New Submitter: Tim Song Opened: 2016-10-29 Last modified: 2017-02-03

Priority: 3

View all other issues in [optional.ctor].

View all issues with New status.

Discussion:

Throughout optional/variant/any's specification references are made to "the selected constructor of T". For example, 19.6.3.1 [optional.ctor]/16 says of the constructor from const T&:

-16- Remarks: If T's selected constructor is a constexpr constructor, this constructor shall be a constexpr constructor.

Similarly, the in-place constructor has this wording (19.6.3.1 [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 a constexpr constructor, this constructor shall be a constexpr constructor.

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.

Proposed resolution:


2813(i). std::function should not return dangling references

Section: 19.14.16.2.1 [func.wrap.func.con] Status: EWG Submitter: Brian Bi Opened: 2016-11-03 Last modified: 2018-08-27

Priority: 2

View other active issues in [func.wrap.func.con].

View all other issues in [func.wrap.func.con].

View all issues with EWG status.

Discussion:

If a std::function has a reference as a return type, and that reference binds to a prvalue returned by the callable that it wraps, then the reference is always dangling. Because any use of such a reference results in undefined behaviour, the std::function should not be allowed to be initialized with such a callable. Instead, the program should be ill-formed.

A minimal example of well-formed code under the current standard that exhibits this issue:

#include <functional>

int main() 
{
  std::function<const int&()> F([]{ return 42; });
  int x = F(); // oops!
}

[2016-11-22, David Krauss comments and suggests wording]

Indirect bindings may also introduce temporaries inside std::function, e.g.:

void f(std::function<long const&()>); // Retains an observer to a long.

void g() {
  int v;
  f([&]()->int& { return v; } ); // int lvalue binds to long const& through a temporary.
}

A fix has been implemented. Conversions that may be conversion operators are allowed, though, because those can produce legitimate glvalues. Before adopting this, it need to be considered considered whether there should be SFINAE or a hard error.

[Issues Telecon 16-Dec-2016]

Priority 2

[2016-07, Toronto Saturday afternoon issues processing]

Billy to work with Brian to rework PR. Status to Open

[2018-08-23 Batavia Issues processing]

Really needs a language change to fix this. Status to EWG.

Proposed resolution:

This wording is relative to N4618.

  1. Add a second paragraph to the remarks section of 19.14.16.2.1 [func.wrap.func.con]:

    template<class F> function(F f);
    

    -7- Requires: F shall be CopyConstructible.

    -8- Remarks: This constructor template shall not participate in overload resolution unless

    • F is Lvalue-Callable (19.14.16.2 [func.wrap.func]) for argument types ArgTypes... and return type R, and

    • If R is type "reference to T" and INVOKE(ArgTypes...) has value category V and type U:

      • V is a prvalue, U is a class type, and T is not reference-related (9.3.3 [dcl.init.ref]) to U, and

      • V is an lvalue or xvalue, and either U is a class type or T is reference-related to U.

    […]


2814(i). [fund.ts.v2] to_array should take rvalue reference as well

Section: 4.2.1 [fund.ts.v2::func.wrap.func.con] Status: LEWG Submitter: Zhihao Yuan Opened: 2016-11-07 Last modified: 2017-06-15

Priority: 3

View all other issues in [fund.ts.v2::func.wrap.func.con].

View all issues with LEWG status.

Discussion:

Addresses: fund.ts.v2

C++ doesn't have a prvalue expression of array type, but rvalue arrays can still come from different kinds of sources:

  1. C99 compound literals (int[]) {2, 4},

  2. std::move(arr),

  3. Deduction to_array<int const>({ 2, 4 }).

    See also CWG 1591: Deducing array bound and element type from initializer list.

For 3), users are "abusing" to_array to get access to uniform initialization to benefit from initializing elements through braced-init-list and/or better narrowing conversion support.

We should just add rvalue reference support to to_array.

[Issues Telecon 16-Dec-2016]

Status to LEWG

[2017-02 in Kona, LEWG responds]

Would like a small paper; see examples before and after. How does this affect overload resolution?

[2017-06-02 Issues Telecon]

Leave status as LEWG; priority 3

Proposed resolution:

This wording is relative to N4600.

  1. Add the following signature to 9.2.1 [fund.ts.v2::header.array.synop]:

    // 9.2.2, Array creation functions
    template <class D = void, class... Types>
      constexpr array<VT, sizeof...(Types)> make_array(Types&&... t);
    template <class T, size_t N>
      constexpr array<remove_cv_t<T>, N> to_array(T (&a)[N]);
    template <class T, size_t N>
      constexpr array<remove_cv_t<T>, N> to_array(T (&&a)[N]);
    
  2. Modify 9.2.2 [fund.ts.v2::container.array.creation] as follows:

    template <class T, size_t N>
      constexpr array<remove_cv_t<T>, N> to_array(T (&a)[N]);
    template <class T, size_t N>
      constexpr array<remove_cv_t<T>, N> to_array(T (&&a)[N]);
    

    -6- Returns: For all i, 0 ≤ i < N, aAn array<remove_cv_t<T>, N> such that each element is copy-initialized with the corresponding element of ainitialized with { a[i]... } for the first form, or { std::move(a[i])... } for the second form..


2815(i). quick_exit can deadlock

Section: 16.5 [support.start.term] Status: New Submitter: Jean-François Bastien Opened: 2016-11-07 Last modified: 2017-05-31

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:

  1. Specify that calling quick_exit while servicing quick_exit is undefined; or
  2. Specifying that calling quick_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 16.5 [support.start.term] p13 (quick_exit()):

Remarks: The function quick_exit() is signal-safe (16.12.3 [csignal.syn]). [Note: It might still be unsafe to call quick_exit() from a handler, because the functions registered with at_quick_exit() might not be signal-safe. — end note]

[Issues Telecon 16-Dec-2016]

Priority 3

Proposed resolution:

This wording is relative to N4606.

  1. Add at new element to the end of 16.5 [support.start.term] p13 (quick_exit()):

    [[noreturn]] void quick_exit(int status) noexcept;
    

    -13- Effects: Functions registered by calls to at_quick_exit are 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 calling quick_exit. If control leaves a registered function called by quick_exit because the function does not provide a handler for a thrown exception, std::terminate() shall be called. [Note: at_quick_exit may 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_exit shall 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 (16.12.3 [csignal.syn]). [Note: It might still be unsafe to call quick_exit() from a handler, because the functions registered with at_quick_exit() might not be signal-safe. — end note]


2818(i). "::std::" everywhere rule needs tweaking

Section: 15.5.1.1 [contents] Status: New Submitter: Tim Song Opened: 2016-11-11 Last modified: 2017-02-03

Priority: 2

View all other issues in [contents].

View all issues with New status.

Discussion:

[contents]/3 says

Whenever a name x defined in the standard library is mentioned, the name x is assumed to be fully qualified as ::std::x, unless explicitly described otherwise. For example, if the Effects section for library function F is described as calling library function G, the function ::std::G is meant.

With the introduction of nested namespaces inside std, this rule needs tweaking. For instance, time_point_cast's Returns clause says "time_point<Clock, ToDuration>(duration_cast<ToDuration>(t.time_since_epoch()))"; that reference to duration_cast obviously means ::std::chrono::duration_cast, not ::std::duration_cast, which doesn't exist.

[Issues Telecon 16-Dec-2016]

Priority 2; Jonathan to provide wording

Proposed resolution:


2819(i). Unspecified Return type: elements

Section: 31.2.5 [thread.req.lockable], 31.4.3 [thread.mutex.requirements] Status: New Submitter: Agustín K-ballo Bergé Opened: 2016-11-12 Last modified: 2017-02-03

Priority: 3

View all issues with New status.

Discussion:

The current draft contains 14 occurrences of a Return type: clause. That clause is not covered by 15.4.1.4 [structure.specifications] p3. This was reported as editorial request #266.

[Issues Telecon 16-Dec-2016]

Priority 3; Jonathan to provide wording.

Proposed resolution:


2820(i). Clarify <cstdint> macros

Section: 16.4 [cstdint] Status: Open Submitter: Thomas Köppe Opened: 2016-11-12 Last modified: 2018-04-23

Priority: 3

View all other issues in [cstdint].

View all issues with Open status.

Discussion:

I would like clarification from LWG regarding the various limit macros like INT_8_MIN in <cstdint>, in pursuit of editorial cleanup of this header's synopsis. I have two questions:

  1. At present, macros like INT_8_MIN that correspond to the optional type int8_t are required (unconditionally), whereas the underlying type to which they appertain is only optional. Is this deliberate? Should the macros also be optional?

  2. Is it deliberate that C++ only specifies sized aliases for the sizes 8, 16, 32 and 64, whereas the corresponding C header allows type aliases and macros for arbitrary sizes for implementations that choose to provide extended integer types? Is the C++ wording more restrictive by accident?

[2017-01-27 Telecon]

Priority 3

[2017-03-04, Kona]

C11 ties the macro names to the existence of the types. Marshall to research the second question.

Close 2764 as a duplicate of this issue.

[2017-03-18, Thomas comments and provides wording]

This is as close as I can get to the C wording without resolving part (a) of the issue (whether we deliberately don't allow sized type aliases for sizes other than 8, 16, 32, 64, a departure from C). Once we resolve part (a), we need to revisit <cinttypes> and fix up the synopsis (perhaps to get rid of N) and add similar wording as the one below to make the formatting macros for the fixed-width types optional. For historical interest, this issue is related to LWG 553 and LWG 841.

[2016-07, Toronto Saturday afternoon issues processing]

Status to Open

Previous resolution: [SUPERSEDED]

This wording is relative to N4640.

  1. Append the following content to 16.4.1 [cstdint.syn] p2:

    -2- The header defines all types and macros the same as the C standard library header <stdint.h>. In particular, for each of the fixed-width types (int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t, uint64_t) the type alias and the corresponding limit macros are defined if and only if the implementation provides the corresponding type.

[2017-10-21, Thomas Köppe provides improved wording]

Previous resolution [SUPERSEDED]:

This wording is relative to N4687.

  1. Change 16.4.1 [cstdint.syn], header <cstdint> synopsis, as indicated:

    […]
    using int64_t = signed integer type; // optional
    using intN_t = see below; // optional, see below
    […]
    using int_fast64_t = signed integer type;
    using int_fastN_t = see below; // optional, see below
    […]
    using int_least64_t = signed integer type;
    using int_leastN_t = see below; // optional, see below
    […]
    using uint64_t = unsigned integer type; // optional
    using uintN_t = see below; // optional, see below
    […]
    using uint_fast64_t = unsigned integer type;
    using uint_fastN_t = see below; // optional, see below
    […]
    using uint_least64_t = unsigned integer type;
    using uint_leastN_t = see below; // optional, see below
    
    using uintmax_t = unsigned integer type;
    using uintptr_t = unsigned integer type; // optional
    
    #define INT_N_MIN  see below;
    #define INT_N_MAX  see below;
    #define UINT_N_MAX  see below;
    
    #define INT_FASTN_MIN  see below;
    #define INT_FASTN_MAX  see below;
    #define UINT_FASTN_MAX  see below;
    
    #define INT_LEASTN_MIN  see below;
    #define INT_LEASTN_MAX  see below;
    #define UINT_LEASTN_MAX  see below;
    
    #define INTMAX_MIN  see below;
    #define INTMAX_MAX  see below;
    #define UINTMAX_MAX  see below;
    
    #define INTPTR_MIN  see below;
    #define INTPTR_MAX  see below;
    #define UINTPTR_MAX  see below;
    
    #define PTRDIFF_MIN  see below;
    #define PTRDIFF_MAX  see below;
    #define SIZE_MAX  see below;
    
    #define SIGATOMIC_MIN  see below;
    #define SIGATOMIC_MAX  see below;
    
    #define WCHAR_MIN  see below;
    #define WCHAR_MAX  see below;
    
    #define WINT_MIN  see below;
    #define WINT_MAX  see below;
    
    #define INTN_C(value)  see below;
    #define UINTN_C(value)  see below;
    #define INTMAX_C(value)  see below;
    #define UINTMAX_C(value)  see below;
    

    -1- The header also defines numerous macros of the form:

    INT_[FAST LEAST]{8 16 32 64}_MIN
    [U]INT_[FAST LEAST]{8 16 32 64}_MAX
    INT{MAX PTR}_MIN
    [U]INT{MAX PTR}_MAX
    {PTRDIFF SIG_ATOMIC WCHAR WINT}{_MAX _MIN}
    SIZE_MAX
    

    plus function macros of the form:

    [U]INT{8 16 32 64 MAX}_C
    

    -2- The header defines all types and macros the same as the C standard library header <stdint.h>. See also: ISO C 7.20

    -?- In particular, all types that use the placeholder N are optional when N is not 8, 16, 32 or 64. The exact-width types intN_t and uintN_t for N = 8, 16, 32, 64 are also optional; however, if an implementation provides integer types with the corresponding width, no padding bits, and (for the signed types) that have a two's complement representation, it defines the corresponding typedef names. Only those macros are defined that correspond to typedef names that the implementation actually provides. [Note: The macros INTN_C and UINTN_C correspond to the typedef names int_leastN_t and uint_leastN_t, respectively. — end note]

  2. Change 28.12.2 [cinttypes.syn] as indicated:

    #define PRIdNN see below
    #define PRIiNN see below
    #define PRIoNN see below
    #define PRIuNN see below
    #define PRIxNN see below
    #define PRIXNN see below
    #define SCNdNN see below
    #define SCNiNN see below
    #define SCNoNN see below
    #define SCNuNN see below
    #define SCNxNN see below
    #define PRIdLEASTNN see below
    #define PRIiLEASTNN see below
    #define PRIoLEASTNN see below
    #define PRIuLEASTNN see below
    #define PRIxLEASTNN see below
    #define PRIXLEASTNN see below
    #define SCNdLEASTNN see below
    #define SCNiLEASTNN see below
    #define SCNoLEASTNN see below
    #define SCNuLEASTNN see below
    #define SCNxLEASTNN see below
    #define PRIdFASTNN see below
    #define PRIiFASTNN see below
    #define PRIoFASTNN see below
    #define PRIuFASTNN see below
    #define PRIxFASTNN see below
    #define PRIXFASTNN see below
    #define SCNdFASTNN see below
    #define SCNiFASTNN see below
    #define SCNoFASTNN see below
    #define SCNuFASTNN see below
    #define SCNxFASTNN see below
    […]
    

    -1- The contents and meaning of the header <cinttypes> […]

    -?- In particular, macros that use the placeholder N are defined if and only if the implementation actually provides the corresponding typedef name in 16.4.1 [cstdint.syn], and moreover, the fscanf macros are provided unless the implementation does not have a suitable fscanf length modifier for the type.

[2018-04-03; Geoffrey Romer suggests improved wording]

Proposed resolution:

This wording is relative to N4727.

  1. Change 16.4.1 [cstdint.syn], header <cstdint> synopsis, as indicated:

    […]
    using int64_t = signed integer type; // optional
    using intN_t = see below; // optional, see below
    […]
    using int_fast64_t = signed integer type;
    using int_fastN_t = see below; // optional, see below
    […]
    using int_least64_t = signed integer type;
    using int_leastN_t = see below; // optional, see below
    […]
    using uint64_t = unsigned integer type; // optional
    using uintN_t = see below; // optional, see below
    […]
    using uint_fast64_t = unsigned integer type;
    using uint_fastN_t = see below; // optional, see below
    […]
    using uint_least64_t = unsigned integer type;
    using uint_leastN_t = see below; // optional, see below
    
    using uintmax_t = unsigned integer type;
    using uintptr_t = unsigned integer type; // optional
    
    #define INT_N_MIN  see below;
    #define INT_N_MAX  see below;
    #define UINT_N_MAX  see below;
    
    #define INT_FASTN_MIN  see below;
    #define INT_FASTN_MAX  see below;
    #define UINT_FASTN_MAX  see below;
    
    #define INT_LEASTN_MIN  see below;
    #define INT_LEASTN_MAX  see below;
    #define UINT_LEASTN_MAX  see below;
    
    #define INTMAX_MIN  see below;
    #define INTMAX_MAX  see below;
    #define UINTMAX_MAX  see below;
    
    #define INTPTR_MIN  see below;
    #define INTPTR_MAX  see below;
    #define UINTPTR_MAX  see below;
    
    #define PTRDIFF_MIN  see below;
    #define PTRDIFF_MAX  see below;
    #define SIZE_MAX  see below;
    
    #define SIGATOMIC_MIN  see below;
    #define SIGATOMIC_MAX  see below;
    
    #define WCHAR_MIN  see below;
    #define WCHAR_MAX  see below;
    
    #define WINT_MIN  see below;
    #define WINT_MAX  see below;
    
    #define INTN_C(value)  see below;
    #define UINTN_C(value)  see below;
    #define INTMAX_C(value)  see below;
    #define UINTMAX_C(value)  see below;
    

    -1- The header also defines numerous macros of the form:

    INT_[FAST LEAST]{8 16 32 64}_MIN
    [U]INT_[FAST LEAST]{8 16 32 64}_MAX
    INT{MAX PTR}_MIN
    [U]INT{MAX PTR}_MAX
    {PTRDIFF SIG_ATOMIC WCHAR WINT}{_MAX _MIN}
    SIZE_MAX
    

    plus function macros of the form:

    [U]INT{8 16 32 64 MAX}_C
    

    -2- The header defines all types and macros the same as the C standard library header <stdint.h>. See also: ISO C 7.20

    -?- In particular, all types that use the placeholder N are optional when N is not 8, 16, 32 or 64. The exact-width types intN_t and uintN_t for N = 8, 16, 32, 64 are also optional; however, if an implementation provides integer types with the corresponding width, no padding bits, and (for the signed types) that have a two's complement representation, it defines the corresponding typedef names. Only those macros are defined that correspond to typedef names that the implementation actually provides. [Note: The macros INTN_C and UINTN_C correspond to the typedef names int_leastN_t and uint_leastN_t, respectively. — end note]

  2. Change 28.12.2 [cinttypes.syn] as indicated:

    #define PRIdNN see below
    #define PRIiNN see below
    #define PRIoNN see below
    #define PRIuNN see below
    #define PRIxNN see below
    #define PRIXNN see below
    #define SCNdNN see below
    #define SCNiNN see below
    #define SCNoNN see below
    #define SCNuNN see below
    #define SCNxNN see below
    #define PRIdLEASTNN see below
    #define PRIiLEASTNN see below
    #define PRIoLEASTNN see below
    #define PRIuLEASTNN see below
    #define PRIxLEASTNN see below
    #define PRIXLEASTNN see below
    #define SCNdLEASTNN see below
    #define SCNiLEASTNN see below
    #define SCNoLEASTNN see below
    #define SCNuLEASTNN see below
    #define SCNxLEASTNN see below
    #define PRIdFASTNN see below
    #define PRIiFASTNN see below
    #define PRIoFASTNN see below
    #define PRIuFASTNN see below
    #define PRIxFASTNN see below
    #define PRIXFASTNN see below
    #define SCNdFASTNN see below
    #define SCNiFASTNN see below
    #define SCNoFASTNN see below
    #define SCNuFASTNN see below
    #define SCNxFASTNN see below
    […]
    

    -1- The contents and meaning of the header <cinttypes> […]

    -?- PRI macros that use the placeholder N are defined if and only if the implementation actually provides the corresponding typedef name in 16.4.1 [cstdint.syn]. SCN macros that use the placeholder N are defined if and only if the implementation actually provides the corresponding typedef name and the implementation has a suitable fscanf length modifier for the type.


2823(i). std::array initialization is still not permissive enough

Section: 21.3.7.1 [array.overview] Status: Open Submitter: Robert Haberlach Opened: 2016-11-16 Last modified: 2018-03-18

Priority: 3

View all other issues in [array.overview].

View all issues with Open status.

Discussion:

LWG 2590'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.3.1 [dcl.init.aggr]) that can be list-initialized with up to N elements whose types are convertible to Tthat can be implicitly converted to T.

[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.

  1. Change 21.3.7.1 [array.overview] p2 as indicated:

    -2- An array is an aggregate (9.3.1 [dcl.init.aggr]) that can be list-initialized with up to N elements whose types are convertiblethat can be implicitly converted to T.


2825(i). LWG 2756 breaks class template argument deduction for optional

Section: 19.6.3 [optional.optional] Status: LEWG Submitter: Richard Smith Opened: 2016-11-24 Last modified: 2017-02-03

Priority: 2

View other active issues in [optional.optional].

View all other issues in [optional.optional].

View all issues with LEWG status.

Discussion:

LWG 2756 applies these changes:

constexpr optional(const T&);
constexpr optional(T&&);
template <class... Args> constexpr explicit optional(in_place_t, Args&&...);
template <class U, class... Args>
  constexpr explicit optional(in_place_t, initializer_list<U>, Args&&...);
template <class U = T> EXPLICIT constexpr optional(U&&);
template <class U> EXPLICIT optional(const optional<U>&);
template <class U> EXPLICIT optional(optional<U>&&);

These break the ability for optional to perform class template argument deduction, as there is now no way to map from optional's argument to the template parameter T.

[2017-01-27 Telecon]

Priority 2

[2017-01-30 Ville comments:]

Seems like the problem is resolved by a simple deduction guide:

template <class T> optional(T) -> optional<T>;

The paper p0433r0 seems to suggest a different guide,

template<class T> optional(T&& t) -> optional<remove_reference_t<T>>;

but I don't think the paper is up to speed with LWG 2756. There's no reason to use such an universal reference in the guide and remove_reference in its target, just guide with T, with the target optional<T>, optional's constructors do the right thing once the type has been deduced.

Proposed resolution:


2827(i). is_trivially_constructible and non-trivial destructors

Section: 19.15.4.3 [meta.unary.prop] Status: New Submitter: Richard Smith Opened: 2016-11-17 Last modified: 2017-02-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:

struct S 
{
  ~S(); // non-trivial
};

static_assert(std::is_trivially_constructible<S>::value, "");

Should the assert pass? Implementations disagree.

Per 19.15.4.3 [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

Proposed resolution:


2829(i). LWG 2740 leaves behind vacuous words

Section: 19.6.3.5 [optional.observe] Status: Open Submitter: Richard Smith Opened: 2016-11-24 Last modified: 2018-06-11

Priority: 2

View all other issues in [optional.observe].

View all issues with Open status.

Discussion:

After applying LWG 2740, we have:

constexpr const T* operator->() const;
constexpr T* operator->();

-1- Requires: *this contains a value.

-2- Returns: val.

-3- Throws: Nothing.

-4- Remarks: These functions shall be constexpr functions.

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, 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.

Proposed resolution:


2832(i). §[fpos.operations] strange requirement for P(i)

Section: 28.5.4.2 [fpos.operations] Status: New Submitter: Jens Maurer Opened: 2016-11-24 Last modified: 2017-02-03

Priority: 3

View other active issues in [fpos.operations].

View all other issues in [fpos.operations].

View all issues with New status.

Discussion:

This is from editorial issue #1031.

The first row in Table 112 "Position type requirements" talks about the expression P(i) and then has an assertion p == P(i). However, there are no constraints on p other than being of type P, so (on the face of it) this seems to require that operator== on P always returns true, which is non-sensical.

[2017-01-27 Telecon]

Priority 3

Proposed resolution:


2833(i). Library needs to specify what it means when it declares a function constexpr

Section: 19.7.3.1 [variant.ctor] Status: Open Submitter: Richard Smith Opened: 2016-11-28 Last modified: 2018-08-27

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 constexpr if and only if the value-initialization of the alternative type T0 would satisfy the requirements for a constexpr function."

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 but is phrased in a more general way.

[2016-12-16, Issues Telecon]

Priority 2; this is also the general case of 2829.

[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.

Proposed resolution:

This wording is relative to N4640.

  1. Modify 15.5.5.6 [constexpr.functions] as indicated:

    17.6.5.6 constexpr functions and constructors [constexpr.functions]

    -1- This International Standard explicitly requires that certain standard library functions are constexpr (9.1.5 [dcl.constexpr]). If the specification for a templated entity requires that it shall be a constexpr templated entity, then that templated entity shall be usable in a constant expression.. An implementation shall notmay declare anyadditional standard library function signature as constexpr except 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.


2839(i). Self-move-assignment of library types, again

Section: 15.5.5.15 [lib.types.movedfrom], 15.5.4.9 [res.on.arguments], 21.2.1 [container.requirements.general] Status: Open Submitter: Tim Song Opened: 2016-12-09 Last modified: 2018-08-27

Priority: 2

View all issues with Open status.

Discussion:

LWG 2468's resolution added to MoveAssignable the requirement to tolerate self-move-assignment, but that does nothing for library types that aren't explicitly specified to meet MoveAssignable other than make those types not meet MoveAssignable any longer.

To realize the intent here, we need to carve out an exception to 15.5.4.9 [res.on.arguments]'s restriction for move assignment operators and specify that self-move-assignment results in valid but unspecified state unless otherwise specified. The proposed wording below adds that to 15.5.5.15 [lib.types.movedfrom] since it seems to fit well with the theme of the current paragraph in that section.

In addition, to address the issue with 21.2.1 [container.requirements.general] noted in LWG 2468's discussion, the requirement tables in that subclause will need to be edited in a way similar to LWG 2468.

[2017-01-27 Telecon]

Priority 2

[2018-1-26 issues processing telecon]

Status to 'Open'; Howard to reword using 'MoveAssignable'.

Previous resolution [SUPERSEDED]:

This wording is relative to N4618.

  1. Add a new paragraph at the end of 15.5.5.15 [lib.types.movedfrom]:

    -1- Objects of types defined in the C++ standard library may be moved from (12.8). Move operations may be explicitly specified or implicitly generated. Unless otherwise specified, such moved-from objects shall be placed in a valid but unspecified state.

    -?- An object of a type defined in the C++ standard library may be move-assigned (10.3.5 [class.copy.assign]) to itself. Such an assignment places the object in a valid but unspecified state unless otherwise specified.

  2. Add a note at the end of 15.5.4.9 [res.on.arguments]/1, bullet 3, 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.1) — […]

    2. (1.2) — […]

    3. (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 argument. [Note: If the parameter is a generic parameter of the form T&& and an lvalue of type A is bound, the argument binds to an lvalue reference (14.8.2.1) and thus is not covered by the previous sentence. — end note] [Note: 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 argument std::move(x)), the program is effectively asking that function to treat that lvalue as a temporary. The implementation is free to optimize away aliasing checks which might be needed if the argument was an lvalue. — end note] [Note: This does not apply to the argument passed to a move assignment operator (15.5.5.15 [lib.types.movedfrom]). — end note]

  3. Edit Table 83 "Container requirements" in 21.2.1 [container.requirements.general] as indicated:

    Table 83 — Container requirements
    Expression Return type Operational
    semantics
    Assertion/note
    pre-/post-condition
    Complexity
    a = rv T& All existing elements of a
    are either move
    assigned to or
    destroyed
    post: If a and rv do not refer to the same object,
    a shall be equal to the value that
    rv had before this assignment
    linear
  4. Edit Table 86 "Allocator-aware container requirements" in 21.2.1 [container.requirements.general] as indicated:

    Table 86 — Allocator-aware container requirements
    Expression Return type Assertion/note
    pre-/post-condition
    Complexity
    a = rv T& Requires: If allocator_traits<allocator_type
    >::propagate_on_container_move_assignment::value

    is false, T is MoveInsertable
    into X and MoveAssignable.
    All existing elements of a are either
    move assigned to or destroyed.
    post: If a and rv do not refer
    to the same object,
    a shall be equal
    to the value that rv had before this assignment
    linear

[2018-08-16, Howard comments and provides updated wording]

I agreed to provide proposed wording for LWG 2839 that was reworded to use MoveAssignable. The advantage of this is that MoveAssignable specifies the self-assignment case, thus we do not need to repeat ourselves.

[2018-08-23 Batavia Issues processing]

Howard and Tim to discuss a revised P/R.

Proposed resolution:

This wording is relative to N4762.

  1. Add a new subsection to 15.5.5 [conforming] after 15.5.5.5 [member.functions]:

    Special members [conforming.special]

    Class types defined by the C++ standard library and specified to be default constructible, move constructible, copy constructible, move assignable, copy assignable, or destructible, shall meet the associated requirements Cpp17DefaultConstructible, Cpp17MoveConstructible, Cpp17CopyConstructible, Cpp17MoveAssignable, Cpp17CopyAssignable, and Cpp17Destructible, respectively (15.5.3.1 [utility.arg.requirements]).


2844(i). Stability of a_uniq.insert(i, j)

Section: 21.2.6 [associative.reqmts], 21.2.7 [unord.req] Status: New Submitter: Matt Austern Opened: 2016-12-14 Last modified: 2017-02-03

Priority: 3

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with New 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.

Proposed resolution:


2845(i). enable_if, result_of, common_type and aligned_storage do not meet the definition of TransformationTrait

Section: 19.15.1 [meta.rqmts] Status: New Submitter: Tim Song Opened: 2016-12-14 Last modified: 2017-02-03

Priority: 3

View all other issues in [meta.rqmts].

View all issues with New status.

Discussion:

[meta.rqmts]/3 defines TransformationTrait as follows:

A TransformationTrait modifies 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 named type, 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:


2846(i). Undefined phrase "effectively cast"

Section: 25.4.9 [cmplx.over], 25.8.1 [cmath.syn] Status: New Submitter: Jens Maurer Opened: 2016-12-15 Last modified: 2017-02-03

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

Proposed resolution:


2847(i). sin(float) should call sinf(float)

Section: 25.8.1 [cmath.syn] Status: New Submitter: Jens Maurer Opened: 2016-12-15 Last modified: 2017-02-03

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:


2848(i). Pass-through threshold for pool allocator

Section: 19.12.5.2 [mem.res.pool.options] Status: New Submitter: Jens Maurer Opened: 2016-12-15 Last modified: 2017-02-03

Priority: 3

View all issues with New status.

Discussion:

19.12.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:


2858(i). LWG 2472: actually an incompatibility with C++03

Section: 22.5.1 [reverse.iterators] Status: New Submitter: Hubert Tong Opened: 2017-01-28 Last modified: 2017-03-13

Priority: 4

View all other issues in [reverse.iterators].

View all issues with New status.

Discussion:

Further to LWG 2472, the case of reverse_iterator comparisons is a regression introduced by LWG 280.

Consider the following program:

#include <utility>
#include <iterator>

using namespace std::rel_ops;

bool f(std::reverse_iterator<int *> it) { return it != it; }

Under C++03, the operator!= in lib.reverse.iterator is more specialized than the operator!= in std::rel_ops.

Following LWG 280, neither operator!= candidate is more specialized than the other. The program is observed to fail with libc++.

Online compiler example, see here.

Suggested resolution:

Reintroduce the homogeneous comparison operators from C++03 alongside the new ones.

[2017-03-04, Kona]

Set priority to 4. STL to write a paper deprecating relops Alisdair to provide an example for Annex C.

Proposed resolution:

This wording is relative to N4618.

  1. Modify 22.5.1 [reverse.iterators], class template reverse_iterator synopsis, as indicated:

    template <class Iterator1, class Iterator2>
      constexpr bool operator==(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator1, class Iterator2>
      constexpr bool operator<(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator1, class Iterator2>
      constexpr bool operator!=(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator1, class Iterator2>
      constexpr bool operator>(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator1, class Iterator2>
      constexpr bool operator>=(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator1, class Iterator2>
      constexpr bool operator<=(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator>
      constexpr bool operator==(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    template <class Iterator>
      constexpr bool operator<(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    template <class Iterator>
      constexpr bool operator!=(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    template <class Iterator>
      constexpr bool operator>(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    template <class Iterator>
      constexpr bool operator>=(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    template <class Iterator>
      constexpr bool operator<=(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    
  2. Modify 99 [reverse.iter.op==] as indicated:

    template <class Iterator1, class Iterator2>
      constexpr bool operator==(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator>
      constexpr bool operator==(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    

    -1- Returns: x.current == y.current.

  3. Modify [reverse.iter.op<] as indicated:

    template <class Iterator1, class Iterator2>
      constexpr bool operator<(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator>
      constexpr bool operator<(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    

    -1- Returns: x.current > y.current.

  4. Modify [reverse.iter.op!=] as indicated:

    template <class Iterator1, class Iterator2>
      constexpr bool operator!=(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator>
      constexpr bool operator!=(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    

    -1- Returns: x.current != y.current.

  5. Modify [reverse.iter.op>] as indicated:

    template <class Iterator1, class Iterator2>
      constexpr bool operator>(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator>
      constexpr bool operator>(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    

    -1- Returns: x.current < y.current.

  6. Modify [reverse.iter.op>=] as indicated:

    template <class Iterator1, class Iterator2>
      constexpr bool operator>=(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator>
      constexpr bool operator>=(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    

    -1- Returns: x.current <= y.current.

  7. Modify [reverse.iter.op<=] as indicated:

    template <class Iterator1, class Iterator2>
      constexpr bool operator<=(
        const reverse_iterator<Iterator1>& x,
        const reverse_iterator<Iterator2>& y);
    template <class Iterator>
      constexpr bool operator<=(
        const reverse_iterator<Iterator>& x,
        const reverse_iterator<Iterator>& y);
    

    -1- Returns: x.current >= y.current.


2859(i). Definition of reachable in [ptr.launder] misses pointer arithmetic from pointer-interconvertible object

Section: 16.6.4 [ptr.launder] Status: Open Submitter: Hubert Tong Opened: 2017-01-31 Last modified: 2018-11-13

Priority: 2

View other active issues in [ptr.launder].

View all other issues in [ptr.launder].

View all issues with Open status.

Discussion:

Given:

struct A { int x, y; };
A a[100];

The bytes which compose a[3] can be reached from &a[2].x: reinterpret_cast<A *>(&a[2].x) + 1 points to a[3], however, the definition of "reachable" in [ptr.launder] does not encompass this case.

[2017-03-04, Kona]

Set priority to 2. Assign this (and 2860) to Core.

[2017-08-14, CWG telecon note]

CWG is fine with the proposed resolution.

Proposed resolution:

This wording is relative to N4618.

  1. Modify 16.6.4 [ptr.launder] as indicated:

    template <class T> constexpr T* launder(T* p) noexcept;
    

    […]

    -3- Remarks: An invocation of this function may be used in a core constant expression whenever the value of its argument may be used in a core constant expression. A byte of storage b is reachable through a pointer value that points to an object Y if there is an object Z, pointer-interconvertible with Y, such that b it is within the storage occupied by ZY, an object that is pointer-interconvertible with Y, or the immediately-enclosing array object if ZY is an array element. The program is ill-formed if T is a function type or (possibly cv-qualified) void.


2860(i). launder and base class subobjects

Section: 16.6.4 [ptr.launder] Status: Open Submitter: Hubert Tong Opened: 2017-01-31 Last modified: 2018-11-25

Priority: 2

View other active issues in [ptr.launder].

View all other issues in [ptr.launder].

View all issues with Open status.

Discussion:

There is an apparent oversight in the wording for launder that allows it to return base class subobjects which differ in their polymorphic behaviour between calls to launder.

This can be fixed by restricting launder from returning pointers to base class subobjects:

  1. always, or

  2. only for polymorphic class types.

[2017-03-04, Kona]

Set priority to 2. This was discussed in EWG (via a paper). Assign this (and 2859) to Core.

[2017-08-14, CWG telecon note]

Core recommends NAD after discussion in EWG.

Hubert Tong summarizes the outcome of that discussion as follows:

launder does not provide positive confirmation of the dynamic type of the object; it is intended that launder can be used to induce a devirtualization barrier even when the static type of the (sub)object to which the returned pointer refers is apparently consistent with prior accesses related to the source pointer.

Proposed resolution:

This wording is relative to N4618.

  1. Option 1:

    1. Modify 16.6.4 [ptr.launder] as indicated:

      template <class T> constexpr T* launder(T* p) noexcept;
      

      -1- Requires: p represents the address A of a byte in memory. An object X that is within its lifetime (6.6.3 [basic.life]) and whose type is similar (7.3.5 [conv.qual]) to T is located at the address A. X shall either be a most derived object, or pointer-interconvertible with a most derived object that is within its lifetime. All bytes of storage that would be reachable through the result are reachable through p (see below).

  2. Option 2:

    1. Modify 16.6.4 [ptr.launder] as indicated:

      template <class T> constexpr T* launder(T* p) noexcept;
      

      -1- Requires: p represents the address A of a byte in memory. An object X that is within its lifetime (6.6.3 [basic.life]) and whose type is similar (7.3.5 [conv.qual]) to T is located at the address A. If T is a polymorphic class type, then X shall be a most derived object. All bytes of storage that would be reachable through the result are reachable through p (see below).


2881(i). Adopt section III of P0308R0

Section: 19.7.3 [variant.variant] Status: New Submitter: Switzerland Opened: 2017-02-03 Last modified: 2017-07-16

Priority: 3

View all other issues in [variant.variant].

View all issues with New status.

Discussion:

Addresses CH 7

Consider 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, Casey to investigate

Proposed resolution:


2883(i). The standard library should provide string_view parameters instead or in addition for functions defined with char const * or string const & as parameter types.

Section: 20.4 [string.view] Status: LEWG Submitter: Switzerland Opened: 2017-02-03 Last modified: 2017-07-16

Priority: Not Prioritized

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.

Proposed resolution:


2884(i). Relational operators for containers should sfinae; if the underlying type is not comparable, neither should the container be

Section: 21 [containers], 19 [utilities] Status: LEWG Submitter: Finland Opened: 2017-02-03 Last modified: 2017-07-16

Priority: Not Prioritized

View other active issues in [containers].

View all other issues in [containers].

View all issues with LEWG status.

Discussion:

Addresses FI 16

Relational operators for containers should sfinae; if the underlying type is not comparable, neither should the container be. Same applies to tuple and pair.

Proposed change: Make the relational operators of containers and utility components reflect the validity of the underlying element types.

[ 2017-06-27 Moved to LEWG after 5 positive votes on c++std-lib. ]

Proposed resolution:


2885(i). The relational operators of optional and variant completely reflect the semantics of the element types — this is inconsistent with other types in the library

Section: 21 [containers], 19 [utilities] Status: LEWG Submitter: Finland Opened: 2017-02-03 Last modified: 2017-07-16

Priority: Not Prioritized

View other active issues in [containers].

View all other issues in [containers].

View all issues with LEWG status.

Discussion:

Addresses FI 17

The relational operators of optional and variant completely reflect the semantics of the element types; this is inconsistent with other types in the library, like pair, tuple and containers. If we believe it's important that we don't synthesize relational operators for wrapper types, we should believe it's important for other types as well. Otherwise comparing containers of floating-point types and tuples/pairs etc. of floating point types will give incorrect answers.

Proposed change: Make the relational operators of containers and utility components reflect the semantics of the operators for the underlying element types.

[2017-07 Toronto Thurs Issue Prioritization]

Move to LEWG

Proposed resolution:


2894(i). The function template std::apply() is required to be constexpr, but std::invoke() isn't

Section: 19.14.4 [func.invoke] Status: Open Submitter: Great Britain Opened: 2017-02-03 Last modified: 2017-07-17

Priority: 3

View other active issues in [func.invoke].

View all other issues in [func.invoke].

View all issues with Open status.

Discussion:

Addresses GB 51

The function template std::apply() in 19.5.3.5 [tuple.apply] is required to be constexpr, but std::invoke() in 19.14.4 [func.invoke] isn't. The most sensible implementation of apply_impl() is exactly equivalent to std::invoke(), so this requires implementations to have a constexpr version of invoke() for internal use, and the public API std::invoke, which must not be constexpr even though it is probably implemented in terms of the internal version.

Proposed change: Add constexpr to std::invoke.

[2017-02-20, Marshall adds wording]

[Kona 2017-03-01]

We think this needs CWG 1581 to work; accepted as Immediate to resolve NB comment.

Friday: CWG 1581 was not moved in Kona. Status back to Open.

[2017-07 Toronto Tuesday PM issue prioritization]

Priority 3

Proposed resolution:

This wording is relative to N4640.

  1. Modify 19.14.4 [func.invoke] as indicated:

    template <class F, class... Args>
         constexpr result_of_t<F&&(Args&&...)> invoke(F&& f, Args&&... args);
    

2899(i). is_(nothrow_)move_constructible and tuple, optional and unique_ptr

Section: 19.5 [tuple], 19.6 [optional], 19.11.1.2.1 [unique.ptr.single.ctor] Status: Open Submitter: United States Opened: 2017-02-03 Last modified: 2017-07-16

Priority: 2

View all other issues in [tuple].

View all issues with Open status.

Discussion:

Addresses US 110

The move constructors for tuple, optional, and unique_ptr should return false for is_(nothrow_)move_constructible_v<TYPE> when their corresponding Requires clauses are not satisfied, as there are now several library clauses that are defined in terms of these traits. The same concern applies to the move-assignment operator. Note that pair and variant already satisfy this constraint.

[2017-02-26, Scott Schurr provides wording]

[ 2017-06-27 P2 after 5 positive votes on c++std-lib. ]

[2016-07, Toronto Thursday night issues processing]

The description doesn't match the resolution; Alisdair to investigate. Status to Open

Proposed resolution:

This wording is relative to N4640.

  1. Modify 19.5.3 [tuple.tuple] as indicated:

    // 20.5.3.1, tuple construction
    EXPLICIT constexpr tuple();
    EXPLICIT constexpr tuple(const Types&...); // only if sizeof...(Types) >= 1
    template <class... UTypes>
    EXPLICIT constexpr tuple(UTypes&&...) noexcept(see below); // only if sizeof...(Types) >= 1
    
    tuple(const tuple&) = default;
    tuple(tuple&&) = default;
    
    template <class... UTypes>
    EXPLICIT constexpr tuple(const tuple<UTypes...>&);
    template <class... UTypes>
    EXPLICIT constexpr tuple(tuple<UTypes...>&&) noexcept(see below);
    
    template <class U1, class U2>
    EXPLICIT constexpr tuple(const pair<U1, U2>&); // only if sizeof...(Types) == 2
    template <class U1, class U2>
    EXPLICIT constexpr tuple(pair<U1, U2>&&) noexcept(see below); // only if sizeof...(Types) == 2
    
    […]
    
    // 20.5.3.2, tuple assignment
    tuple& operator=(const tuple&);
    tuple& operator=(tuple&&) noexcept(see below);
    
    template <class... UTypes>
    tuple& operator=(const tuple<UTypes...>&);
    template <class... UTypes>
    tuple& operator=(tuple<UTypes...>&&) noexcept(see below);
    
    template <class U1, class U2>
    tuple& operator=(const pair<U1, U2>&); // only if sizeof...(Types) == 2
    template <class U1, class U2>
    tuple& operator=(pair<U1, U2>&&) noexcept(see below); // only if sizeof...(Types) == 2
    
  2. Modify 19.5.3.1 [tuple.cnstr] as indicated:

    template <class... UTypes> EXPLICIT constexpr tuple(UTypes&&... u) noexcept(see below);
    

    -8- Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u).

    -9- Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) == sizeof...(UTypes) and sizeof...(Types) >= 1 and is_constructible_v<Ti, Ui&&> is true for all i. The constructor is explicit if and only if is_convertible_v<Ui&&, Ti> is false for at least one i. The expression inside noexcept is equivalent to the logical AND of the following expressions:

    is_nothrow_constructible_v<Ti, Ui&&>
    

    where Ti is the ith type in Types, and Ui is the ith type in UTypes.

    […]

    template <class... UTypes> EXPLICIT constexpr tuple(tuple<UTypes...>&& u) noexcept(see below);
    

    -16- Effects: For all i, initializes the ith element of *this with std::forward<Ui>(get<i>(u)).

    -17- Remarks: This constructor shall not participate in overload resolution unless

    […]

    The constructor is explicit if and only if is_convertible_v<Ui&&, Ti> is false for at least one i. The expression inside noexcept is equivalent to the logical AND of the following expressions:

    is_nothrow_constructible_v<Ti, Ui&&>
    

    where Ti is the ith type in Types, and Ui is the ith type in UTypes.

    […]

    template <class U1, class U2> EXPLICIT constexpr tuple(pair<U1, U2>&& u) noexcept(see below);
    

    -21- Effects: Initializes the first element with std::forward<U1>(u.first) and the second element with std::forward<U2>(u.second).

    -22- Remarks: This constructor shall not participate in overload resolution unless sizeof...(Types) == 2, is_constructible_v<T0, U1&&> is true and is_constructible_v<T1, U2&&> is true.

    -23- The constructor is explicit if and only if is_convertible_v<U1&&, T0> is false or is_convertible_v<U2&&, T1> is false. The expression inside noexcept is equivalent to:

    is_nothrow_constructible_v<T0, U1&&> && is_nothrow_constructible_v<T1, U2&&>
    
  3. Modify 19.5.3.2 [tuple.assign] as indicated:

    template <class... UTypes> tuple& operator=(tuple<UTypes...>&& u) noexcept(see below);
    

    -12- Effects: For all i, assigns std::forward<Ui>(get<i>(u)) to get<i>(*this).

    -13- Remarks: This operator shall not participate in overload resolution unless is_assignable_v<Ti&, Ui&&> == true for all i and sizeof...(Types) == sizeof...(UTypes). The expression inside noexcept is equivalent to the logical AND of the following expressions:

    is_nothrow_assignable_v<Ti&, Ui&&>
    

    where Ti is the ith type in Types, and Ui is the ith type in UTypes.

    -14- Returns: *this.

    […]

    template <class U1, class U2> tuple& operator=(pair<U1, U2>&& u) noexcept(see below);
    

    -18- Effects: Assigns std::forward<U1>(u.first) to the first element of *this and std::forward<U2>(u.second) to the second element of *this.

    -19- Remarks: This operator shall not participate in overload resolution unless sizeof...(Types) == 2 and is_assignable_v<T0&, U1&&> is true for the first type T0 in Types and is_assignable_v<T1&, U2&&> is true for the second type T1 in Types. The expression inside noexcept is equivalent to:

    is_nothrow_assignable_v<T0&, U1&&> && is_nothrow_assignable_v<T1&, U2&&>
    

    -20- Returns: *this.


2906(i). There is no ability to supply an allocator for the control block when constructing a shared_ptr from a unique_ptr

Section: 19.11.3.1 [util.smartptr.shared.const] Status: New Submitter: United States Opened: 2017-02-03 Last modified: 2017-07-16

Priority: 3

View all other issues in [util.smartptr.shared.const].

View all issues with New status.

Discussion:

Addresses US 130

There 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:


2922(i). The *_constant<> templates do not make use of template<auto>

Section: 19.15.2 [meta.type.synop] Status: LEWG Submitter: United States Opened: 2017-02-03 Last modified: 2017-07-16

Priority: Not Prioritized

View other active issues in [meta.type.synop].

View all other issues in [meta.type.synop].

View all issues with LEWG status.

Discussion:

Addresses US 171

The *_constant<> templates (including the proposed addition, bool_constant<>) do not make use of the new template<auto> feature.

Proposed change: Add a constant<> (subject to bikeshedding) template which uses template<auto>.

Define integral_constant<> as using integral_constant<T, V> = constant<T(V)> or integral_constant<T, V> = constant<V>.

Either remove bool_constant, define it as using bool_constant = constant<bool(B)> or using bool_constant = constant<B>.

[2017-03-03, Kona, LEWG]

Straw polls:

Name:
constant3
numeric_constant8
static_constant1
scalar_constant7
integer_constant (Over LWG's dead body)1
auto_constant4
integral_c7
int_0
scalar_constant6
numeric_constant3
integral_c5

Accept P0377 with "scalar_constant" for C++17 to address LWG 2922 and US 171:

SF | F | N | A | SA

0 | 1 | 3 | 7 | 5

[2017-07 Toronto Thurs Issue Prioritization]

Status LEWG with P0377

Proposed resolution:


2923(i). noexcept is inconsistently applied across headers which import components of the C standard library

Section: 25.8.1 [cmath.syn] Status: New Submitter: United States Opened: 2017-02-03 Last modified: 2017-07-16

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 172

noexcept 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:


2931(i). Missed optimization opportunity with single-argument std::next

Section: 22.4.2 [iterator.operations] Status: Open Submitter: Morwenn Opened: 2017-02-04 Last modified: 2017-03-13

Priority: 3

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.

Proposed resolution:


2933(i). PR for LWG 2773 could be clearer

Section: 19.5.3.4 [tuple.creation] Status: New Submitter: Eric Fiselier Opened: 2017-02-06 Last modified: 2017-06-01

Priority: 3

View all other issues in [tuple.creation].

View all issues with New status.

Discussion:

The current PR for LWG 2773 changes std::ignore to be a constexpr variable. However it says nothing about whether using std::ignore in std::tie is a constant expression. I think the intent was clearly to allow this. Therefore I suggest we update the resolution to explicitly call this out in a note. (I don't think new normative wording is needed).

I propose we update the current PR as follows:

  1. Keep the current changes proposed by the PR.

  2. Add a note after [tuple.creation]/p7 (std::tie):

    [Note: The constructors and assignment operators provided by ignore shall be constexpr]

Perhaps LWG feels the existing wording is clear enough, but if not I think the above changes sufficiently clarify it.

The ability to constexpr assign to std::ignore can be important: Here is an extremely contrived example:

constexpr bool foo() {
  auto res = std::tie(std::ignore);
  std::get<0>(res) =42; 
  return true;
}
static_assert(foo());

[2017-03-04, Kona]

Set priority to 3. P/R is incorrect; it should NOT be a note. Marshall to work with Eric to get better wording. STL says "use an exposition-only class".

Proposed resolution:

This wording is relative to N4640.

  1. Modify 19.5.3.4 [tuple.creation] as indicated:

    template<class... TTypes>
      constexpr tuple<TTypes&...> tie(TTypes&... t) noexcept;
    

    -7- Returns: […]

    -?- [Note: The constructors and assignment operators provided by ignore shall be constexpr. — end note]

    -8- [Example: […] — end example]


2939(i). Some type-completeness constraints of traits are overspecified

Section: 19.15.2 [meta.type.synop] Status: New Submitter: Daniel Krügler Opened: 2017-03-02 Last modified: 2018-08-27

Priority: 2

View other active issues in [meta.type.synop].

View all other issues in [meta.type.synop].

View all issues with New status.

Discussion:

LWG 2797 (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, 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, but really needs an audit of the type traits.

[2018-08 Batavia Monday issue discussion]

Issues 2797, 2939, 3022, and 3099 are all closely related. Walter to write a paper resolving them.

Proposed resolution:


2947(i). Clarify several filesystem terms

Section: 28.11.9.1 [fs.enum.path.format], 28.11.12 [fs.class.directory_iterator], 28.11.14.3 [fs.op.copy] Status: New Submitter: Thomas Köppe Opened: 2017-03-14 Last modified: 2018-01-22

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:

[2017-07 Toronto Wed Issue Prioritization]

Priority 3

[2018-01-16, Jonathan comments]

In 28.11.9.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 28.11.12 [fs.class.directory_iterator] an "implementation-defined directory-like file type" is an implementation-defined file type (see 28.11.9.2 [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 28.11.7.1 [fs.path.generic]).

In 28.11.14.3 [fs.op.copy], an "implementation-defined file type" is exactly that, see 28.11.9.2 [fs.enum.file_type] and Table 115 again. I don't see what isn't clear about that. Maybe add a cross-reference to 28.11.9.2 [fs.enum.file_type].

Proposed resolution:


2949(i). Unclear complexity requirements: space vs. time

Section: 15 [library] Status: New Submitter: Jens Maurer Opened: 2017-03-20 Last modified: 2017-07-16

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."

15.4.1.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:

etc.

[2017-07 Toronto Wed Issue Prioritization]

Priority 4; Robert to look at

Proposed resolution:


2951(i). iterator_traits should SFINAE for void* and function pointers

Section: 22.3.2.3 [iterator.traits] Status: New Submitter: Billy Robert O'Neal III Opened: 2017-03-24 Last modified: 2017-07-16

Priority: 3

View all other issues in [iterator.traits].

View all issues with New status.

Discussion:

A customer recently triggered an unexpected SFINAE condition with class path. We constrain the constructor that takes const Source& by asking if iterator_traits<Source>::value_type is one that's OK. This forms iterator_traits<void*>, which is a hard error, as it tries to form void&.

Pointers-to-non-object-type can never act as iterators, as they don't support pointer arithmetic (as we recently discovered in atomic<void*> / atomic<int(*)(int)>).

[2017-07 Toronto Wed Issue Prioritization]

Priority 3; Billy to look into arrays of unknown bounds

Proposed resolution:

This wording is relative to N4659.

  1. Change 22.3.2.3 [iterator.traits] as indicated:

    -3- It is specialized for pointers as

    namespace std {
      template<class T> struct iterator_traits<T*> {
        using difference_type = ptrdiff_t;
        using value_type = T;
        using pointer = T*;
        using reference = T&;
        using iterator_category = random_access_iterator_tag;
      };
    }
    

    and for pointers to const as

    namespace std {
      template<class T> struct iterator_traits<const T*> {
        using difference_type = ptrdiff_t;
        using value_type = T;
        using pointer = const T*;
        using reference = const T&;
        using iterator_category = random_access_iterator_tag;
      };
    }
    

    -?- These partial specializations for pointers apply only when T is an object type.


2957(i). bind's specification doesn't apply the cv-qualification of the call wrapper to the callable object

Section: 19.14.14.3 [func.bind.bind] Status: New Submitter: Tim Song Opened: 2017-05-04 Last modified: 2017-07-16

Priority: 3

View all other issues in [func.bind.bind].

View all issues with New status.

Discussion:

According to 19.14.14.3 [func.bind.bind]/1.2,

fd is an lvalue of type FD constructed from std::forward<F>(f),

and then uses fd throughout the specification, seemingly without regard to the cv-qualification of the call wrapper g. But this definition means that fd is always cv-unqualified, rather than having its cv-qualification change with that of g as intended.

LWG 2545 accidentally exacerbated the problem by removing any hint that fd's cv-qualifier followed that of the call wrapper.

A similar issue affects the type of tdi for nested binds.

[2017-07 Toronto Wed Issue Prioritization]

Priority 3

Proposed resolution:

This wording is relative to N4659.

  1. Edit 19.14.14.3 [func.bind.bind] as indicated:

    template<class F, class... BoundArgs>
      unspecified bind(F&& f, BoundArgs&&... bound_args);
    

    […]

    -3- Returns: A forwarding call wrapper g (19.14.3 [func.require]). The effect of g(u1, u2, ..., uM) shall be

    INVOKE(static_cast<FD cv &>(fd), std::forward<V1>(v1), 
      std::forward<V2>(v2), ..., std::forward<VN>(vN))
    

    where cv represents the cv-qualifiers of g and the values and types of the bound arguments v1, v2, . . . , vN are determined as specified below. The copy constructor and move constructor of the forwarding call wrapper shall throw an exception if and only if the corresponding constructor of FD or of any of the types TDi throws an exception.

    […]

    template<class R, class F, class... BoundArgs>
      unspecified bind(F&& f, BoundArgs&&... bound_args);
    

    […]

    -7- Returns: A forwarding call wrapper g (19.14.3 [func.require]). The effect of g(u1, u2, ..., uM) shall be

    INVOKE<R>(static_cast<FD cv &>(fd), std::forward<V1>(v1), 
      std::forward<V2>(v2), ..., std::forward<VN>(vN))
    

    where cv represents the cv-qualifiers of g and the values and types of the bound arguments v1, v2, . . . , vN are determined as specified below. The copy constructor and move constructor of the forwarding call wrapper shall throw an exception if and only if the corresponding constructor of FD or of any of the types TDi throws an exception.

    […]

    -10- The values of the bound arguments v1, v2, ... , vN and their corresponding types V1, V2, ... , VN depend on the types TDi derived from the call to bind and the cv-qualifiers cv of the call wrapper g as follows:

    1. (10.1) — […]

    2. (10.2) — if the value of is_bind_expression_v<TDi> is true, the argument is static_cast<TDi cv &>(tdi)(std::forward<Uj>(uj)...) and its type Vi is invoke_result_t<TDi cv &, Uj...>&&;

    3. (10.3) — […]


2959(i). char_traits<char16_t>::eof is a valid UTF-16 code unit

Section: 20.2.3.3 [char.traits.specializations.char16_t] Status: New Submitter: Jonathan Wakely Opened: 2017-05-05 Last modified: 2017-07-16

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.

20.2.3.3 [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, 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:


2962(i). Iterators of Containers of move-only types do not model InputIterator

Section: 22.3.5.2 [input.iterators] Status: Open Submitter: Gašper Ažman Opened: 2017-05-10 Last modified: 2018-11-11

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 22.3.5.2 [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 and LWG 484, 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).

reference needs to be related to value_type

Algorithms need this for the following reasons:

We must give due consideration to code that so far required its inputs to be CopyConstructible implicitly by requiring convertibility to T. This is done in the issue LWG 2963, 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 than T const& convertibility without separately requiring convertibility to T.

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 InputIterator concept requirement on reference being convertible to value_type gets 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 implicit CopyConstructible assertion 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 = *first without 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.

  1. Change Table 95 — "Input iterator requirements", 22.3.5.2 [input.iterators] as indicated:

    Table 107 — Input iterator requirements (in addition to Iterator)
    Expression Return type Operational
    semantics
    Assertion/note pre-/post-condition
    *a reference,
    convertible to T
    that binds to const T&
    […]
    *r++ convertible to T
    that binds to const 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

Proposed resolution:

This wording is relative to N4741.

  1. Change Table 89 — "Input iterator requirements", 22.3.5.2 [input.iterators] as indicated:

    Table 89 — Input iterator requirements (in addition to Iterator)
    Expression Return type Operational
    semantics
    Assertion/note pre-/post-condition
    *a reference,
    convertible to T
    that binds to const T&
    […]
    *r++ convertible to T
    that binds to const T&
    When T tmp = *r is well-formed and
    T is MoveConstructible, then
    { T tmp = *r;
    ++r;
    return tmp; }

2963(i). Algorithms with underspecified iterator requirements

Section: 24 [algorithms], 25 [numerics] Status: New Submitter: Gašper Ažman Opened: 2017-05-10 Last modified: 2017-07-16

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 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:

  1. copy, copy_if, copy_n, copy_backward

    Add to description: *result shall be assignable from *first.

  2. move, move_backward

    Add to description: *result shall be move-assignable from *first.

  3. transform

    Add to description: The result of the expression op(*first) or binary_op(*first1, *first2) shall be writable to result.

  4. rotate_copy

    Add to description: *first shall be writable to result.

  5. merge

    Add to description: *first1 and *first2 shall be writable to result..

  6. set_union, set_intersection, set_difference, set_symmetric_difference

    Add to description: *first1 and *first2 shall be writable to result.

  7. partial_sum

    acc is not defined.

    Change description: acc, a variable of InputIterator's value type, shall be constructible

  8. 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.

  9. 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:


2973(i). inplace_merge exact comparison count complexity prohibits useful real-world optimizations

Section: 24.7.5 [alg.merge] Status: LEWG Submitter: Billy Robert O'Neal III Opened: 2017-06-08 Last modified: 2017-07-16

Priority: Not Prioritized

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

Proposed resolution:

This wording is relative to N4659.

  1. Edit 24.7.5 [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:

    1. (8.1) — For the overloads with no ExecutionPolicy, if enough additional memory is available, exactly N - 1 comparisons on average, 𝒪(N) comparisons in the worst case.

    2. (8.2) — For the overloads with no ExecutionPolicy if no additional memory is available, 𝒪(N log N) comparisons.

    3. (8.3) — For the overloads with an ExecutionPolicy, 𝒪(N log N) comparisons.

    -9- Remarks: Stable (15.5.5.7 [algorithm.stable]).


2983(i). money_put::do_put underspecified

Section: 27.4.6.2.2 [locale.money.put.virtuals] Status: New Submitter: Jonathan Wakely Opened: 2017-06-21 Last modified: 2017-07-16

Priority: 3

View all other issues in [locale.money.put.virtuals].

View all issues with New status.

Discussion:

Whether you get ".99" or "0.99" for the following depends on the implementation:

std::cout.imbue(std::locale("en_US"));
std::cout << std::put_money(99.L);

I don't see any justification in [locale.money.put.virtuals] for the leading 0, although that seems more useful.

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.

Proposed resolution:

This wording is relative to N4659.

  1. Edit 27.4.6.2.2 [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 by str.width() on entry to the function, then copies of fill are inserted as necessary to pad to the specified width. For the value af equal to (str.flags() & str.adjustfield), if (af == str.internal) is true, the fill characters are placed where none or space appears in the formatting pattern; otherwise if (af == str.left) is true, they are placed after the other characters; otherwise, they are placed before the other characters.


2984(i). put_money(99) is unnecessarily undefined

Section: 28.7.7 [ext.manip] Status: New Submitter: Jonathan Wakely Opened: 2017-06-22 Last modified: 2017-07-16

Priority: 3

View all other issues in [ext.manip].

View all issues with New status.

Discussion:

[ext.manip] p5 says:

Requires: The type moneyT shall be either long double or a specialization of the basic_string template (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.

  1. Edit 28.7.7 [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, let Mo, Ch, and Tr be the deduced template argument types of the template parameters moneyT, charT, and traits, respectively, of the instantiated specialization of the template f.

    -2- Requires: The type moneyT shall be either long double or a specialization of the basic_string template (Clause 20 [strings])Mo shall be either convertible to long double or shall be convertible to money_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 (28.7.4.2.1 [istream.formatted.reqmts]).

    -4- Returns: An object of unspecified type such that if in is an object of type basic_istream<charTCh, traitsTr> then the expression in >> get_money(mon, intl) behaves as if it called f(in, mon, intl), where the function f is 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 type basic_istream<charTCh, traitsTr>& and value in.

    template <class moneyT> unspecified put_money(const moneyT& mon, bool intl = false);
    

    -?- For an expression out << put_money(mon, intl) described below, let Mo, Ch, and Tr be the deduced template argument types of the template parameters moneyT, charT, and traits, respectively, of the instantiated specialization of the template f.

    -5- Requires: The type moneyT shall be either long double or a specialization of the basic_string template (Clause 20 [strings])Mo shall be either convertible to long double or shall be convertible to money_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 out is an object of type basic_ostream<charTCh, traitsTr> then the expression out << put_money(mon, intl) behaves as a formatted output function (28.7.5.2.1 [ostream.formatted.reqmts]) that calls f(out, mon, intl), where the function f is 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 type basic_ostream<charTCh, traitsTr>& and value out.


2985(i). std::reverse should be permitted to be vectorized

Section: 24.6.10 [alg.reverse] Status: LEWG Submitter: Billy O'Neal III Opened: 2017-06-24 Last modified: 2018-04-23

Priority: Not Prioritized

View all other issues in [alg.reverse].

View all issues with LEWG status.

Discussion:

The fine folks on our backend team suggested that we special case std::reverse of 1/2/4/8 to take advantage of vector units. Unfortunately, at present std::reverse says it does N/2 iter_swaps, which doesn't permit our vector implementation even if the iterator inputs are pointers to trivially copyable Ts.

The vectorized version for pointers to shorts is ~8x faster on Skylake than the serial version, and about 7x faster for unsigned long longs; and users don't actually care whether or not we call swap here.

[2017-07 Toronto Monday issue prioritization]

Status to LEWG; this is similar to 2973

[2018-04-02, Billy comments]

This issue should be resolved by P0551, because it prohibits user specialization of std::swap and std::iter_swap, which means the proposed vectorization optimization for pointers-to-trivially-copyable is now implementable without changes to reverse's specification (We can detect if the user has provided an alternate swap in their own namespace, but not if they explicitly specialized swap or iter_swap).

Proposed resolution:

This wording is relative to N4659.

  1. Edit 24.6.10 [alg.reverse] as indicated:

    template<class BidirectionalIterator>
      void reverse(BidirectionalIterator first, BidirectionalIterator last);
    template<class ExecutionPolicy, class BidirectionalIterator>
      void reverse(ExecutionPolicy&& exec,
                   BidirectionalIterator first, BidirectionalIterator last);
    

    -1- Requires: *first shall be swappable (15.5.3.2 [swappable.requirements]).

    -2- Effects: For each non-negative integer i < (last - first) / 2, applies iter_swap to all pairs of iterators first + i, (last - i) - 1. If is_trivially_copyable_v<typename iterator_traits<BidirectionalIterator>::value_type> is true, an implementation may permute the elements by making temporary copies, rather than by calling iter_swap. [Note: this allows the implementation to be vectorized. — end note]


2986(i). Handling of multi-character collating elements by the regex FSM is underspecified

Section: 29.13 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2017-06-25 Last modified: 2017-07-16

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, ClassAtomCollatingElement and ClassAtomEquivalence provide 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_type flags 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:

The ECMA-262 specification for ClassRanges also deals in characters.

[2017-07 Toronto Monday issue prioritization]

Priority 4

Proposed resolution:


2987(i). Relationship between traits_inst.lookup_collatename and the regex FSM is underspecified with regards to ClassAtomCollatingElement

Section: 29.13 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2017-06-25 Last modified: 2017-07-16

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:

  1. a test if the returned string is empty ([re.grammar]/8), and

  2. 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:


2990(i). optional::value_type is not always a value type

Section: 19.6.3 [optional.optional] Status: Open Submitter: Casey Carter Opened: 2017-06-27 Last modified: 2018-01-28

Priority: 3

View other active issues in [optional.optional].

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.

  1. Edit 19.6.3 [optional.optional], class template optional synopsis, as indicated:

    template <class T>
      class optional {
      public:
        using value_type = remove_cv_t<T>;
        […]
      };
    

2991(i). variant copy constructor missing noexcept(see below)

Section: 19.7.3.1 [variant.ctor] Status: LEWG Submitter: Peter Dimov Opened: 2017-06-27 Last modified: 2017-07-16

Priority: Not Prioritized

View other active issues in [variant.ctor].

View all other issues in [variant.ctor].

View all issues with LEWG status.

Discussion:

The copy constructor of std::variant is not conditionally noexcept (I think it was in the original proposal.)

It should be, for two reasons: first, this would be consistent with the other three constructors

constexpr variant() noexcept(see below);

variant(variant&&) noexcept(see below);

template <class T>
constexpr variant(T&&) noexcept(see below);

and second, variant itself makes use of is_nothrow_copy_constructible, so it's inconsistent for it to take a stance against it.

[2017-07 Toronto Tuesday PM issue prioritization]

Status to LEWG

Proposed resolution:

This wording is relative to N4659.

  1. Edit 19.7.3 [variant.variant], class template variant synopsis, as indicated:

    template <class... Types>
      class variant {
      public:
        // 23.7.3.1, constructors
        constexpr variant() noexcept(see below);
        variant(const variant&) noexcept(see below);
        variant(variant&&) noexcept(see below);
        […]
      };
    
  2. Edit 19.7.3.1 [variant.ctor] as indicated:

    variant(const variant& w) noexcept(see below);
    

    […]

    -8- Remarks: This function shall not participate in overload resolution unless is_copy_constructible_v<Ti> is true for all i. The expression inside noexcept is equivalent to the logical AND of is_nothrow_copy_constructible_v<Ti> for all i.


2994(i). Needless UB for basic_string and basic_string_view

Section: 20.2 [char.traits], 20.3.2.1 [string.require], 20.4.2 [string.view.template] Status: Open Submitter: Gennaro Prota Opened: 2017-07-03 Last modified: 2018-11-25

Priority: 3

View all other issues in [char.traits].

View all issues with Open status.

Discussion:

basic_string and basic_string_view involve undefined behavior in a few cases where it's simple for the implementation to add a static_assert and make the program ill-formed.

With regards to basic_string, 20.2 [char.traits]/3 states:

Traits::char_type shall be the same as CharT.

Here, the implementation can add a static_assert using the is_same type trait. Similar issues exist in 20.3.2.1 [string.require] and, for basic_string_view, in 20.4.2 [string.view.template]/1.

[2017-07 Toronto Tuesday PM issue prioritization]

Priority 3; need to check general container requirements

Partially by the adoption of P1148 in San Diego.

Tim opines: "the remainder deals with allocator value type mismatch, which I think is NAD."

Proposed resolution:

This wording is relative to N4659.

  1. Edit 20.2 [char.traits] as indicated:

    -3- To specialize those templates to generate a string or iostream class to handle a particular character container type CharT, that and its related character traits class Traits are passed as a pair of parameters to the string or iostream template as parameters charT and traits. If Traits::char_type shall be the same is not the same type as CharT, the program is ill-formed.

  2. Edit 20.3.2.1 [string.require] as indicated:

    -3- In every specialization basic_string<charT, traits, Allocator>, if the type allocator_traits<Allocator>::value_type shall name the same type is not the same type as charT, the program is ill-formed. Every object of type basic_string<charT, traits, Allocator> shall use an object of type Allocator to allocate and free storage for the contained charT objects as needed. The Allocator object used shall be obtained as described in 21.2.1 [container.requirements.general]. In every specialization basic_string<charT, traits, Allocator>, the type traits shall satisfy the character traits requirements (20.2 [char.traits]). If, and the type traits::char_type shall name the same type is not the same type as charT, the program is ill-formed.

  3. Edit 20.4.2 [string.view.template] as indicated:

    -1- In every specialization basic_string_view<charT, traits>, the type traits shall satisfy the character traits requirements (20.2 [char.traits]). If, and the type traits::char_type shall name the same type is not the same type as charT, the program is ill-formed.


2997(i). LWG 491 and the specification of {forward_,}list::unique

Section: 21.3.10.5 [list.ops], 21.3.9.6 [forwardlist.ops] Status: New Submitter: Tim Song Opened: 2017-07-07 Last modified: 2017-07-16

Priority: 3

View other active issues in [list.ops].

View all other issues in [list.ops].

View all issues with New status.

Discussion:

There are various problems with the specification of list::unique and its forward_list counterpart, some of which are obvious even on cursory inspection:

LWG 491, which pointed out many of those problems with the specification of list::unique, was closed as NAD with the rationale that

"All implementations known to the author of this Defect Report comply with these assumption", and "no impact on current code is expected", i.e. there is no evidence of real-world confusion or harm.
That implementations somehow managed to do the right thing in spite of obviously defective standardese doesn't seem like a good reason to not fix the defects.

[2017-07 Toronto Tuesday PM issue prioritization]

Priority 3; by the way, there's general wording in 24.2 [algorithms.requirements] p10 that lets us specify iterator arithmetic as if we were using random access iterators.

[2017-07-11 Tim comments]

I drafted the P/R fully aware of the general wording in 24.2 [algorithms.requirements] p10. However, that general wording is limited to Clause 28, so to make use of the shorthand permitted by that wording, we would need additional wording importing it to these subclauses.

Moreover, that general wording only defines a+n and b-a; it notably doesn't define a-n, which is needed here. And one cannot merely define a-n as a+(-n) since that has undefined behavior for forward iterators.

Proposed resolution:

This wording is relative to N4659.

  1. Edit 21.3.10.5 [list.ops] as indicated:

    void unique();
    template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);
    

    -?- Requires: The comparison function shall be an equivalence relation.

    -19- Effects: If empty(), has no effects. Otherwise, eErases all but the first element from every consecutive group of equalequivalent elements referred to by the iterator i in the range [first + 1, last)[next(begin()), end()) for which *i == *(i-1)*j == *i (for the version of unique with no arguments) or pred(*i, *(i - 1))pred(*j, *i) (for the version of unique with a predicate argument) holds, where j is an iterator in [begin(), end()) such that next(j) == i. Invalidates only the iterators and references to the erased elements.

    -20- Throws: Nothing unless an exception is thrown by *i == *(i-1) or pred(*i, *(i - 1)) the equality comparison or the predicate.

    -21- Complexity: If the range [first, last) is not empty!empty(), exactly (last - first) - 1size() - 1 applications of the corresponding predicate, otherwise no applications of the predicate.

  2. Edit 21.3.9.6 [forwardlist.ops] as indicated:

    void unique();
    template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);
    

    -?- Requires: The comparison function shall be an equivalence relation.

    -16- Effects: If empty(), has no effects. Otherwise, eErases all but the first element from every consecutive group of equalequivalent elements referred to by the iterator i in the range [first + 1, last)[next(begin()), end()) for which *i == *(i-1)*j == *i (for the version with no arguments) or pred(*i, *(i - 1))pred(*j, *i) (for the version with a predicate argument) holds, where j is an iterator in [begin(), end()) such that next(j) == i. Invalidates only the iterators and references to the erased elements.

    -17- Throws: Nothing unless an exception is thrown by the equality comparison or the predicate.

    -18- Complexity: If the range [first, last) is not empty!empty(), exactly (last - first) - 1distance(begin(), end()) - 1 applications of the corresponding predicate, otherwise no applications of the predicate.


2999(i). §[thread.decaycopy] issue

Section: 31.2.6 [thread.decaycopy] Status: New Submitter: Marshall Clow Opened: 2017-07-11 Last modified: 2017-11-12

Priority: 3

View all issues with New status.

Discussion:

[thread.decaycopy] says:

In several places in this Clause the operation DECAY_COPY(x) is used. All such uses mean call the function decay_copy(x) and use the result, where decay_copy is defined as follows:

but decay_copy is not defined in any synopsis.

The Ranges TS has a similar use of DECAY_COPY, except that theirs is also constexpr and noexcept.

We should mark the function decay_copy as "exposition only" and constexpr and noexcept.

[2017-07-16, Daniel comments]

Currently there exists no proper way to mark decay_copy as conditionally noexcept as explained in N3255 section "Adding to the Standard". This is also slighly related to the request to add an is_nothrow_convertible trait, as requested by LWG 2040.

[ 2017-11-01 P3 as result of c++std-lib online vote. ]

Proposed resolution:


3003(i). <future> still has type-erased allocators in promise

Section: 31.6.6 [futures.promise] Status: Open Submitter: Billy O'Neal III Opened: 2017-07-16 Last modified: 2018-01-28

Priority: 2

View other active issues in [futures.promise].

View all other issues in [futures.promise].

View all issues with Open status.

Discussion:

In Toronto Saturday afternoon LWG discussed LWG 2976 which finishes the job of removing allocator support from packaged_task. LWG confirmed that, despite the removal of packaged_task allocators "because it looks like std::function" was incorrect, they wanted to keep the allocator removals anyway, in large part due to this resolution being a response to an NB comment.

If we don't want the type erased allocator situation at all, then we should remove them from the remaining place they exist in <future>, namely, in promise.

This change also resolves potential implementation divergence on whether allocator::construct is intended to be used on elements constructed in the shared state, and allows the emplace-construction-in-future paper, P0319, to be implemented without potential problems there.

[28-Nov-2017 Mailing list discussion - set priority to P2]

Lots of people on the ML feel strongly about this; the suggestion was made that a paper would be welcomed laying out the rationale for removing allocator support here (and in other places).

[2018-1-26 issues processing telecon]

Status to 'Open'; Billy to write a paper.

Proposed resolution:

This resolution is relative to N4659.

  1. Edit 31.6.6 [futures.promise], class template promise synopsis, as indicated:

    template<class R> 
    class promise {
    public:
      promise();
      template <class Allocator>
        promise(allocator_arg_t, const Allocator& a);
      […]
    };
    template <class R>
      void swap(promise<R>& x, promise<R>& y) noexcept;
    template <class R, class Alloc>
      struct uses_allocator<promise<R>, Alloc>;
    
    […]
    template <class R, class Alloc>
      struct uses_allocator<promise<R>, Alloc>
        : true_type { };
    

    -3- Requires: Alloc shall be an Allocator (15.5.3.5 [allocator.requirements]).

    promise();
    template <class Allocator>
      promise(allocator_arg_t, const Allocator& a);
    

    -4- Effects: constructs a promise object and a shared state. The second constructor uses the allocator a to allocate memory for the shared state.


3006(i). Constructing a basic_stringbuf from a string — where does the allocator come from?

Section: 28.8.2.1 [stringbuf.cons] Status: New Submitter: Marshall Clow Opened: 2017-08-02 Last modified: 2017-11-12

Priority: 3

View all other issues in [stringbuf.cons].

View all issues with New status.

Discussion:

Consider the following constructor from 28.8.2.1 [stringbuf.cons] before p3:

explicit basic_stringbuf(
  const basic_string<charT, traits, Allocator>& s,
  ios_base::openmode which = ios_base::in | ios_base::out);

In [stringbuf.cons]/3, it says:

Effects: Constructs an object of class basic_stringbuf, initializing the base class with basic_streambuf() (30.6.3.1), and initializing mode with which. Then calls str(s).

But that doesn't mention where the allocator for the basic_stringbuf comes from. Until recently, most implementations just default constructed the allocator. But that requires that the allocator be default constructible.

This bug was filed against libc++ for this case.

I think that the basic_stringbuf should be constructed with a copy of the allocator from the string.

[2017-11-01, Marshall comments]

There exist two related proposals, namely P0407R1 and P0408R2.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 3

Proposed resolution:


3011(i). Requirements for assert(E) inconsistent with C

Section: 18.3 [assertions] Status: Open Submitter: Jonathan Wakely Opened: 2017-08-18 Last modified: 2018-08-27

Priority: 2

View other active issues in [assertions].

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 18.3.1 [cassert.syn] or 18.3.2 [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:


3012(i). atomic<T> is unimplementable for non-is_trivially_copy_constructible T

Section: 30.7 [atomics.types.generic] Status: Ready Submitter: Billy O'Neal III Opened: 2017-08-16 Last modified: 2018-11-11

Priority: 2

View all other issues in [atomics.types.generic].

View all issues with Ready status.

Discussion:

30.7 [atomics.types.generic] requires that T for std::atomic is trivially copyable. Unfortunately, that's not sufficient to implement atomic. Consider atomic<T>::load, which wants to look something like this:

template<class T>
struct atomic {
  __compiler_magic_storage_for_t storage;

  T load(memory_order = memory_order_seq_cst) const {
    return __magic_intrinsic(storage);
  }

};

Forming this return statement, though, requires that T is copy constructible — trivially copyable things aren't necessarily copyable! For example, the following is trivially copyable but breaks libc++, libstdc++, and msvc++:

struct NonAssignable {
  int i;
  NonAssignable() = delete;
  NonAssignable(int) : i(0) {}
  NonAssignable(const NonAssignable&) = delete;
  NonAssignable(NonAssignable&&) = default;
  NonAssignable& operator=(const NonAssignable&) = delete;
  NonAssignable& operator=(NonAssignable&&) = delete;
  ~NonAssignable() = default;
};

All three standard libraries are happy as long as T is trivially copy constructible, assignability is not required. Casey Carter says that we might want to still require trivially copy assignable though, since what happens when you do an atomic<T>::store is morally an "assignment" even if it doesn't use the user's assignment operator.

[2017-11 Albuquerque Wednesday issue processing]

Status to Open; Casey and STL to work with Billy for better wording.

Should this include trivially copyable as well as trivially copy assignable?

2017-11-09, Billy O'Neal provides updated wording.

Previous resolution [SUPERSEDED]:

This resolution is relative to N4687.

  1. Edit 30.7 [atomics.types.generic] as indicated:

    -1- If is_trivially_copy_constructible_v<T> is false, the program is ill-formedThe template argument for T shall be trivially copyable (6.7 [basic.types]). [Note: Type arguments that are not also statically initializable may be difficult to use. — end note]

Previous resolution [SUPERSEDED]:

This resolution is relative to N4687.

  1. Edit 30.7 [atomics.types.generic] as indicated:

    -1- If is_copy_constructible_v<T> is false or if is_trivially_copyable_v<T> is false, the program is ill-formedThe template argument for T shall be trivially copyable (6.7 [basic.types]). [Note: Type arguments that are not also statically initializable may be difficult to use. — end note]

[2017-11-12, Tomasz comments and suggests alternative wording]

According to my understanding during Albuquerque Saturday issue processing we agreed that we want the type used with the atomics to have non-deleted and trivial copy/move construction and assignment.

Wording note: CopyConstructible and CopyAssignable include semantic requirements that are not checkable at compile time, so these are requirements imposed on the user and cannot be validated by an implementation without heroic efforts.

[2018-11 San Diego Thursday night issue processing]

Status to Ready.

Proposed resolution:

This resolution is relative to N4700.

  1. Edit 30.7 [atomics.types.generic] as indicated:

    -1- The template argument for T shall meet the CopyConstructible and CopyAssignable requirements. If is_trivially_copyable_v<T> && is_copy_constructible_v<T> && is_move_constructible_v<T> && is_copy_assignable_v<T> && is_move_assignable_v<T> is false, the program is ill-formedbe trivially copyable (6.7 [basic.types]). [Note: Type arguments that are not also statically initializable may be difficult to use. — end note]


3018(i). shared_ptr of function type

Section: 19.11.3 [util.smartptr.shared] Status: New Submitter: Agustín K-ballo Bergé Opened: 2017-09-13 Last modified: 2017-11-12

Priority: 3

View other active issues in [util.smartptr.shared].

View all other issues in [util.smartptr.shared].

View all issues with New status.

Discussion:

shared_ptr has been designed to support use cases where it owns a pointer to function, and whose deleter does something with it. This can be used, for example, to keep a dynamic library loaded for as long as their exported functions are referenced.

Implementations have overlooked that the T in shared_ptr<T> can be a function type. It isn't immediately obvious from the standard, and it's not possible to tell from the wording that this is intentional.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 3

Proposed resolution:

This wording is relative to N4687.

  1. Edit 19.11.3 [util.smartptr.shared] as indicated:

    […]

    -2- Specializations of shared_ptr shall be CopyConstructible, CopyAssignable, and LessThanComparable, allowing their use in standard containers. Specializations of shared_ptr shall be contextually convertible to bool, allowing their use in boolean expressions and declarations in conditions. The template parameter T of shared_ptr may be an incomplete type.

    -?- The template parameter T of shared_ptr may be an incomplete type. T* shall be an object pointer type or a function pointer type.

    […]


3019(i). Presentation of "program defined classes derived from error_category" [syserr.errcat.derived] unclear and contains mistakes

Section: 18.5.2.4 [syserr.errcat.derived] Status: New Submitter: Thomas Köppe Opened: 2017-09-20 Last modified: 2017-11-12

Priority: 3

View all issues with New status.

Discussion:

The presentation of section [syserr.errcat.derived] is currently somewhat problematic:

Partial wording proposal:

  1. In 18.5.2.1 [syserr.errcat.overview] p1, change:

    -1- The class error_category serves as a base class for types used to identify the source and encoding of a particular category of error code. Classes may be derived from error_category to support categories of errors in addition to those defined in this International Standard. Such classes shall behave as specified in this subclause18.5.2.4 [syserr.errcat.derived]. [Note: error_category objects are passed by reference, and two such objects are equal if they have the same address. This means that applications using custom error_category types should create a single object of each such type. — end note]

  2. In 18.5.2.4 [syserr.errcat.derived], change:

    virtual const char* name() const noexcept override  = 0;
    

    -1- Returns: A string naming the error category.

    virtual error_condition default_error_condition(int ev) const noexcept override;
    

    -2- Returns: An object of type error_condition that corresponds to ev.

    virtual bool equivalent(int code, const error_condition& condition) const noexcept override;
    

    -3- Returns: true if, for the category of error represented by *this, code is considered equivalent to condition; otherwise, false.

    virtual bool equivalent(const error_code& code, int condition) const noexcept override;
    

    -4- Returns: true if, for the category of error represented by *this, code is 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:


3021(i). [networking.ts] Relax pointer equivalence requirement for ConstBufferSequence

Section: 16.2.2 [networking.ts::buffer.reqmts.constbuffersequence] Status: New Submitter: Vinnie Falco Opened: 2017-09-20 Last modified: 2017-11-12

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.

  1. Modify 16.2.2 [networking.ts::buffer.reqmts.constbuffersequence] Table 13 "ConstBufferSequence requirements" as indicated:

    Table 13 — ConstBufferSequence requirements
    expression 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;
        })
    

3023(i). Clarify unspecified call wrappers

Section: 19.14.15 [func.memfn], 19.14.12 [func.not_fn], 19.14.14 [func.bind] Status: New Submitter: Detlef Vollmann Opened: 2017-10-07 Last modified: 2017-11-12

Priority: 3

View all other issues in [func.memfn].

View all issues with New status.

Discussion:

Even after the discussion on the reflector, starting with this reflector message it's not completely clear that unspecified as return type of mem_fn really means 'unspecified, but always the same'. The same problem exists for bind() and not_fn().

Possible solution:

Specify in 19.14.2 [func.def] or 19.14.3 [func.require] that a call wrapper type is always the same for forwarding call wrappers if the object is returned by a function with the same parameter types. And also put into 19.14.12 [func.not_fn] that a call_wrapper object is a simple call wrapper.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 3. Tomasz to write a paper that will address this issue. See also 3015

[2017-11-10, Tomasz comments and provides wording together with STL]

From the core language rules it is already required that same function template specialization have the same return type. Given that the invocation of mem_fn/bind/not_fn will always return the same wrapper type, if they are instantiated (called with) same parameters type. However, the existence of this issue, shows that some library-wide clarification note would be welcomed.

Proposed resolution:

This wording is relative to N4700.

  1. After section 15.4.2.1.2 [expos.only.types] "Exposition-only types" add the following new section:

    ?.?.?.?.? unspecified types [unspecified.types]

    [Note: Whenever the return type of a function template is declared as unspecified, the return type depends only on the template arguments of the specialization. Given the example:

    template<class T> unspecified f(T);
    

    the expressions f(0) and f(1) have the same type. — end note]


3027(i). [networking.ts] DynamicBuffer prepare exception specification

Section: 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer] Status: New Submitter: Vinnie Falco Opened: 2017-10-16 Last modified: 2017-11-12

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.

  1. Change 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer], Table 14 "DynamicBuffer requirements", as indicated:

    Table 14 — DynamicBuffer requirements
    expression return type assertion/note pre/post-condition
    […]
    x.prepare(n) X::mutable_buffers_type Returns 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() or prepare() are invalidated.
    Throws: length_error if size() + n
    exceeds max_size() or any other exception
    if the request cannot otherwise be satisfied
    .

3028(i). Container requirements tables should distinguish const and non-const variables

Section: 21.2.1 [container.requirements.general] Status: New Submitter: Jonathan Wakely Opened: 2017-10-17 Last modified: 2017-11-22

Priority: 3

View other active issues in [container.requirements.general].

View all other issues in [container.requirements.general].

View all issues with New status.

Discussion:

[container.requirements.general] p4 says:

In Tables 83, 84, and 85 X denotes a container class containing objects of type T, a and b denote values of type X, u denotes an identifier, r denotes a non-const value of type X, and rv denotes a non-const rvalue of type X.

This doesn't say anything about whether a and b are allowed to be const, or must be non-const. In fact Table 83 uses them inconsistently, e.g. the rows for "a = rv" and "a.swap(b)" most certainly require them to be non-const, but all other uses are valid for either const or non-const X.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 3; Jonathan to provide updated wording.

Wording needs adjustment - could use "possibly const values of type X"

Will distinguish between lvalue/rvalue

Proposed resolution:

This wording is relative to N4687.

  1. Change 21.2.1 [container.requirements.general] p4 as indicated:

    -4- In Tables 83, 84, and 85 X denotes a container class containing objects of type T, a and b denote values of type X, u denotes an identifier, r and s denotes a non-const values of type X, and rv denotes a non-const rvalue of type X.

  2. Change 21.2.1 [container.requirements.general], Table 83 "Container requirements", as indicated:

    Table 83 — Container requirements
    Expression Return type Operational
    semantics
    Assertion/note
    pre/post-condition
    Complexity
    […]
    ar = rv X& All existing elements
    of ar are either move
    assigned to or
    destroyed
    ar shall be equal to
    the value that rv had
    before this
    assignment
    linear
    […]
    ar.swap(bs) void exchanges the
    contents of ar and bs
    (Note A)
    […]
    swap(ar, bs) void ar.swap(bs) (Note A)

3029(i). pop_heap over-constrains input

Section: 24.7.7.2 [pop.heap] Status: Open Submitter: Mathias Stearn Opened: 2017-11-04 Last modified: 2017-11-13

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.

  1. Change 24.7.7.2 [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 valid non-empty heap. RandomAccessIterator shall satisfy the requirements of ValueSwappable (15.5.3.2 [swappable.requirements]). The type of *first shall satisfy the requirements of MoveConstructible (Table 23) and of MoveAssignable (Table 25).


3032(i). ValueSwappable requirement missing for push_heap and make_heap

Section: 24.7.7 [alg.heap.operations] Status: New Submitter: Robert Douglas Opened: 2017-11-08 Last modified: 2017-11-12

Priority: 3

View all other issues in [alg.heap.operations].

View all issues with New status.

Discussion:

In discussion of D0202R3 in Albuquerque, it was observed that pop_heap and sort_heap had constexpr removed for their requirement of ValueSwappable. It was then observed that push_heap and make_heap were not similarly marked as having the ValueSwappable requirement. The room believed this was likely a specification error, and asked to open an issue to track it.

[2017-11 Albuquerque Wednesday night issues processing]

Priority set to 3; Marshall to investigate

Proposed resolution:

This wording is relative to N4700.

  1. Change 24.7.7.1 [push.heap] as indicated:

    template<class RandomAccessIterator>
      void push_heap(RandomAccessIterator first, RandomAccessIterator last);
    template<class RandomAccessIterator, class Compare>
      void push_heap(RandomAccessIterator first, RandomAccessIterator last,
                     Compare comp);
    

    -1- Requires: The range [first, last - 1) shall be a valid heap. RandomAccessIterator shall satisfy the requirements of ValueSwappable (15.5.3.2 [swappable.requirements]). The type of *first shall satisfy the MoveConstructible requirements (Table 23) and the MoveAssignable requirements (Table 25).

  2. Change 24.7.7.3 [make.heap] as indicated:

    template<class RandomAccessIterator>
      void make_heap(RandomAccessIterator first, RandomAccessIterator last);
    template<class RandomAccessIterator, class Compare>
      void make_heap(RandomAccessIterator first, RandomAccessIterator last,
                     Compare comp);
    

    -1- Requires: RandomAccessIterator shall satisfy the requirements of ValueSwappable (15.5.3.2 [swappable.requirements]). The type of *first shall satisfy the MoveConstructible requirements (Table 23) and the MoveAssignable requirements (Table 25).


3036(i). polymorphic_allocator::destroy is extraneous

Section: 19.12.3 [mem.poly.allocator.class] Status: New Submitter: Casey Carter Opened: 2017-11-15 Last modified: 2017-11-28

Priority: 3

View all other issues in [mem.poly.allocator.class].

View all issues with New status.

Discussion:

polymorphic_allocator's member function destroy is exactly equivalent to the default implementation of destroy in allocator_traits (19.10.9.2 [allocator.traits.members] para 6). It should be struck from polymorphic_allocator as it provides no value.

[28-Nov-2017 Mailing list discussion - set priority to P3]

PJ says that Dinkumware is shipping an implementation of polymorphic_allocator with destroy, so removing it would be a breaking change for him.

Proposed resolution:

Wording relative to N4700.

  1. Strike the declaration of destroy from the synopsis of class polymorphic_allocator in 19.12.3 [mem.poly.allocator.class]:

    template <class T1, class T2, class U, class V>
      void construct(pair<T1,T2>* p, pair<U, V>&& pr);
    
    template <class T>
      void destroy(T* p);
    
    polymorphic_allocator select_on_container_copy_construction() const;
    
  2. Strike the specification of destroy in 19.12.3.2 [mem.poly.allocator.mem]:

    […]
    template <class T>
      void destroy(T* p);
    

    14 Effects: As if by p->~T().

    […]

3040(i). basic_string_view::starts_with Effects are incorrect

Section: 20.4.2.6 [string.view.ops] Status: Tentatively Ready Submitter: Marshall Clow Opened: 2017-11-29 Last modified: 2018-11-11

Priority: 0

View all other issues in [string.view.ops].

View all issues with Tentatively Ready status.

Discussion:

The effects of starts_with are described as equivalent to return compare(0, npos, x) == 0.

This is incorrect, because it returns false when you check to see if any sequence begins with the empty sequence. (There are other failure cases, but that one's easy)

As a drive-by fix, we can make the Effects: for starts_with and ends_with clearer.

Those are the second and proposed third changes, and they are not required.

[ 2017-12-13 Moved to Tentatively Ready after 8 positive votes for P0 on c++std-lib. ]

Previous resolution: [SUPERSEDED]

This wording is relative to N4713.

  1. Change 20.4.2.6 [string.view.ops] p20 as indicated:

    constexpr bool starts_with(basic_string_view x) const noexcept;

    -20- Effects: Equivalent to: return size() >= x.size() && compare(0, nposx.size(), x) == 0;

  2. Change 20.4.2.6 [string.view.ops] p21 as indicated:

    constexpr bool starts_with(charT x) const noexcept;

    -21- Effects: Equivalent to: return !empty() && traits::eq(front(), x)starts_with(basic_string_view(&x, 1));

  3. Change 20.4.2.6 [string.view.ops] p24 as indicated:

    constexpr bool ends_with(charT x) const noexcept;

    -24- Effects: Equivalent to: return !empty() && traits::eq(back(), x)ends_with(basic_string_view(&x, 1));

[2018-01-23, Reopening due to a comment of Billy Robert O'Neal III requesting a change of the proposed wording]

The currently suggested wording has:

Effects: Equivalent to: return size() >= x.size() && compare(0, x.size(), x) == 0;

but compare() already does the size() >= x.size() check.

It seems like it should say:

Effects: Equivalent to: return substr(0, x.size()) == x;

[ 2018-10-29 Moved to Tentatively Ready after 5 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This wording is relative to N4713.

  1. Change 20.4.2.6 [string.view.ops] p20 as indicated:

    constexpr bool starts_with(basic_string_view x) const noexcept;

    -20- Effects: Equivalent to: return substr(0, x.size()) == xcompare(0, npos, x) == 0;

  2. Change 20.4.2.6 [string.view.ops] p21 as indicated:

    constexpr bool starts_with(charT x) const noexcept;

    -21- Effects: Equivalent to: return !empty() && traits::eq(front(), x)starts_with(basic_string_view(&x, 1));

  3. Change 20.4.2.6 [string.view.ops] p24 as indicated:

    constexpr bool ends_with(charT x) const noexcept;

    -24- Effects: Equivalent to: return !empty() && traits::eq(back(), x)ends_with(basic_string_view(&x, 1));


3044(i). Strange specification of max_size() for an allocator

Section: 15.5.3.5 [allocator.requirements] Status: New Submitter: Jon Cohen Opened: 2017-12-06 Last modified: 2018-08-27

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 (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.

[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 says it can't change.

As noted in the LWG 2162 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). 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.

Proposed resolution:

This wording is relative to N4762.

  1. Change 15.5.3.5 [allocator.requirements], Table 32 — "Descriptive variable definitions", as indicated:

    Table 32 — Descriptive variable definitions
    Variable Definition
    T, U, C any cv-unqualified object type (3.9)
    a, a1, a2 lvalues of type X
    a3 an lvalue of type const X
  2. Change 15.5.3.5 [allocator.requirements], Table 33 — "Cpp17Allocator requirements", as indicated:

    Table 33 — Cpp17Allocator requirements
    Expression Return type Assertion/note
    pre-/post-condition
    Default
    a3.max_size() X::size_type 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.
    numeric_limits<size_type>::max()
    / sizeof(value_type)

3046(i). Do not require reference_wrapper to support non-referenceable function types

Section: 19.14.5 [refwrap] Status: New Submitter: Jonathan Wakely Opened: 2017-12-14 Last modified: 2018-01-29

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]

Proposed resolution:

This wording is relative to N4713.

  1. Modify 19.14.5 [refwrap] as indicated:

    -1- reference_wrapper<T> is a CopyConstructible and CopyAssignable wrapper around a reference to an object or function of type T. T shall be a referenceable type (15.3.22 [defns.referenceable]) that is not a reference type.

    -2- reference_wrapper<T> shall be a trivially copyable type (6.7 [basic.types]).


3047(i). atomic compound assignment operators can cause undefined behavior when corresponding fetch_meow members don't

Section: 30.7.2 [atomics.types.int], 30.7.4 [atomics.types.pointer], 30.7.5 [atomics.types.memop] Status: New Submitter: Tim Song Opened: 2017-12-15 Last modified: 2018-01-29

Priority: 3

View all issues with New status.

Discussion:

Given atomic<int> meow{INT_MAX};, meow.fetch_add(1) has well-defined behavior because 30.7.2 [atomics.types.int] p7 says that

Remarks: For signed integer types, arithmetic is defined to use two's complement representation. There are no undefined results.

but meow += 1 and ++meow have undefined behavior, because these operator functions are defined (by, respectively, 30.7.2 [atomics.types.int] p8 and 30.7.5 [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]

Proposed resolution:


3049(i). Missing wording allowing algorithms to use copies of function objects as substitutes for their parameters

Section: 24.2 [algorithms.requirements] Status: Open Submitter: Jared Hoberock Opened: 2017-12-04 Last modified: 2018-03-18

Priority: 3

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 24.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, 24.6.7 [alg.generate] refers to gen as a function object, but 24.5.4 [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 24.2 [algorithms.requirements] and 24.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.

Proposed resolution:

This wording is relative to N4713.

  1. Modify 24.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 as reference_wrapper<T> (19.14.5 [refwrap]), or some equivalent solution. — end note]

  2. Modify 24.5.4 [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 f to 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: Function shall meet the requirements of CopyConstructible.

    -7- Effects: Applies the function object f to 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 f to 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: Function shall meet the requirements of CopyConstructible.

    […]

    -18- Effects: Applies the function object f to the result of dereferencing every iterator in the range [first, first + n). […]

    […]


3050(i). Conversion specification problem in chrono::duration constructor

Section: 26.5.1 [time.duration.cons], 26.5.5 [time.duration.nonmember] Status: Open Submitter: Barry Revzin Opened: 2018-01-22 Last modified: 2018-11-11

Priority: 3

View other active issues in [time.duration.cons].

View all other issues in [time.duration.cons].

View all issues with Open status.

Discussion:

The converting constructor in std::chrono::duration is currently specified as (26.5.1 [time.duration.cons] p1):

template<class Rep2>
  constexpr explicit duration(const Rep2& r); 

Remarks: This constructor shall not participate in overload resolution unless Rep2 is implicitly convertible to rep and […]

But the parameter is of type Rep2 const, so we should check that Rep2 const is implicitly convertible to rep, not just Rep2. This means that for a type like:

struct X { operator int64_t() /* not const */; };

std::is_constructible_v<std::chrono::seconds, X> is true, but actual construction will fail to compile.

Further analysis of sub-clause 26.5 [time.duration] revealed similar specification problems in some other functions.

[2018-06 Rapperswil Thursday issues processing]

P3; Status to Open

[2018-11 San Diego Thursday night issue processing]

Jonathan to provide updated wording; the underlying text has changed.

Proposed resolution:

This wording is relative to N4713.

  1. Modify 26.5.1 [time.duration.cons] as indicated:

    template<class Rep2>
      constexpr explicit duration(const Rep2& r);
    

    -1- Remarks: This constructor shall not participate in overload resolution unless is_convertible_v<const Rep2&, rep> is trueRep2 is implicitly convertible to rep and

    1. (1.1) — treat_as_floating_point_v<rep> is true or

    2. (1.2) — treat_as_floating_point_v<Rep2> is false.

    […]

    -2- Effects: Constructs an object of type duration.

    -3- Postconditions: count() == static_cast<rep>(r).

  2. Modify 26.5.5 [time.duration.nonmember] as indicated:

    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator*(const duration<Rep1, Period>& d, const Rep2& s);
    

    -4- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep2&, CR(Rep1, Rep2)> is trueRep2 is implicitly convertible to CR(Rep1, Rep2).

    […]

    template<class Rep1, class Rep2, class Period>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator*(const Rep1& s, const duration<Rep2, Period>& d);
    

    -6- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep1&, CR(Rep1, Rep2)> is trueRep1 is implicitly convertible to CR(Rep1, Rep2).

    […]

    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator/(const duration<Rep1, Period>& d, const Rep2& s);
    

    -8- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep2&, CR(Rep1, Rep2)> is trueRep2 is implicitly convertible to CR(Rep1, Rep2) and Rep2 is not a specialization of duration.

    […]
    template<class Rep1, class Period, class Rep2>
      constexpr duration<common_type_t<Rep1, Rep2>, Period>
        operator%(const duration<Rep1, Period>& d, const Rep2& s);
    

    -11- Remarks: This operator shall not participate in overload resolution unless is_convertible_v<const Rep2&, CR(Rep1, Rep2)> is trueRep2 is implicitly convertible to CR(Rep1, Rep2) and Rep2 is not a specialization of duration.

    […]


3052(i). visit is underconstrained

Section: 19.7.7 [variant.visit] Status: LEWG Submitter: Casey Carter Opened: 2018-01-23 Last modified: 2018-06-18

Priority: 2

View all other issues in [variant.visit].

View all issues with LEWG status.

Discussion:

std::visit accepts a parameter pack of forwarding references named vars whose types are the parameter pack Variants. Despite that:

  1. the names of both packs are variants of "variant",
  2. para 1 passes the types in Variants (modified) to variant_size_v,
  3. para 3 uses the expression varsi.index(),
  4. para 4 says "...if any variant in vars is valueless_by_exception, and
  5. para 5 mentions "..the number of alternative types of Variants0"
the Requires element imposes no explicit requirements on the types in Variants. Notably, the Variants are not required to be variants. This lack of constraints appears to be simply an oversight.

[2017-01-24, Daniel comments]

This issue should be reviewed in common with LWG 2970.

[2018-06-18 after reflector discussion]

Priority set to 2; status to LEWG

Proposed resolution:

This wording is relative to N4727.

  1. Modify 19.7.7 [variant.visit] as indicated:

    template<class Visitor, class... Variants>
      constexpr see below visit(Visitor&& vis, Variants&&... vars);
    

    […]

    -4- Throws: bad_variant_access if any variant in vars is valueless_by_exception()(vars.valueless_by_exception() || ...) is true.

    -5- Complexity: […]

    -?- Remarks: This function shall not participate in overload resolution unless remove_cvref_t<Variantsi> is a specialization of variant for all 0 <= i < n.


3053(i). Prohibit error_code construction from rvalues of error_category

Section: 18.5.3.1 [syserr.errcode.overview] Status: New Submitter: Antony Polukhin Opened: 2018-01-24 Last modified: 2018-06-18

Priority: 3

View all other issues in [syserr.errcode.overview].

View all issues with New status.

Discussion:

Constructor error_code(int val, const error_category& cat) and member function void assign(int val, const error_category& cat) could be misused if a custom error_category is provided:

error_code ec{1, test_category{}}; // ec holds a pointer/reference to a temporary

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4713.

  1. Modify 18.5.3.1 [syserr.errcode.overview] as indicated:

    namespace std {
      class error_code {
      public:
        // 18.5.3.2 [syserr.errcode.constructors], constructors
        […]
        error_code(int val, const error_category& cat) noexcept;
        error_code(int val, const error_category&& cat) = delete;
        […]
        // 18.5.3.3 [syserr.errcode.modifiers], modifiers
        void assign(int val, const error_category& cat) noexcept;
        void assign(int val, const error_category&& cat) = delete;
        […]
      };
      […]
    }
    

3055(i). path::operator+=(single-character) misspecified

Section: 28.11.7.4.4 [fs.path.concat] Status: New Submitter: Tim Song Opened: 2018-01-24 Last modified: 2018-11-11

Priority: 3

View all other issues in [fs.path.concat].

View all issues with New status.

Discussion:

28.11.7.4.4 [fs.path.concat] uses the expression path(x).native() to specify the effects of concatenating a single character x. However, there is no path constructor taking a single character.

[2018-06-18 after reflector discussion]

Priority set to 3

[2018-10-12 Tim updates PR to avoid suggesting the creation of a temporary path.]

Previous resolution [SUPERSEDED]:

This wording is relative to N4713.

  1. Modify 28.11.7.4.4 [fs.path.concat] as indicated:

    path& operator+=(const path& x);
    path& operator+=(const string_type& x);
    path& operator+=(basic_string_view<value_type> x);
    path& operator+=(const value_type* x);
    path& operator+=(value_type x);
    template<class Source>
      path& operator+=(const Source& x);
    template<class EcharT>
      path& operator+=(EcharT x);
    template<class Source>
      path& concat(const Source& x);
    

    -1- Effects: […]

    -2- Returns: *this.

    path& operator+=(value_type x);
    template<class EcharT>
      path& operator+=(EcharT x);
    

    -?- Effects: Equivalent to: return *this += path(&x, &x + 1);.

Proposed resolution:

This wording is relative to N4762.

  1. Modify 28.11.7.4.4 [fs.path.concat] as indicated:

    path& operator+=(const path& x);
    path& operator+=(const string_type& x);
    path& operator+=(basic_string_view<value_type> x);
    path& operator+=(const value_type* x);
    path& operator+=(value_type x);
    template<class Source>
      path& operator+=(const Source& x);
    template<class EcharT>
      path& operator+=(EcharT x);
    template<class Source>
      path& concat(const Source& x);
    

    -1- Effects: […]

    -2- Returns: *this.

    path& operator+=(value_type x);
    template<class EcharT>
      path& operator+=(EcharT x);
    

    -?- Effects: Equivalent to: return *this += basic_string_view(&x, 1);


3056(i). copy_file() copies which attributes?

Section: 28.11.14.4 [fs.op.copy_file] Status: New Submitter: Davis Herring Opened: 2018-01-26 Last modified: 2018-01-28

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.

  1. Modify 28.11.14.4 [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/setgid permissions 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;
    

    […]

    1. […]

    2. (4.2) — Otherwise, copy the contents, permissions, and data modification time and attributes of the file from resolves to, to the file to resolves to, if:

    3. […]

      1. (4.2.3) — (options & copy_options::update_existing) != copy_options::none and from is more recent than to, determined as if by use of the last_write_time function (28.11.14.25 [fs.op.last_write_time]).

      Other implementation-defined attributes may be copied. Failure (or partial failure) to copy attributes is not an error.

    4. […]

    […]


3057(i). Correct copy_options handling

Section: 28.11.14.3 [fs.op.copy] Status: Open Submitter: Davis Herring Opened: 2018-01-29 Last modified: 2018-11-25

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:

  1. 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.

  2. 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).

  3. The copy_options groups for existing target files and the form of copying are consulted only for creating regular files.

  4. copy("file", "dir") creates dir/file, but copy("symlink", "dir", copy_options::copy_symlinks) fails.

  5. 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.

  6. 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:

  1. 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.)

  2. 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.

This also addresses the same issue as LWG 2682, 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]

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 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.

  1. Modify Table 115 — "Enum class copy_options" as indicated:

    Option group controlling copy and copy_file function effects for existing target files
    Constant Meaning
    […] […]
  2. Modify 28.11.14.3 [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.

    28.11.14.3 [fs.op.copy]/4.9 is redundant given 28.11.6 [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 (28.11.9.3 [fs.enum.copy.opts]) is set in options.

    -4- Effects: Before the first use of f and t:

    1. (4.1) — If […]

    2. […]

    3. (4.10) — Otherwise, no effects.

    If each is needed below,
    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);
    

    [Drafting note: copy_options::create_symlinks is intentionally omitted for linkf; it may simply have been a typo for copy_options::copy_symlinks (which was added by LWG 2681) since at least N3940.]

    Effects are then as follows:
    1. (?.?) — If f.type() or t.type() is an implementation-defined file type 28.11.9.2 [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.]

    2. (?.?) — Otherwise, if exists(f) is false, report an error as specified in 28.11.6 [fs.err.report].

    3. (?.?) — Otherwise, do nothing if

      1. (?.?.?) — (options & copy_options::directories_only) != copy_options::none and is_directory(f) is false, or

      2. (?.?.?) — (options & copy_options::skip_symlinks) != copy_options::none and is_symlink(f) is true, or

      3. (?.?.?) — (options & copy_options::skip_existing) != copy_options::none and exists(t2) is true.

    4. (?.?) — Otherwise, report an error as specified in 28.11.6 [fs.err.report] if:

      1. (?.?.?) — is_other(f) || is_other(t2) is true, or

      2. (?.?.?) — exists(t2) && exists(from) == exists(to2) && equivalent(from, to) is true.

    5. (?.?) — Otherwise, if is_directory(f) is true, then:

      1. (?.?.?) — create_directory(to, from).

      2. (?.?.?) — If (options & copy_options::recursive) != copy_options::none or if (options & copy_options::directories_only) == copy_options::none, iterate over the files in from, as if by

        for (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);
        
    6. (?.?) — Otherwise, do nothing if (options & copy_options::update_existing) != copy_options::none, exists(to2) is true, and from is not more recent than to2, determined as if by use of the last_write_time function (28.11.14.25 [fs.op.last_write_time]).

    7. (?.?) — Otherwise, report an error as specified in 28.11.6 [fs.err.report] if:

      1. (?.?.?) — is_directory(t2) is true, or

      2. (?.?.?) — (options & (copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none and exists(t2) is true.

    8. (?.?) — Otherwise, if linkt is true, then:

      1. (?.?.?) — remove(to2) if an existing to2 would prevent the following link creation.

      2. (?.?.?) — If (options & copy_options::create_symlinks) != copy_options::none, create_symlink(from, to2). [Note: If from is a symbolic link, it is not followed. — end note]

      3. (?.?.?) — Otherwise, if (options & copy_options::create_hard_links) != copy_options::none, then create a hard link to from, if linkf is true, or else to the file that from resolves to. [Note: Not all file systems that support hard links and symbolic links support creating hard links to symbolic links. — end note]

      4. (?.?.?) — Otherwise, copy_symlink(from, to2).

    9. (?.?) — Otherwise, copy_file(from, to2, options).

    -5- Throws: As specified in 28.11.6 [fs.err.report].

    -6- Remarks: For the signature with argument ec, any library functions called by the implementation shall have an error_code argument if applicable. If any such function fails, copy returns immediately without (further) modifying ec.


3059(i). Wrong requirements for map-like associative container assignment?

Section: 21.2 [container.requirements] Status: New Submitter: Richard Smith Opened: 2018-02-05 Last modified: 2018-02-23

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:

(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.

[2018-02-13, Priority set to 3 after mailing list discussion]

Proposed resolution:


3060(i). XXX_scan algorithms are specified to work with move-only T, but are specified to make N copies of T into the destination range

Section: 24.9.7 [exclusive.scan], 24.9.8 [inclusive.scan], 24.9.9 [transform.exclusive.scan], 24.9.10 [transform.inclusive.scan] Status: New Submitter: Billy O'Neal III Opened: 2018-02-06 Last modified: 2018-02-09

Priority: Not Prioritized

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.

Proposed resolution:


3061(i). What is the return type of compare_3way?

Section: 24.7.11 [alg.3way] Status: Open Submitter: Richard Smith Opened: 2018-02-07 Last modified: 2018-11-11

Priority: 2

View all issues with Open status.

Discussion:

The P0768R1 specification of compare_3way says:

template<class T, class U> constexpr auto compare_3way(const T& a, const U& b);

-1- Effects: Compares two values and produces a result of the strongest applicable comparison category type:

  1. (1.1) — Returns a <=> b if that expression is well-formed.

  2. (1.2) — Otherwise, if the expressions a == b and a < b are each well-formed and convertible to bool, returns strong_ordering::equal when a == b is true, otherwise returns strong_ordering::less when a < b is true, and otherwise returns strong_ordering::greater.

  3. (1.3) — Otherwise, if the expression a == b is well-formed and convertible to bool, returns strong_equality::equal when a == b is true, and otherwise returns strong_equality::nonequal.

  4. (1.4) — Otherwise, the function shall be defined as deleted.

So, it returns strong_ordering::... or strong_equality:: or a <=> b. By the normal core deduction rules, that means it's always ill-formed, because one return type deduction deduces strong_ordering and another deduces strong_equality.

I'm guessing the idea was actually that the above happens as if by four separate overloads / constexpr if / else. But I think you need to actually say that.

[Tomasz suggests proposed wording]

[2018-06-18 after reflector discussion]

Priority set to 2

[2018-11 San Diego Thursday night issue processing]

Spaceship is still in flux; revisit in Kona. Status to Open

Proposed resolution:

This wording is relative to N4713.

  1. Modify 24.4 [algorithm.syn], header <algorithm> synopsis, as indicated:

    // 24.7.11 [alg.3way], three-way comparison algorithms
    template<class T, class U>
    constexpr autosee below compare_3way(const T& a, const U& b);
    
  2. Modify 24.7.11 [alg.3way] as indicated:

    template<class T, class U> constexpr autosee below compare_3way(const T& a, const U& b);
    

    -1- Effects: Compares two values and produces a result of the strongest applicable comparison category type:

    1. (1.1) — Returns a <=> b if that expression is well-formedIf the expression a <=> b is well-formed, returns a value of type decay_t<decltype(a <=> b)> initialized from a <=> b.

    2. (1.2) — Otherwise, if the expressions a == b and a < b are each well-formed and convertible to bool, returns a value of type strong_ordering equal to strong_ordering::equal when a == b is true, otherwise returns strong_ordering::less when a < b is true, and otherwise returns strong_ordering::greater.

    3. (1.3) — Otherwise, if the expression a == b is well-formed and convertible to bool, returns a value of type strong_equality equal to strong_equality::equal when a == b is true, and otherwise returns strong_equality::nonequal.

    4. (1.4) — Otherwise, the return type is void and the function is defined as deleted.


3063(i). Parallel algorithms in <memory> are underspecified

Section: 19.10.11 [specialized.algorithms] Status: New Submitter: Alisdair Meredith Opened: 2018-02-12 Last modified: 2018-03-18

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:


3064(i). How do uninitialized memory algorithms obtain pointer without undefined behavior?

Section: 19.10.11 [specialized.algorithms] Status: New Submitter: Alisdair Meredith Opened: 2018-02-12 Last modified: 2018-03-18

Priority: 3

View other active issues in [specialized.algorithms].

View all other issues in [specialized.algorithms].

View all issues with New status.

Discussion:

A typical specification of the algorithms for initializing raw memory in <memory> looks like:

Effects: Equivalent to:

for (; first != last; ++first)
  ::new (static_cast<void*>(addressof(*first)))
    typename iterator_traits<ForwardIterator>::value_type;

However, this hides a nasty question:

How do we bind a reference to an uninitialized object when dereferencing our iterator, so that static_cast<void*>(addressor(*first)) does not trigger undefined behavior on the call to *first?

When pointers are the only iterators we cared about, we could simply cast the iterator value to void* without dereferencing. I don't see how to implement this spec safely without introducing another customization point for iterators that performs the same function as casting a pointer to void* in order to get the address of the element.

[2018-02-20, Priority set to 3 after mailing list discussion]

Proposed resolution:


3066(i). "report a domain error" in [sf.cmath]/1 is underspecified

Section: 25.8.5 [sf.cmath] Status: New Submitter: Casey Carter Opened: 2018-02-17 Last modified: 2018-06-18

Priority: 3

View all issues with New status.

Discussion:

25.8.5 [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 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 18.2.3 [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:


3068(i). Forbid assigning an rvalue basic_string to basic_string_view

Section: 20.4.2 [string.view.template] Status: LEWG Submitter: Antony Polukhin Opened: 2018-02-19 Last modified: 2018-06-18

Priority: 2

View all other issues in [string.view.template].

View all issues with LEWG status.

Discussion:

It is known that we cannot disable move construction of basic_string_view from rvalues of basic_string, because it breaks a valid use case:

string foo();
void bar(string_view );
bar(foo());

Though it is still possible to disable an absolutely wrong case of assigning an rvalue basic_string to basic_string_view:

string_view sw = "Hello";
sw = foo();

Some tests that make sure that other use cases are not affected are available here

[2018-06-18 after reflector discussion]

Priority set to 2; status to LEWG

Proposed resolution:

This wording is relative to N4727.

  1. Change 20.4.2 [string.view.template], class template basic_string_view synopsis, as indicated:

    […]
    // 20.4.2.1 [string.view.cons], construction and assignment
    constexpr basic_string_view() noexcept;
    constexpr basic_string_view(const basic_string_view&) noexcept = default;
    constexpr basic_string_view& operator=(const basic_string_view&) noexcept = default;
    template <class A>
    basic_string_view& operator=(const basic_string<charT, traits, A>&&) = delete;
    constexpr basic_string_view(const charT* str);
    constexpr basic_string_view(const charT* str, size_type len);
    […]
    

3069(i). Move assigning variant's subobject corrupts data

Section: 19.7.3.3 [variant.assign] Status: New Submitter: Antony Polukhin Opened: 2018-02-20 Last modified: 2018-06-21

Priority: 3

View all other issues in [variant.assign].

View all issues with New status.

Discussion:

variant::emplace functions in 19.7.3.4 [variant.mod] destroy the currently contained value before initializing it to a new value. Assignments in 19.7.3.3 [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.

Proposed resolution:

This wording is relative to N4727.

  1. Change 19.7.3.3 [variant.assign] as indicated:

    variant& operator=(const variant& rhs);
    

    -1- Let j be rhs.index().

    -2- Effects:

    1. (2.1) — If neither *this nor rhs holds a value, there is no effect.

    2. (2.2) — Otherwise, if *this holds a value but rhs does not, destroys the value contained in *this and sets *this to not hold a value.

    3. (2.3) — Otherwise, if index() == j, assigns the value contained in rhs to the value contained in *this.

    4. (2.4) — Otherwise, if either is_nothrow_copy_constructible_v<Tj> is true or is_nothrow_move_constructible_v<Tj> is false, equivalent to emplace<j>(get<j>(rhs)).

    5. (2.5) — Otherwise, equivalent to emplace<j>(Tj{get<j>(rhs)})operator=(variant(rhs)).

    […]

    variant& operator=(variant&& rhs) noexcept(see below);
    

    -6- Let j be rhs.index().

    -7- Effects:

    1. (7.1) — If neither *this nor rhs holds a value, there is no effect.

    2. (7.2) — Otherwise, if *this holds a value but rhs does not, destroys the value contained in *this and sets *this to not hold a value.

    3. (7.3) — Otherwise, if index() == j, assigns get<j>(std::move(rhs)) to the value contained in *this.

    4. (7.4) — Otherwise, equivalent to emplace<j>(Tj{get<j>(std::move(rhs))}).

    […]

    
    

    -10- Let Tj be a type that is determined as follows: build an imaginary function FUN(Ti) for each alternative type Ti. The overload FUN(Tj) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative Tj which is the type of the contained value after assignment.

    -11- Effects:

    1. (11.1) — If *this holds a Tj, assigns std::forward<T>(t) to the value contained in *this.

    2. (11.2) — Otherwise, if is_nothrow_constructible_v<Tj, T> || !is_nothrow_move_constructible_v<Tj> is true, equivalent to emplace<j>(std::forward<T>(t)).

    3. (11.3) — Otherwise, equivalent to emplace<j>(Tj{std::forward<T>(t)})operator=(variant(std::forward<T>(t))).

    […]


3070(i). path::lexically_relative causes surprising results if a filename can also be a root-name

Section: 28.11.7.4.11 [fs.path.gen] Status: New Submitter: Billy O'Neal III Opened: 2018-02-23 Last modified: 2018-03-18

Priority: Not Prioritized

View all other issues in [fs.path.gen].

View all issues with New status.

Discussion:

path::lexically_relative constructs the resulting path with operator/=. If any of the filename elements from *this are themselves acceptable root-names, operator/= will destroy any previous value, and take that root_name(). For example:

path("/a:/b:").lexically_relative("/a:/c:")

On a POSIX implementation, this would return path("../b:"), but on a Windows implementation, the "b:" element is interpreted as a root-name, and clobbers the entire result path, giving path("b:"). We should detect this problematic condition and fail (by returning path()).

Proposed resolution:

This wording is relative to N4727.

  1. Change 28.11.7.4.11 [fs.path.gen] as indicated:

    path lexically_relative(const path& base) const;
    

    -3- […]

    -4- Effects: If root_name() != base.root_name() is true or is_absolute() != base.is_absolute() is true or !has_root_directory() && base.has_root_directory() is true or if any filename in relative_path() or base.relative_path() can be interpreted as a root-name, returns path(). [Note: On a POSIX implementation, no filename in a relative-path is acceptable as a root-nameend note] Determines the first mismatched element of *this and base as if by:

    auto [a, b] = mismatch(begin(), end(), base.begin(), base.end());
    

    Then,

    1. (4.1) — if a == end() and b == base.end(), returns path("."); otherwise

    2. (4.2) — let n be the number of filename elements in [b, base.end()) that are not dot or dot-dot minus the number that are dot-dot. If n < 0, returns path(); otherwise

    3. (4.3) — returns an object of class path that is default-constructed, followed by

      1. (4.3.1) — application of operator/=(path("..")) n times, and then

      2. (4.3.2) — application of operator/= for each element in [a, end()).


3072(i). [networking.ts] DynamicBuffer object lifetimes underspecified

Section: 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: 2018-06-18

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 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:

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.

  1. Edit 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer] as indicated:

    -3- In Table 14, x denotes a value of type X, x1 denotes a (possibly const) value of type X, andmx1 denotes an xvalue of type X, n denotes a (possibly const) value of type size_t, and u denotes 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 of mx1.size().
    • u.max_size() is equal to the prior value of mx1.max_size().
    • u.capacity() is equal to the prior value of mx1.capacity().
    • u.data() satisfies the ConstBufferSequence requirements (16.2.2 [buffer.reqmts.constbuffersequence]) as if copy constructed from the prior value of mx1.data().
    • All valid const or mutable buffer sequences that were previously obtained using mx1.data() or mx1.prepare() remain valid.
  2. Edit 17.6 [networking.ts::buffer.async.read] as indicated:

    -11- Let bd be the result of DECAY_COPY(b). Data is placed into the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) object bbd. A mutable buffer sequence (16.2.1 [buffer.reqmts.mutablebuffersequence]) is obtained prior to each read_some call using bd.prepare(N), where N is an unspecified value less than or equal to bd.max_size() - bd.size(). [Note: Implementations are encouraged to use bd.capacity() when determining N, to minimize the number of read_some calls performed on the stream. -- end note] After each read_some call, the implementation performs bd.commit(n), where n is the return value from read_some.

    […]

    -13- The synchronous read operation continues until:

    • bd.size() == bd.max_size(); or

    • the completion condition returns 0.

  3. Edit 17.8 [networking.ts::buffer.async.write] as indicated:

    -11- Let bd be the result of DECAY_COPY(b). Data is written from the dynamic buffer (16.2.4 [buffer.reqmts.dynamicbuffer]) object bd. A constant buffer sequence (16.2.2 [buffer.reqmts.constbuffersequence]) is obtained using bd.data(). After the data has been written to the stream, the implementation performs bd.consume(n), where n is the number of bytes successfully written.

    […]

    -13- The asynchronous write operation continues until:

    • bd.size() == 0; or

    • the completion condition returns 0.

  4. Edit 17.10 [networking.ts::buffer.async.read.until] as indicated:

    -3- Effects: Let bd be the result of DECAY_COPY(b). Initiates an asynchronous operation to read data from the buffer-oriented asynchronous read stream (17.1.2 [buffer.stream.reqmts.asyncreadstream]) object stream by 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]) object bd contain the specified delimiter delim.

    -4- Data is placed into the dynamic buffer object bd. A mutable buffer sequence (16.2.1 [buffer.reqmts.mutablebuffersequence]) is obtained prior to each async_read_some call using bd.prepare(N), where N is an unspecified value such that N <= max_size() - size(). [Note: Implementations are encouraged to use bd.capacity() when determining N, to minimize the number of asynchronous read_some operations performed on the stream. — end note] After the completion of each asynchronous read_some operation, the implementation performs bd.commit(n), where n is the value passed to the asynchronous read_some operation's completion handler.

    -5- The asynchronous read_until operation continues until:

    • the readable bytes of bd contain the delimiter delim; or

    • bd.size() == bd.max_size(); or

    • an asynchronous read_some operation fails.

    […]

    -8- On completion of the asynchronous operation, if the readable bytes of bd contain the delimiter, ec is set such that !ec is true. Otherwise, if bd.size() == bd.max_size(), ec is set such that ec == stream_errc::not_found. If bd.size() < bd.max_size(), ec is the error_code from the most recent asynchronous read_some operation. n is the number of readable bytes in bd up to and including the delimiter, if present, otherwise 0.


3073(i). [networking.ts] (async_)read and (async_)write don't support DynamicBuffer lvalues

Section: 17 [networking.ts::buffer.stream] Status: New Submitter: Christopher Kohlhoff Opened: 2018-02-27 Last modified: 2018-03-18

Priority: Not Prioritized

View all issues with New status.

Discussion:

Addresses: networking.ts

Suppose 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:

This can fixed by changing the test to is_dynamic_buffer_v<decay_t<DynamicBuffer>>.

Proposed resolution:

This wording is relative to N4711.

  1. 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>> is true.

  2. 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>> is true.

  3. 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>> is true.

  4. 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>> is true.


3077(i). (push|emplace)_back should invalidate the end iterator

Section: 21.3.11.5 [vector.modifiers] Status: New Submitter: Casey Carter Opened: 2018-03-10 Last modified: 2018-11-28

Priority: 3

View all other issues in [vector.modifiers].

View all issues with New status.

Discussion:

21.3.11.5 [vector.modifiers] paragraph 1 specifies that emplace_back and push_back do not invalidate iterators before the insertion point when reallocation is unnecessary:

Remarks: Causes reallocation if the new size is greater than the old capacity. Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. If no reallocation happens, all the iterators and references before the insertion point remain valid. […]
This statement is redundant, given the blanket wording in 21.2.1 [container.requirements.general] paragraph 12:
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.
It seems that this second sentence (1) should be a note that reminds us that the blanket wording applies here when no reallocation occurs, and/or (2) actually intends to specify that iterators at and after the insertion point are invalidated.

Also, it seems intended that reallocation should invalidate the end iterator as well.

[2018-06-18 after reflector discussion]

Priority set to 3

Previous resolution [SUPERSEDED]:

  1. Edit 21.3.11.5 [vector.modifiers] as indicated:

    -1- Remarks: Invalidates the past-the-end iterator. Causes reallocation if the new size is greater than the old capacity. Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. [Note: If no reallocation happens, all the iterators and references before the insertion point remain valid.end note] If an exception is thrown […]

[2018-11-28 Casey provides an updated P/R]

Per discussion in the prioritization thread on the reflector.

Proposed resolution:

This wording is relative to the post-San Diego working draft.

  1. Change 20.3.2.4 [string.capacity] as indicated:

    void shrink_to_fit();
    

    -11- Effects: shrink_­to_­fit is a non-binding request to reduce capacity() to size(). [ Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ] It does not increase capacity(), but may reduce capacity() by causing reallocation.

    -12- Complexity: If the size is not equal to the old capacity, linear in the size of the sequence; otherwise constant.

    -13- Remarks: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence, as well as the past-the-end iterator. [ Note: If no reallocation happens, they remain valid. end note ]

  2. Change 21.3.8.3 [deque.capacity] as indicated:

    void shrink_to_fit();
    

    -5- Requires: T shall be Cpp17MoveInsertable into *this.

    -6- Effects: shrink_­to_­fit is a non-binding request to reduce memory use but does not change the size of the sequence. [ Note: The request is non-binding to allow latitude for implementation-specific optimizations. —end note ] If the size is equal to the old capacity, or if an exception is thrown other than by the move constructor of a non-Cpp17CopyInsertable T, then there are no effects.

    -7- Complexity: If the size is not equal to the old capacity, linear in the size of the sequence; otherwise constant.

    -8- Remarks: shrink_to_fit If the size is not equal to the old capacity, then invalidates all the references, pointers, and iterators referring to the elements in the sequence, as well as the past-the-end iterator.

  3. Change 21.3.11.3 [vector.capacity] as indicated:

    void reserve(size_type n);
    

    […]

    -7- Remarks: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence, as well as the past-the-end iterator. [ Note: If no reallocation happens, they remain valid. — end note ] No reallocation shall take place during insertions that happen after a call to reserve() until the time when an insertion would make the size of the vector greater than the value of capacity().

    void shrink_to_fit();
    

    […]

    -10- Complexity: If reallocation happens, linear in the size of the sequence.

    -11- Remarks: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence, as well as the past-the-end iterator. [ Note: If no reallocation happens, they remain valid. end note ]

  4. Change 21.3.11.5 [vector.modifiers] as indicated:

    -1- Remarks: Causes reallocation if the new size is greater than the old capacity. Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence as well as the past-the-end iterator. If no reallocation happens, all the iterators and references then references, pointers, and iterators before the insertion point remain valid but those at or after the insertion point, including the past-the-end iterator, are invalidated. If an exception is thrown […]

    -2- Complexity: The complexity is If reallocation happens, linear in the number of elements of the resulting vector; otherwise linear in the number of elements inserted plus the distance to the end of the vector.


3078(i). directory_entry, directory_iterator and recursive_directory_iterator perform needless path copies

Section: 28.11.11 [fs.class.directory_entry], 28.11.12 [fs.class.directory_iterator], 28.11.13 [fs.class.rec.dir.itr] Status: New Submitter: Gor Nishanov Opened: 2018-03-05 Last modified: 2018-03-31

Priority: 3

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:


3081(i). Floating point from_chars API does not distinguish between overflow and underflow

Section: 19.19.3 [charconv.from.chars] Status: Open Submitter: Greg Falcon Opened: 2018-03-12 Last modified: 2018-08-27

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

Proposed resolution:

This wording is relative to N4727.

  1. Edit 19.19.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 member ptr of the return value points to the first character not matching the pattern, or has the value last if all characters match. If the parsed value is not in the range representable by the type of value, value is unmodified and the member ec of the return value is equal to errc::result_out_of_range. Otherwise, value is set to the parsed value, after rounding according to round_to_nearest (16.3.3.1 [round.style]), and the member ec is value-initialized.

    from_chars_result from_chars(const char* first, const char* last,
                                 see below& value, int base = 10);
    

    -2- Requires: base has 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 for strtol, except that no "0x" or "0X" prefix shall appear if the value of base is 16, and except that '-' is the only sign that may appear, and only if value has a signed type. On overflow, value is 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: fmt has the value of one of the enumerators of chars_format.

    -7- Effects: The pattern is the expected form of the subject sequence in the "C" locale, as described for strtod, except that

    1. (7.1) […]

    2. (7.2) […]

    3. (7.3) […]

    4. (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, value is set to plus or minus std::numeric_limits<T>::max() of the appropriate type. On underflow, value is set to a value with magnitude no greater than std::numeric_limits<T>::min().

    […]


3082(i). from_chars specification regarding floating point rounding is inconsistent

Section: 19.19.3 [charconv.from.chars] Status: Open Submitter: Greg Falcon Opened: 2018-03-12 Last modified: 2018-08-27

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

Proposed resolution:

This wording is relative to N4727.

  1. Edit 19.19.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: fmt has the value of one of the enumerators of chars_format.

    -7- Effects: The pattern is the expected form of the subject sequence in the "C" locale, as described for strtod, except that

    1. (7.1) […]

    2. (7.2) […]

    3. (7.3) […]

    4. (7.4) […]

    In any case, the resulting value is one of at most twothe floating-point values closest to the value of the string matching the pattern, with ties broken according to round_to_nearest.

    […]


3084(i). Termination in C++ is unclear

Section: 16.5 [support.start.term], 16.9.4 [exception.terminate] Status: New Submitter: JF Bastien Opened: 2018-03-15 Last modified: 2018-04-23

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:

What's unclear is:

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.]

Proposed resolution:


3085(i). char_traits::copy precondition too weak

Section: 20.2.1 [char.traits.require] Status: Open Submitter: Jonathan Wakely Opened: 2018-03-16 Last modified: 2018-08-27

Priority: 2

View all other issues in [char.traits.require].

View all issues with Open status.

Discussion:

Table 54, Character traits requirements, says that char_traits::move allows the ranges to overlap, but char_traits::copy requires that p is not in the range [s, s + n). This appears to be an attempt to map to the requirements of memmove and memcpy respectively, allowing those to be used to implement the functions, however the requirements for copy are weaker than those for memcpy. The C standard says for memcpy "If copying takes place between objects that overlap, the behavior is undefined" which is a stronger requirement than the start of the source range not being in the destination range.

All of libstdc++, libc++ and VC++ simply use memcpy for char_traits<char>::copy, resulting in undefined behaviour in this example:

char p[] = "abc";
char* s = p + 1;
std::char_traits<char>::copy(s, p, 2);
assert(std::char_traits<char>::compare(p, "aab", 3) == 0);

If the intention is to allow memcpy as a valid implementation then the precondition is wrong (unfortunately nobody realized this when fixing char_traits::move in LWG DR 7). If the intention is to require memmove then it is strange to have separate copy and move functions that both use memmove.

N.B. std::copy and std::copy_backward are not valid implementations of char_traits::copy either, due to different preconditions.

Changing the precondition implicitly applies to basic_string::copy (20.3.2.6.7 [string.copy]), and basic_string_view::copy (20.4.2.6 [string.view.ops]), which are currently required to support partially overlapping ranges:

std::string s = "abc";
s.copy(s.data() + 1, s.length() - 1);
assert(s == "aab");

[2018-04-03 Priority set to 2 after discussion on the reflector.]

[2018-08-23 Batavia Issues processing]

No consensus for direction; revisit in San Diego. Status to Open.

Proposed resolution:

This wording is relative to N4727.

Option A:

  1. Edit 20.2.1 [char.traits.require], Table 54 — "Character traits requirements", as indicated:

    Table 54 — Character traits requirements
    Expression Return type Assertion/note
    pre/post-condition
    Complexity
    […]
    X::copy(s,p,n) X::char_type* Requires: p not in [s,s+n)The ranges [p,p+n)
    and [s,s+n) do not overlap
    .
    Returns: s.
    for each i in [0,n), performs
    X::assign(s[i],p[i]).
    linear
    […]

Option B:

NAD (i.e. implementations need to be fixed, in practice char_traits::copy and char_traits::move might be equivalent).


3086(i). Possible problem in §[new.delete.single]

Section: 16.6.2.1 [new.delete.single] Status: New Submitter: William M. Miller Opened: 2018-03-16 Last modified: 2018-06-18

Priority: 3

View other active issues in [new.delete.single].

View all other issues in [new.delete.single].

View all issues with New status.

Discussion:

In general requirements on a whole program, as opposed to a single translation unit, generally specify "no diagnostic required", since we don't want to require implementations to do multi-translation-unit analysis. However, 16.6.2.1 [new.delete.single] paragraph 11 says,

If a function with a size parameter is defined, the program shall also define the corresponding version without the size parameter.

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.

  1. Edit 16.6.2.1 [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 size parameter is defined, the program should also define the corresponding function with a size parameter. If a function with a size parameter is defined, the program shall also define the corresponding version without the size parameter; 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]

  2. Edit 16.6.2.2 [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 size parameter is defined, the program should also define the corresponding function with a size parameter. If a function with a size parameter is defined, the program shall also define the corresponding version without the size parameter; 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]


3087(i). One final &x in §[list.ops]

Section: 21.3.10.5 [list.ops] Status: Tentatively Ready Submitter: Tim Song Opened: 2018-03-19 Last modified: 2018-11-11

Priority: 3

View other active issues in [list.ops].

View all other issues in [list.ops].

View all issues with Tentatively Ready status.

Discussion:

LWG 3017 missed an instance of &x in 21.3.10.5 [list.ops] p14.

[2018-06-18 after reflector discussion]

Priority set to 3

[2018-10-15 Status to Tentatively Ready after seven positive votes on the reflector.]

Proposed resolution:

This wording is relative to N4727.

  1. Edit 21.3.10.5 [list.ops] as indicated:

    void splice(const_iterator position, list& x, const_iterator first,
                const_iterator last);
    void splice(const_iterator position, list&& x, const_iterator first,
                const_iterator last);
    

    -11- Requires: […]

    -12- Effects: […]

    -13- Throws: Nothing.

    -14- Complexity: Constant time if &addressof(x) == this; otherwise, linear time.


3088(i). forward_list::merge behavior unclear when passed *this

Section: 21.3.9.6 [forwardlist.ops] Status: New Submitter: Tim Song Opened: 2018-03-19 Last modified: 2018-06-18

Priority: 3

View all other issues in [forwardlist.ops].

View all issues with New status.

Discussion:

LWG 300 changed list::merge to be a no-op when passed *this, but there's no equivalent rule for forward_list::merge. Presumably the forward_list proposal predated the adoption of LWG 300's PR and was never updated for the change. Everything in the discussion of that issue applies mutatis mutandis to the current specification of forward_list::merge.

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4727.

  1. Edit 21.3.9.6 [forwardlist.ops] as indicated:

    void merge(forward_list& x);
    void merge(forward_list&& x);
    template<class Compare> void merge(forward_list& x, Compare comp);
    template<class Compare> void merge(forward_list&& x, Compare comp);
    

    -20- Requires: *this and x are both sorted with respect to the comparator operator< (for the first two overloads) or comp (for the last two overloads), and get_allocator() == x.get_allocator() is true.

    -21- Effects: If addressof(x) == this, does nothing. Otherwise, mMerges the two sorted ranges [begin(), end()) and [x.begin(), x.end()). The result is a range that is sorted with respect to the comparator operator< (for the first two overloads) or comp (for the last two overloads). x is empty after the merge. If an exception is thrown other than by a comparison there are no effects. Pointers and references to the moved elements of x now refer to those same elements but as members of *this. Iterators referring to the moved elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.

    -22- Remarks: Stable (15.5.5.7 [algorithm.stable]). The behavior is undefined if get_allocator() != x.get_allocator().

    -23- Complexity: At most distance(begin(), end()) + distance(x.begin(), x.end()) - 1 comparisons if addressof(x) != this; otherwise, no comparisons are performed.


3089(i). copy_n should require non-overlapping ranges

Section: 24.6.1 [alg.copy] Status: New Submitter: Marshall Clow Opened: 2018-03-21 Last modified: 2018-06-18

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: result shall 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: result shall 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 is a similar issue for char_traits::copy.

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4727.

  1. Edit 24.6.1 [alg.copy] as indicated:

    [Drafting note: I'm using the permission in 24.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: result shall 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 n assignments.


3090(i). What is §[time.duration.cons]p4's "no overflow is induced in the conversion" intended to mean?

Section: 26.5.1 [time.duration.cons] Status: New Submitter: Richard Smith Opened: 2018-03-22 Last modified: 2018-06-18

Priority: 3

View other active issues in [time.duration.cons].

View all other issues in [time.duration.cons].

View all issues with New status.

Discussion:

26.5.1 [time.duration.cons] p4 says:

template<class Rep2, class Period2>
  constexpr duration(const duration<Rep2, Period2>& d);

Remarks: This constructor shall not participate in overload resolution unless no overflow is induced in the conversion and treat_as_floating_point_v<rep> is true or both ratio_divide<Period2, period>::den is 1 and treat_as_floating_point_v<Rep2> is false.

with this example:

duration<int, milli> ms(3);
duration<int, micro> us = ms;  // OK
duration<int, milli> ms2 = us; // error

It's unclear to me what "no overflow is induced in the conversion" means in the above. What happens here:

duration<int, milli> ms(INT_MAX);
duration<int, micro> us = ms;  // ???

An overflow is clearly induced in the conversion here: internally, we'll multiply INT_MAX by 1000. But that cannot be determined statically (in general), and so can't affect the result of overload resolution.

So what's actually supposed to happen? Are we actually just supposed to check that Rep2 is no larger than Rep? (If so, what happens on overflow? Undefined behavior?)

It has been pointed out by Howard Hinnant:

This refers to the compile-time conversion factor to convert Period2 to Period. If that conversion factor is not representable as a (reduced) ratio<N, D>, then the constructor is SFINAE'd out. This might happen (for example) converting years to picoseconds.

I would not have guessed that from the wording. Maybe replacing "no overflow is induced in the conversion" with "the result of ratio_divide<Period2, Period> is representable as a ratio" or similar would help?

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:


3091(i). subsecond-precision time_of_day and durations that seconds cannot convert to

Section: 26.9.5 [time.tod.subsecond] Status: New Submitter: Richard Smith Opened: 2018-03-24 Last modified: 2018-04-23

Priority: 2

View all issues with New status.

Discussion:

What should happen here:

const int bpm = 100;
using beats = duration<int, ratio<60, 100>>;
auto v = time_of_day<beats>(beats{2}).subseconds();

? 2 beats at 100bpm is 1.2 seconds. The time_of_day constructor specification says:

seconds() returns the integral number of seconds since_midnight is after (00:00:00 + hours() + minutes()). subseconds() returns the integral number of fractional precision seconds since_midnight is after (00:00:00 + hours() + minutes() + seconds()).

But that's impossible. If seconds() returns 1, we need to return a subseconds() value representing 0.2s of type precision, but type precision can only represent multiples of 0.6s.

Should this time_of_day specialization only be available for the case where seconds is convertible to precision? Or should the precision type used by this specialization be common_type_t<seconds, duration<Rep, Period>> rather than merely duration<Rep, Period>?

Either way I think we need a wording update to specify one of those two behaviors.

[2018-04-09 Priority set to 2 after discussion on the reflector.]

Proposed resolution:


3092(i). Unclear semantics of enum class bitmask types

Section: 15.4.2.1.4 [bitmask.types] Status: Open Submitter: Geoffrey Romer Opened: 2018-03-26 Last modified: 2018-08-27

Priority: 2

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.

Proposed resolution:

This wording is relative to N4727.

  1. Edit 15.4.2.1.4 [bitmask.types] as indicated:

    -2- The bitmask type bitmask can 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, E may represent either enum or enum class (the choice is implementation-defined unless otherwise specified), and the names C0, C1, etc. represent bitmask elements for this particular bitmask type. The zero value bitmask{} is used to represent an empty bitmask, in which no bitmask elements are set. All suchbitmask elements have distinct, nonzero values such that, for any pair Ci and Cj where ij, Ci & Ci is nonzero and Ci & Cj is zero. Additionally, the value 0 is used to represent an empty bitmask, in which no bitmask elements are set.


3093(i). LWG 2294/2192 missed a std::abs overload

Section: 25.8.2 [c.math.abs], 16.2.2 [cstdlib.syn], 28.12.2 [cinttypes.syn] Status: New Submitter: Richard Smith Opened: 2018-03-30 Last modified: 2018-06-18

Priority: 3

View all issues with New status.

Discussion:

LWG 2192 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). 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:


3095(i). strstreambuf refers to nonexistent member of fpos, fpos::offset

Section: D.8.2.3 [depr.strstreambuf.virtuals] Status: New Submitter: Billy O'Neal III Opened: 2018-04-04 Last modified: 2018-06-18

Priority: 4

View all other issues in [depr.strstreambuf.virtuals].

View all issues with New status.

Discussion:

strstreambuf refers to a nonexistent member function of fpos in the specification of the member function seekpos, D.8.2.3 [depr.strstreambuf.virtuals]/18 (emphasize mine):

For a sequence to be positioned, if its next pointer is a null pointer, the positioning operation fails. Otherwise, the function determines newoff from sp.offset():

The intent is clearly to get the corresponding streamoff from the fpos, as p19 says "the resultant offset newoff (of type off_type)". The mechanism to make that conversion is a normal explicit conversion, as indicated in the last row of the table in [fpos.operations].

[2018-06-18 after reflector discussion]

Priority set to 4

Proposed resolution:

This wording is relative to N4727.

  1. Edit D.8.2.3 [depr.strstreambuf.virtuals] as indicated:

    pos_type seekpos(pos_type sp, ios_base::openmode which
                     = ios_base::in | ios_base::out) override;
    

    -17- Effects: […]

    -18- For a sequence to be positioned, if its next pointer is a null pointer, the positioning operation fails. Otherwise, the function determines newoff from static_cast<off_type>(sp).offset():

    […]


3097(i). basic_stringbuf seekoff effects trigger undefined behavior and have contradictory returns

Section: 28.8.2.4 [stringbuf.virtuals] Status: New Submitter: Billy O'Neal III Opened: 2018-04-07 Last modified: 2018-04-23

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:


3098(i). Misleading example for filesystem::path::filename()

Section: 28.11.7.4.9 [fs.path.decompose] Status: New Submitter: Jonathan Wakely Opened: 2018-04-06 Last modified: 2018-06-18

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.

  1. Edit 28.11.7.4.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.

  1. Edit 28.11.7.4.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]


3099(i). is_assignable<Incomplete&, Incomplete&>

Section: 19.15.2 [meta.type.synop] Status: New Submitter: Casey Carter Opened: 2018-04-10 Last modified: 2018-08-27

Priority: Not Prioritized

View other active issues in [meta.type.synop].

View all other issues in [meta.type.synop].

View all issues with New status.

Discussion:

LWG 2939 suggests that the the preconditions of the type traits need reevaluation. This issue focuses specifically on is_assignable and, by extension, its variants:

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, 2939, 3022, and 3099 are all closely related. Walter to write a paper resolving them.

Proposed resolution:

This wording is relative to N4741.

  1. In 19.15.4.3 [meta.unary.prop] Table 42, change the Precondition text for is_assignable, is_trivially_assignable, and is_nothrow_assignable as follows:
    remove_cvref_t<T> and remove_cvref_t<U> shall be complete types, cv void, or arrays of unknown bound.
  2. In 19.15.4.3 [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, and is_nothrow_move_assignable as follows:
    remove_cvref_t<T> shall be a complete type, cv void, or an array of unknown bound.

3101(i). span's Container constructors need another constraint

Section: 21.7.3.2 [span.cons] Status: Tentatively Ready Submitter: Stephan T. Lavavej Opened: 2018-04-12 Last modified: 2018-11-11

Priority: 1

View all other issues in [span.cons].

View all issues with Tentatively Ready status.

Discussion:

When I overhauled span's constructor constraints, I was careful about the built-in array, std::array, and converting span constructors. These types contain bounds information, so we can achieve safety at compile-time by permitting implicit conversions if and only if the destination extent is dynamic (this accepts anything by recording the size at runtime) or the source and destination extents are identical. However, I missed the fact that the Container constructors are the opposite case. A Container (e.g. a vector) has a size that's known only at runtime. It's safe to convert this to a span with dynamic_extent, but for consistency and safety, this shouldn't implicitly convert to a span with fixed extent. (The more verbose (ptr, count) and (first, last) constructors are available to construct fixed extent spans from runtime-length ranges. Note that debug precondition checks are equally possible with the Container and (ptr, count)/(first, last) constructors. The issue is that implicit conversions are notoriously problematic, so they should be permitted only when they are absolutely known to be safe.)

[2018-04-24 Priority set to 1 after discussion on the reflector.]

[2018-06 Rapperswil Thursday issues processing]

Status to LEWG. Should this be ill-formed, or fail at runtime if the container is too small? Discussion on the reflector here.

[2018-11 San Diego Saturday]

LEWG said that they're fine with the proposed resolution. Status to Tentatively Ready.

Proposed resolution:

This wording is relative to N4741.

  1. Edit 21.7.3.2 [span.cons] as indicated:

    template<class Container> constexpr span(Container& cont);
    template<class Container> constexpr span(const Container& cont);
    

    -14- Requires: [data(cont), data(cont) + size(cont)) shall be a valid range. If extent is not equal to dynamic_extent, then size(cont) shall be equal to extent.

    -15- Effects: Constructs a span that is a view over the range [data(cont), data(cont) + size(cont)).

    -16- Postconditions: size() == size(cont) && data() == data(cont).

    -17- Throws: What and when data(cont) and size(cont) throw.

    -18- Remarks: These constructors shall not participate in overload resolution unless:

    1. (18.?) — extent == dynamic_extent,

    2. (18.1) — Container is not a specialization of span,

    3. (18.2) — Container is not a specialization of array,

    4. […]


3103(i). Errors in taking subview of span should be ill-formed where possible

Section: 21.7.3.3 [span.sub] Status: Open Submitter: Tomasz Kamiński Opened: 2018-04-13 Last modified: 2018-11-25

Priority: 3

View all issues with Open status.

Discussion:

Currently all out-of-bound/inputs errors in the functions taking an subview of span lead to undefined behavior, even in the situation when they could be detected at compile time. This is inconsistent with the behavior of the span constructors, which make similar constructs ill-formed.

Furthermore, with the current specification of the subspan function, the following invocation:

span<T, N> s;   // N > 0
s.subspan<O>(); // with O > 0

is ill-formed when O > N + 1, as the return of the function is span<T, K> with K < -1. However in case when O == N + 1, runtime sized span is returned (span<T, -1>) instead and the behavior of the function is undefined.

Firstly, for either run time sized (N == dynamic_extent) and fixed sized (N > 0) object s of type span<T, N>, the following constructs should be ill-formed, instead of having undefined behavior:

  1. s.first<C>() with C < 0

  2. s.last<C>() with C < 0

  3. s.subspan<O, E> with O < 0 or E < 0 and E != dynamic_extent.

This would follow span specification, that make instantiation of span<T, N> ill-formed for N < 0 and N != dynamic_extent.

In addition the following constructs should be made ill-formed for fixed size span s of type span<T, N> (with N > 0):

  1. s.first<C>() with C > N

  2. s.last<C>() with C > N

  3. s.subspan<O, dynamic_extent>() with O > N

  4. s.subspan<O, C>() with O + C > N

This will match the span constructor that made construction of fixed size span<T, N> from fixed size span of different size ill-formed.

[2018-04-24 Priority set to 3 after discussion on the reflector.]

[2018-11 San Diego Thursday night issue processing]

Tomasz to provide updated wording.

Previous resolution: [SUPERSEDED]

This wording is relative to N4741.

  1. Edit 21.7.3.3 [span.sub] as indicated:

    template<ptrdiff_t Count> constexpr span<element_type, Count> first() const;
    

    -?- Remarks: If Count < 0 || (Extent != dynamic_extent && Count > Extent), the program is ill-formed.

    -1- Requires: 0 <= Count && Count <= size().

    -2- Effects: Equivalent to: return {data(), Count};

    template<ptrdiff_t Count> constexpr span<element_type, Count> last() const;
    

    -?- Remarks: If Count < 0 || (Extent != dynamic_extent && Count > Extent), the program is ill-formed.

    -3- Requires: 0 <= Count && Count <= size().

    -4- Effects: Equivalent to: return {data() + (size() - Count), Count};

    template<ptrdiff_t Offset, ptrdiff_t Count = dynamic_extent>
      constexpr span<element_type, see below> subspan() const;
    

    -?- Remarks: The program is ill-formed if:

    • Offset < 0 || (Count < 0 && Count != dynamic_extent), or

    • Extend != dynamic_extent && (Offset > Extent || (Count != dynamic_extent && Offset + Count > Extent)).

    -5- Requires: (0 <= Offset && Offset <= size()) && (Count == dynamic_extent || Count >= 0 && Offset + Count <= size()).

    -6- Effects: Equivalent to: return span<ElementType, see below>( data() + Offset, Count != dynamic_extent ? Count : size() - Offset);

    -7- Remarks: The second template argument of the returned span type is:

    Count != dynamic_extent ? Count
                            : (Extent != dynamic_extent ? Extent - Offset
                                                        : dynamic_extent)
    

[2018-11-09; Tomasz provides updated wording]

I have decided to replace all Requires: elements in the section 21.7.3.3 [span.sub] to preserve consistency.

Proposed resolution:

This wording is relative to N4778.

  1. Edit 21.7.3.3 [span.sub] as indicated:

    template<ptrdiff_t Count> constexpr span<element_type, Count> first() const;
    

    -?- Mandates: Count >= 0 && (Extent == dynamic_extent || Count <= Extent).

    -1- RequiresExpects: 0 <= Count && Count <= size().

    -2- Effects: Equivalent to: return {data(), Count};

    template<ptrdiff_t Count> constexpr span<element_type, Count> last() const;
    

    -?- Mandates: Count >= 0 && (Extent == dynamic_extent || Count <= Extent).

    -3- RequiresExpects: 0 <= Count && Count <= size().

    -4- Effects: Equivalent to: return {data() + (size() - Count), Count};

    template<ptrdiff_t Offset, ptrdiff_t Count = dynamic_extent>
      constexpr span<element_type, see below> subspan() const;
    

    -?- Mandates: Offset >= 0 && (Count >= 0 || Count == dynamic_extent) && (Extent == dynamic_extent || (Offset <= Extent && (Count == dynamic_extent || Offset + Count <= Extent))).

    -5- RequiresExpects: (0 <= Offset && Offset <= size()) && (Count == dynamic_extent || Count >= 0 && Offset + Count <= size()).

    -6- Effects: Equivalent to: return span<ElementType, see below>( data() + Offset, Count != dynamic_extent ? Count : size() - Offset);

    -7- Remarks: The second template argument of the returned span type is:

    Count != dynamic_extent ? Count
                            : (Extent != dynamic_extent ? Extent - Offset
                                                        : dynamic_extent)
    
    constexpr span<element_type, dynamic_extent> first(index_type count) const;
    

    -8- RequiresExpects: 0 <= count && count <= size().

    -9- Effects: Equivalent to: return {data(), count};

    constexpr span<element_type, dynamic_extent> last(index_type count) const;
    

    -10- RequiresExpects: 0 <= count && count <= size().

    -11- Effects: Equivalent to: return {data() + (size() - count), count};

    constexpr span<element_type, dynamic_extent> subspan(
      index_type offset, index_type count = dynamic_extent) const;
    

    -12- RequiresExpects: (0 <= offset && offset <= size()) && (count == dynamic_extent || count >= 0 && offset + count <= size())

    -13- Effects: Equivalent to: return {data() + offset, count == dynamic_extent ? size() - offset : count};


3105(i). T1 is convertible to T2

Section: 15 [library] Status: New Submitter: Jens Maurer Opened: 2018-04-24 Last modified: 2018-06-18

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

Proposed resolution:


3106(i). nothrow should be inline constexpr rather that extern const

Section: 16.6.1 [new.syn] Status: Tentatively NAD Submitter: Antony Polukhin Opened: 2018-04-25 Last modified: 2018-08-27

Priority: 2

View all issues with Tentatively NAD status.

Discussion:

std::nothrow in [new.syn] is declared as 'extern const nothrow_t nothrow;'. Unfortunately this declaration requires linking with Standard Library (which does not work well on GCC with -nostdlib flag). Moreover the declaration differs from other Standard Library tags that are 'inline constexpr'.

[2018-06-18 after reflector discussion]

Priority set to 2

[2018-08-23 Batavia Issues processing]

Status to Tentatively NAD - if we had a status 'Wont Fix' that would be correct.

This is an ABI break for most implementations, and people can define their own variable of type std::nothrow_t if they want a constexpr one.

Proposed resolution:

This wording is relative to N4741.

  1. Edit 16.6.1 [new.syn] as indicated:

    struct nothrow_t { explicit nothrow_t() = default; };
    extern constinline constexpr nothrow_t nothrow;
    

3107(i). istreambuf_iterator has public exposition-only member

Section: 22.6.3 [istreambuf.iterator] Status: New Submitter: Billy O'Neal III Opened: 2018-04-26 Last modified: 2018-06-18

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.

  1. Edit 22.6.3 [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
      };
      […]
    }
    

3108(i). istreambuf_iterator::proxy::operator* should be const

Section: 22.6.3.1 [istreambuf.iterator.proxy] Status: New Submitter: Billy O'Neal III Opened: 2018-04-26 Last modified: 2018-06-18

Priority: 3

View all issues with New status.

Discussion:

operator* on iterators is usually intended to be const; see 22.3.5.2 [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.

  1. Edit 22.6.3.1 [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_; }
      };
    }
    

3109(i). strstreambuf is copyable

Section: D.8.2 [depr.strstreambuf] Status: New Submitter: Jonathan Wakely Opened: 2018-05-02 Last modified: 2018-06-18

Priority: 4

View all issues with New status.

Discussion:

In C++03 strstreambuf was not copyable, because basic_streambuf wasn't copyable. In C++11 we made basic_streambuf copyable by derived classes, and strstreambuf doesn't define any special members, so it (unintentionally?) became copyable, with completely unspecified semantics.

VC++ and libc++ make it movable not copyable, and libstdc++ still follows C++03, so it's neither movable nor copyable. Making it movable seems to be the sane option, and consistent with filebuf and stringbuf.

[2018-06-18 after reflector discussion]

Priority set to 4

Proposed resolution:


3110(i). Contiguous Iterators should always be Random-Access

Section: 22.3.1 [iterator.requirements.general] Status: New Submitter: Marc Aldorasi Opened: 2018-05-07 Last modified: 2018-06-18

Priority: 3

View other active issues in [iterator.requirements.general].

View all other issues in [iterator.requirements.general].

View all issues with New status.

Discussion:

In [iterator.requirements.general] paragraph 6, contiguous iterators are defined in terms of general iterators, not random-access iterators. Since the defining expressions require random-access and the original paper's introduction describes contiguous iterators as a refinement of random-access iterators, contiguous iterators should be defined in terms of random-access iterators.

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4741.

  1. Edit 22.3.1 [iterator.requirements.general] as indicated:

    -6- Random-access iIterators that further satisfy the requirement that, for integral values n and dereferenceable iterator values a and (a + n), *(a + n) is equivalent to *(addressof(*a) + n), are called contiguous iterators. [Note: For example, the type "pointer to int" is a contiguous iterator, but reverse_iterator<int *> is not. For a valid iterator range [a, b) with dereferenceable a, the corresponding range denoted by pointers is [addressof(*a), addressof(*a) + (b - a)); b might not be dereferenceable. — end note]


3112(i). system_error and filesystem_error constructors taking a string may not be able to meet their postconditions

Section: 18.5.7.2 [syserr.syserr.members], 28.11.8.1 [fs.filesystem_error.members] Status: New Submitter: Tim Song Opened: 2018-05-10 Last modified: 2018-06-11

Priority: Not Prioritized

View other active issues in [syserr.syserr.members].

View all other issues in [syserr.syserr.members].

View all issues with New status.

Discussion:

The constructors of system_error and filesystem_error taking a std::string what_arg are specified to have a postcondition of string(what()).find(what_arg) != string::npos (or the equivalent with string_view). This is not possible if what_arg contains an embedded null character.

Proposed resolution:

This wording is relative to N4727.

Drafting note: This contains a drive-by editorial change to use string_view for these postconditions rather than string.

  1. Edit 18.5.7.2 [syserr.syserr.members] p1-4 as indicated:

    system_error(error_code ec, const string& what_arg);
    

    -1- Effects: Constructs an object of class system_error.

    -2- Postconditions: code() == ec and string_view(what()).find(what_arg.c_str()) != string_view::npos.

    system_error(error_code ec, const char* what_arg);
    

    -3- Effects: Constructs an object of class system_error.

    -4- Postconditions: code() == ec and string_view(what()).find(what_arg) != string_view::npos.

  2. Edit 18.5.7.2 [syserr.syserr.members] p7-10 as indicated:

    system_error(int ev, const error_category& ecat, const std::string& what_arg);
    

    -7- Effects: Constructs an object of class system_error.

    -8- Postconditions: code() == error_code(ev, ecat) and string_view(what()).find(what_arg.c_str()) != string_view::npos.

    system_error(int ev, const error_category& ecat, const char* what_arg);
    

    -9- Effects: Constructs an object of class system_error.

    -10- Postconditions: code() == error_code(ev, ecat) and string_view(what()).find(what_arg) != string_view::npos.

  3. Edit 28.11.8.1 [fs.filesystem_error.members] p2-4 as indicated:

    filesystem_error(const string& what_arg, error_code ec);
    

    -2- Postconditions:

    • code() == ec,
    • path1().empty() == true,
    • path2().empty() == true, and
    • string_view(what()).find(what_arg.c_str()) != string_view::npos.
    filesystem_error(const string& what_arg, const path& p1, error_code ec);
    

    -3- Postconditions:

    • code() == ec,
    • path1() returns a reference to the stored copy of p1,
    • path2().empty() == true, and
    • string_view(what()).find(what_arg.c_str()) != string_view::npos.
    filesystem_error(const string& what_arg, const path& p1, const path& p2, error_code ec);
    

    -4- Postconditions:

    • code() == ec,
    • path1() returns a reference to the stored copy of p1,
    • path2() returns a reference to the stored copy of p2,
    • string_view(what()).find(what_arg.c_str()) != string_view::npos.

3113(i). polymorphic_allocator::construct() should more closely match scoped_allocator_adaptor::construct()

Section: 19.12.3.2 [mem.poly.allocator.mem] Status: New Submitter: Arthur O'Dwyer Opened: 2018-05-14 Last modified: 2018-06-18

Priority: 3

View all other issues in [mem.poly.allocator.mem].

View all issues with New status.

Discussion:

Based on my new understanding of how uses_allocator and is_constructible are supposed to play together, I think there was a minor defect in the resolution of LWG 2969 "polymorphic_allocator::construct() shouldn't pass resource()".

The calls to is_constructible in 19.12.3.2 [mem.poly.allocator.mem] (10.2), which were added to resolve LWG 2969, are missing an lvalue-qualification to match the lvalue-qualification of the parallel calls in [llocator.adaptor.member] (9.2).

The latter talks about constructibility from inner_allocator_type&; the former (after LWG 2969) talks about constructibility from polymorphic_allocator with no ref-qualification. But since we are perfect-forwarding *this through a tuple of references, we definitely are going to try to construct the target object from a polymorphic_allocator& and not from a polymorphic_allocator. I believe that the wording in 19.12.3.2 [mem.poly.allocator.mem] (10.2) needs to be updated to make the program ill-formed in cases where that construction is going to fail.

Orthogonally, I believe we need additional std::move's in the sentence following 19.12.3.2 [mem.poly.allocator.mem] (10.8) for two reasons:

[2018-06-18 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4750.

  1. Edit 19.12.3.2 [mem.poly.allocator.mem] as indicated:

    template<class T1, class T2, class... Args1, class... Args2>
      void construct(pair<T1, T2>* p, piecewise_construct_t, tuple<Args1...> x, tuple<Args2...> y);
    

    -9- […]

    -10- Effects:: […]

    1. (10.1) — […]

    2. (10.2) — Otherwise, if uses_allocator_v<T1,polymorphic_allocator> is true and is_constructible_v<T1, allocator_arg_t, polymorphic_allocator&, Args1...> is true, then xprime is tuple_cat(make_tupletuple<allocator_arg_t, polymorphic_allocator&>(allocator_arg, *this), std::move(x)).

    3. (10.3) — Otherwise, if uses_allocator_v<T1, polymorphic_allocator> is true and is_constructible_v<T1, Args1..., polymorphic_allocator&> is true, then xprime is tuple_cat(std::move(x), make_tupletuple<polymorphic_allocator&>(*this)).

    4. (10.4) — Otherwise the program is ill formed.

    Let yprime be a tuple constructed from y according to the appropriate rule from the following list:

    1. (10.5) — […]

    2. (10.6) — Otherwise, if uses_allocator_v<T2, polymorphic_allocator> is true and is_constructible_v<T2, allocator_arg_t, polymorphic_allocator&, Args2...> is true, then yprime is tuple_cat(make_tupletuple<allocator_arg_t, polymorphic_allocator&>(allocator_arg, *this), std::move(y)).

    3. (10.7) — Otherwise, if uses_allocator_v<T2, polymorphic_allocator> is true and is_constructible_v<T2, Args2..., polymorphic_allocator&> is true, then yprime is tuple_cat(std::move(y), make_tupletuple<polymorphic_allocator&>(*this)).

    4. (10.8) — Otherwise the program is ill formed.

    Then, using piecewise_construct, std::move(xprime), and std::move(yprime) as the constructor arguments, this function constructs a pair<T1, T2> object in the storage whose address is represented by p.


3114(i). [networking.ts] Permit efficient composition when using DynamicBuffer

Section: 16 [networking.ts::buffer], 17 [networking.ts::buffer.stream] Status: LEWG Submitter: Vinnie Falco Opened: 2018-05-18 Last modified: 2018-06-18

Priority: Not Prioritized

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.

Proposed resolution:

This wording is relative to N4734.

[Drafting note: The project editor is kindly asked to replace all occurrences of DynamicBuffer&& with DynamicBuffer& as indicated by the provided wording changes below. — end drafting note]

  1. 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);
    
    […]
    
  2. Modify 16.2.4 [networking.ts::buffer.reqmts.dynamicbuffer], as indicated:

    -1- […]

    -2- A type X meets the DynamicBuffer requirements if it satisfies the requirements of Destructible (C++ 2014 [destructible]) and MoveConstructible (C++ 2014 [moveconstructible]), as well as the additional requirements listed in Table 14.

  3. 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 only
      const size_t max_size_; // exposition only
    };
    
    […]
    

    -2- […]

    -3- […]

    explicit dynamic_vector_buffer(size_t maximum_size)
    

    -?- Effects: Default-constructs vec_. Initializes size_ with 0, and max_size_ with maximum_size.

    explicit dynamic_vector_buffer(vector<T, Allocator>& vec) noexcept
    

    -4- Effects: Initializes vec_ with move(vec), size_ with vec_.size(), and max_size_ with vec_.max_size()

    dynamic_vector_buffer(vector<T, Allocator>& vec,
                          size_t maximum_size) noexcept;
    

    -5- Requires: vec.size() <= maximum_size

    -6- Effects: Initializes vec_ with move(vec), size_ with vec_.size(), and max_size_ with maximum_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_).

  4. 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 only
      const size_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_. Initializes size_ with 0, and max_size_ with maximum_size.

    […]

    explicit basic_dynamic_string_buffer(basic_string<CharT, Traits, Allocator>& str) noexcept
    

    -4- Effects: Initializes str_ with move(str), size_ with str_.size(), and max_size_ with str_.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_ with move(str), size_ with str_.size(), and max_size_ with maximum_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_).

  5. Remove 16.14 [networking.ts::buffer.dynamic.creation] entirely

  6. 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);
    
    […]
    
  7. 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 AsyncReadStream object stream and the DynamicBuffer object b are is valid until the completion handler for the asynchronous operation is invoked.

  8. 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);
    
    […]
    
  9. 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 AsyncWriteStream object stream and the DynamicBuffer object b memory associated with the dynamic buffer b are valid until the completion handler for the asynchronous operation is invoked.

  10. 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);
    
    […]
    
  11. 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 AsyncReadStream object stream and the DynamicBuffer object b are is valid until the completion handler for the asynchronous operation is invoked.


3117(i). Missing packaged_task deduction guides

Section: 31.6.10 [futures.task] Status: New Submitter: Marc Mutz Opened: 2018-06-08 Last modified: 2018-06-24

Priority: 3

View all other issues in [futures.task].

View all issues with New status.

Discussion:

std::function has deduction guides, but std::packaged_task, which is otherwise very similar, does not. This is surprising to users and I can think of no reason for the former to be treated differently from the latter. I therefore propose to add deduction guides for packaged task with the same semantics as the existing ones for function.

[2018-06-23 after reflector discussion]

Priority set to 3

Proposed resolution:

This wording is relative to N4750.

  1. Modify 31.6.10 [futures.task], class template packaged_task synopsis, as indicated:

    namespace std {
      […]
      template<class R, class... ArgTypes>
      class packaged_task<R(ArgTypes...)> {
        […]
      };
      
      template<class R, class... ArgTypes>
      packaged_task(R (*)( ArgTypes ...)) -> packaged_task<R( ArgTypes...)>;
    
      template<class F> packaged_task(F) -> packaged_task<see below>;
      
      template<class R, class... ArgTypes>
        void swap(packaged_task<R(ArgTypes...)>& x, packaged_task<R(ArgTypes...)>& y) noexcept;
    }
    
  2. Modify 31.6.10.1 [futures.task.members] as indicated:

    template<class F>
      packaged_task(F&& f);
    
    […]
    template<class F> packaged_task(F) -> packaged_task<see below>;
    

    -?- Remarks: This deduction guide participates in overload resolution only if &F::operator() is well-formed when treated as an unevaluated operand. In that case, if decltype(&F::operator()) is of the form R(G::*)(A...) cv &opt noexceptopt for a class type G, then the deduced type is packaged_task<R(A...)>.

    […]
    packaged_task(packaged_task&& rhs) noexcept;
    

3118(i). fpos equality comparison unspecified

Section: 28.5.4.2 [fpos.operations] Status: New Submitter: Jonathan Wakely Opened: 2018-06-04 Last modified: 2018-06-24

Priority: 4

View other active issues in [fpos.operations].

View all other issues in [fpos.operations].

View all issues with New status.

Discussion:

The fpos requirements do not give any idea what is compared by operator== (even after Daniel's P0759R1 paper). I'd like something to make it clear that return true; is not a valid implementation of operator==(const fpos<T>&, const fpos<T>&). Maybe in the P(o) row state that "p == P(o)" and "p != P(o + 1)", i.e. two fpos objects constructed from the same streamoff values are equal, and two fpos objects constructed from two different streamoff values are not equal.

[2018-06-23 after reflector discussion]

Priority set to 4

Proposed resolution:


3119(i). Program-definedness of closure types

Section: 99 [defns.program.defined.spec] Status: Ready Submitter: Hubert Tong Opened: 2018-06-09 Last modified: 2018-11-11

Priority: 2

View all issues with Ready status.

Discussion:

The description of closure types in 7.5.5.1 [expr.prim.lambda.closure] says:

An implementation may define the closure type differently […]

The proposed resolution to LWG 2139 defines a "program-defined type" to be a

class type or enumeration type that is not part of the C++ standard library and not defined by the implementation, or an instantiation of a program-defined specialization

I am not sure that the intent of whether closure types are or are not program-defined types is clearly conveyed by the wording.

[2018-06-23 after reflector discussion]

Priority set to 2

[2018-08-14 Casey provides additional discussion and a Proposed Resolution]

We use the term "program-defined" in the library specification to ensure that two users cannot create conflicts in a component in namespace std by specifying different behaviors for the same type. For example, we allow users to specialize common_type when at least one of the parameters is a program-defined type. Since two users cannot define the same program-defined type, this rule prevents two users (or libraries) defining the same specialization of std::common_type.

Since it's guaranteed that even distinct utterances of identical lambda expressions produce closures with distinct types (7.5.5.1 [expr.prim.lambda.closure]), adding closure types to our term "program-defined type" is consistent with the intended use despite that such types are technically defined by the implementation.

Previous resolution [SUPERSEDED]:

This wording is relative to N4762.

[2018-08-23 Batavia Issues processing]

Updated wording

[2018-11 San Diego Thursday night issue processing]

Status to Ready.

Proposed resolution:

This wording is relative to N4762.


3120(i). Unclear behavior of monotonic_buffer_resource::release()

Section: 19.12.6.2 [mem.res.monotonic.buffer.mem] Status: Open Submitter: Arthur O'Dwyer Opened: 2018-06-10 Last modified: 2018-08-27

Priority: 2

View all other issues in [mem.res.monotonic.buffer.mem].

View all issues with Open status.

Discussion:

The effects of monotonic_buffer_resource::release() are defined as:

Calls upstream_rsrc->deallocate() as necessary to release all allocated memory.

This doesn't give any instruction on what to do with the memory controlled by the monotonic_buffer_resource which was not allocated, i.e., what to do with the initial buffer provided to its constructor.

Boost.Container's pmr implementation expels its initial buffer after a release(). Arthur O'Dwyer's proposed pmr implementation for libc++ reuses the initial buffer after a release(), on the assumption that this is what the average library user will be expecting.

#include <memory_resource>

int main() 
{
  char buffer[100];
  {
    std::pmr::monotonic_buffer_resource mr(buffer, 100, std::pmr::null_memory_resource());
    mr.release();
    mr.allocate(60);  // A
  }
  {
    std::pmr::monotonic_buffer_resource mr(buffer, 100, std::pmr::null_memory_resource());
    mr.allocate(60);  // B
    mr.release();
    mr.allocate(60);  // C
  }
}

Assume that allocation "B" always succeeds.
With the proposed libc++ implementation, allocations "A" and "C" both succeed.
With Boost.Container's implementation, allocations "A" and "C" both fail.
Using another plausible implementation strategy, allocation "A" could succeed but allocation "C" could fail. I have been informed that MSVC's implementation does this.

Which of these strategies should be permitted by the Standard?

Arthur considers "A and C both succeed" to be the obviously most user-friendly strategy, and really really hopes it's going to be permitted. Requiring "C" to succeed is unnecessary (and would render MSVC's current implementation non-conforming) but could help programmers concerned with portability between different implementations.

Another side-effect of release() which goes underspecified by the Standard is the effect of release() on next_buffer_size. As currently written, my interpretation is that release() is not permitted to decrease current_buffer_size; I'm not sure if this is a feature or a bug.

Consider this test case (taken from here):

std::pmr::monotonic_buffer_resource mr(std::pmr::new_delete_resource());
for (int i=0; i < 100; ++i) {
  mr.allocate(1);  // D
  mr.release();
}

Arthur believes it is important that the 100th invocation of line "D" does not attempt to allocate 2100 bytes from the upstream resource.

[2018-06-23 after reflector discussion]

Priority set to 2

Previous resolution [SUPERSEDED]:

This wording is relative to N4750.

[Drafting note: The resolution depicted below would make MSVC's and my-proposed-libc++'s implementations both conforming.]

  1. Modify 19.12.6.2 [mem.res.monotonic.buffer.mem] as indicated:

    void release();
    

    -1- Effects: Calls upstream_rsrc->deallocate() as necessary to release all allocated memory. Resets the state of the initial buffer.

    -2- [Note: The memory is released back to upstream_rsrc even if some blocks that were allocated from this have not been deallocated from this. This function has an unspecified effect on next_buffer_size.end note]

[2018-08-23 Batavia Issues processing]

We liked Pablo's wording from the reflector discussion. Status to Open.

Proposed resolution:

This wording is relative to N4750.

  1. Modify 19.12.6.2 [mem.res.monotonic.buffer.mem] as indicated:

    void release();
    

    -1- Effects: Calls upstream_rsrc->deallocate() as necessary to release all allocated memory. Resets *this to its initial state at construction.


3121(i). tuple constructor constraints for UTypes&&... overloads

Section: 19.5.3.1 [tuple.cnstr] Status: Open Submitter: Matt Calabrese Opened: 2018-06-12 Last modified: 2018-11-11

Priority: 2

View other active issues in [tuple.cnstr].

View all other issues in [tuple.cnstr].

View all issues with Open status.

Discussion:

Currently the tuple constructors of the form:

template<class... UTypes>
EXPLICIT constexpr tuple(UTypes&&...);

are not properly constrained in that in the 1-element tuple case, the constraints do no short-circuit when the constructor would be (incorrectly) considered as a possible copy/move constructor candidate. libc++ has a workaround for this, but the additional short-circuiting does not actually appear in the working draft.

As an example of why this lack of short circuiting is a problem in practice, consider the following line:

bool a = std::is_copy_constructible_v<std::tuple<any>>;

The above code will cause a compile error because of a recursive trait definition. The copy constructibility check implies doing substitution into the UTypes&&... constructor overloads, which in turn will check if tuple<any> is convertible to any, which in turn will check if tuple<any> is copy constructible (and so the trait is dependent on itself).

I do not provide wording for the proposed fix in anticipation of requires clauses potentially changing how we do the specification, however, the basic solution should be similar to what we've done for other standard library types, which is to say that the very first constraint should be to check that if sizeof...(UTypes) == 1 and the type, after applying remove_cvref_t, is the tuple type itself, then we should force substitution failure rather than checking any further constraints.

[2018-06-23 after reflector discussion]

Priority set to 3

[2018-08-20, Jonathan provides wording]

[2018-08-20, Daniel comments]

The wording changes by this issue are very near to those suggested for LWG 3155.

[2018-11 San Diego Thursday night issue processing]

Jonathan to update wording - using conjunction. Priority set to 2

Proposed resolution:

This wording is relative to N4762.


3123(i). duration constructor from representation shouldn't be effectively non-throwing

Section: 26.5 [time.duration] Status: New Submitter: Johel Ernesto Guerrero Peña Opened: 2018-06-22 Last modified: 2018-07-20

Priority: 3

View all other issues in [time.duration].

View all issues with New status.

Discussion:

[time.duration]/4 states:

Members of duration shall not throw exceptions other than those thrown by the indicated operations on their representations.

Where representation is defined in the non-normative, brief description at [time.duration]/1:

[…] A duration has a representation which holds a count of ticks and a tick period. […]

[time.duration.cons]/2 doesn't indicate the operation undergone by its representation, merely stating a postcondition in [time.duration.cons]/3:

Effects: Constructs an object of type duration.

Postconditions: count() == static_cast<rep>(r).

I suggest this reformulation that follows the format of [time.duration.cons]/5.

Effects: Constructs an object of type duration, constructing rep_ from r.

Now it is clear why the constructor would throw.

Previous resolution [SUPERSEDED]:

This wording is relative to N4750.

Change 26.5.1 [time.duration.cons] as indicated:

template<class Rep2>
  constexpr explicit duration(const Rep2& r);

-1- Remarks: This constructor shall not participate in overload resolution unless […]

-2- Effects: Constructs an object of type duration, constructing rep_ from r.

-3- Postconditions: count() == static_cast<rep>(r).

[2018-06-27 after reflector discussion]

Priority set to 3. Improved wording as result of that discussion.

Proposed resolution:

This wording is relative to N4750.

Change 26.5.1 [time.duration.cons] as indicated:

template<class Rep2>
  constexpr explicit duration(const Rep2& r);

-1- Remarks: This constructor shall not participate in overload resolution unless […]

-2- Effects: Constructs an object of type durationInitializes rep_ with r.

-3- Postconditions: count() == static_cast<rep>(r).


3124(i). [networking.ts] Unclear how execution_context is intended to store services

Section: 13.7.5 [networking.ts::async.exec.ctx.globals] Status: New Submitter: Billy O'Neal III Opened: 2018-06-23 Last modified: 2018-07-20

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.

  1. 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.


3125(i). duration streaming precondition should be a SFINAE condition

Section: 26.5.10 [time.duration.io] Status: Open Submitter: Johel Ernesto Guerrero Peña Opened: 2018-06-23 Last modified: 2018-08-27

Priority: 2

View all other issues in [time.duration.io].

View all issues with Open status.

Discussion:

26.5.10 [time.duration.io]/1 states:

template<class charT, class traits, class Rep, class Period>
  basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>& os, const duration<Rep, Period>& d);

Requires: Rep is an integral type whose integer conversion rank (6.7.4 [conv.rank]) is greater than or equal to that of short, or a floating point type. charT is char or wchar_t.

I think the intention was to make this a compile-time error, since all the information to make it so is available at compile-time. But the wording doesn't suggest that.

[2018-06-29; Daniel comments]

The wording will be significantly simplified by the application of the new Library element Constraints: introduced by P0788R3 and available with the post-Rapperswil working draft.

[2018-07-20 Priority set to 2 after reflector discussion. NAD and P0 were also mentioned.]

[2018-08-23 Batavia Issues processing]

Marshall to talk to Howard about his intent. Status to Open

Proposed resolution:

This wording is relative to N4750.

Change 26.5.10 [time.duration.io] as indicated:

[Drafting note: The suggested wording changes include the insertion of two bullets into the existing running text to improve clarification of the logic arm of the "unless"].

template<class charT, class traits, class Rep, class Period>
  basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>& os, const duration<Rep, Period>& d);

-1- RequiresRemarks: This function shall not participate in overload resolution unless:

[…]


3126(i). There's no std::sub_match::compare(string_view) overload

Section: 29.9 [re.submatch] Status: New Submitter: Jonathan Wakely Opened: 2018-06-26 Last modified: 2018-07-20

Priority: 3

View all issues with New status.

Discussion:

std::sub_match::compare can be called with a basic_string or a pointer to a null-terminated character sequence, but can't be called with a basic_string_view. To compare to a string_view requires either conversion to basic_string (with a potential allocation) or a redundant call to traits_type::length to calculate a length that is already known.

[2018-07-02, Jonathan comments and completes proposed wording]

To make the relational and equality operators for sub_match support string views I propose specifying the semantics, not adding another 12 overloaded operators to namespace std, in addition to the 42 already there. This allows them to be implemented as "hidden friends" if the implementation so desires, or to retain namespace-scope declaration if backwards compatibility with C++11 - C++17 is preferred.

[2018-07-20 Priority set to 3 after reflector discussion]

Proposed resolution:

This wording is relative to N4750.

  1. Change 29.4 [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>;
    
      // 29.9.2 [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);
      […]
    }
    
  2. Change 29.9 [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;
      };
    }
    
  3. Change 29.9.1 [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).

  4. Change sub-clause 29.9.2 [re.submatch.op] as indicated:

    31.9.2 sub_match non-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_match provides overloaded relational operators (7.6.9 [expr.rel]) and equality operators (7.6.10 [expr.eq]) for comparisons with another sub_match, with a string, or with a single character. The expressions shown in Table ?? are valid when one of the operands is a type S, that is a specialization of sub_match, and the other expression is one of:

    1. (?.?) — a value x of a type S, in which case STR(x) is x.str();

    2. (?.?) — a value x of type basic_string<S::value_type, T, A> for any types T and A, in which case STR(x) is basic_string_view<S::value_type>(x.data(), x.length());

    3. (?.?) — a value x of type basic_string_view<S::value_type, T> for any type T, in which case STR(x) is basic_string_view<S::value_type>(x.data(), x.length());

    4. (?.?) — a value x of a type convertible to const S::value_type*, in which case STR(x) is basic_string_view<S::value_type>(x);

    5. (?.?) — a value x of type convertible to S::value_type, in which case STR(x) is basic_string_view<S::value_type>(&x, 1).

    Table ?? — sub_match comparisons
    Expression Return type Operational
    semantics
    s == t bool STR(s).compare(STR(t)) == 0
    s != t bool STR(s).compare(STR(t)) != 0
    s < t bool STR(s).compare(STR(t)) < 0
    s > t bool STR(s).compare(STR(t)) > 0
    s <= t bool STR(s).compare(STR(t)) <= 0
    s >= t bool STR(s).compare(STR(t)) >= 0

3133(i). Modernizing numeric type requirements

Section: 25.2 [numeric.requirements] Status: New Submitter: Tim Song Opened: 2018-07-05 Last modified: 2018-07-20

Priority: Not Prioritized

View all other issues in [numeric.requirements].

View all issues with New status.

Discussion:

25.2 [numeric.requirements] contains some very old wording that hasn't been changed since C++98 except for issue 2699. As a result, it is at once over- and under-restrictive. For example:

We can significantly clean up this wording by using the existing named requirements. For ease of review, the following table provides a side-by-side comparison of the current and proposed wording.

Before After
A C++ program shall instantiate these components only with a type T that satisfies the following requirements: [Footnote … ] A C++ program shall instantiate these components only with a cv-unqualified object type T that satisfies the Cpp17DefaultConstructible, Cpp17CopyConstructible, Cpp17CopyAssignable, and Cpp17Destructiblefollowing requirements (15.5.3.1 [utility.arg.requirements]).:
(1.1) — T is not an abstract class (it has no pure virtual member functions); Cpp17DefaultConstructible
(1.2) — T is not a reference type;
(1.3) — T is not cv-qualified;
Implied by "cv-unqualified object type"
(1.4) — If T is a class, it has a public default constructor; Cpp17DefaultConstructible
(1.5) — If T is a class, it has a public copy constructor with the signature T::T(const T&); Cpp17CopyConstructible
(1.6) — If T is a class, it has a public destructor; Cpp17Destructible
(1.7) — If T is a class, it has a public copy assignment operator whose signature is either T& T::operator=(const T&) or T& T::operator=(T); Cpp17CopyAssignable
(1.8) — If T is a class, its assignment operator, copy and default constructors, and destructor shall correspond to each other in the following sense:
(1.8.1) — Initialization of raw storage using the copy constructor on the value of T(), however obtained, is semantically equivalent to value-initialization of the same raw storage.
(1.8.2) — Initialization of raw storage using the default constructor, followed by assignment, is semantically equivalent to initialization of raw storage using the copy constructor.
(1.8.3) — Destruction of an object, followed by initialization of its raw storage using the copy constructor, is semantically equivalent to assignment to the original object.
[Note: […] — end note]
These requirements are implied by Cpp17CopyConstructible and Cpp17CopyAssignable's requirement that the value of the copy is equivalent to the source.
(1.9) — If T is a class, it does not overload unary operator&. omitted now that we have std::addressof

Proposed resolution:

This wording is relative to the post-Rapperswil 2018 working draft.

  1. Edit 25.2 [numeric.requirements] p1 as indicated, striking the entire bulleted list:

    -1- The complex and valarray components are parameterized by the type of information they contain and manipulate. A C++ program shall instantiate these components only with a cv-unqualified object type T that satisfies the Cpp17DefaultConstructible, Cpp17CopyConstructible, Cpp17CopyAssignable, and Cpp17Destructiblefollowing requirements (15.5.3.1 [utility.arg.requirements]). :[Footnote: … ]

    (1.1) — T is not an abstract class (it has no pure virtual member functions);

    […]

    (1.9) — If T is a class, it does not overload unary operator&.

  2. Edit 25.7.2.4 [valarray.access] p3-4 as indicated:

    const T&  operator[](size_t n) const;
    T& operator[](size_t n);
    

    -1- Requires: n < size().

    -2- Returns: […]

    -3- Remarks: The expression &addressof(a[i+j]) == &addressof(a[i]) + j evaluates to true for all size_t i and size_t j such that i+j < a.size().

    -4- The expression &addressof(a[i]) != &addressof(b[j]) evaluates to true for any two arrays a and b and for any size_t i and size_t j such that i < a.size() and j < b.size(). [Note: […] — end note ]


3135(i). [fund.ts.v3] LFTSv3 contains two redundant alias templates

Section: 3.3.1 [fund.ts.v3::meta.type.synop], 5.1 [fund.ts.v3::header.memory.synop] Status: New Submitter: Thomas Köppe Opened: 2018-07-02 Last modified: 2018-07-20

Priority: 3

View all other issues in [fund.ts.v3::meta.type.synop].

View all issues with New status.

Discussion:

Addresses: fund.ts.v3

The LFTSv3 prospective-working-paper N4758 contains two aliases that are already in C++17:

I'd like to propose deleting both, but separate discussion is warranted:

void_t belongs with the larger "detection idiom", which has not been merged into C++17. We may prefer to keep our own local version of the alias.

uses_allocator_v aliases a version of uses_allocator that is modified by this TS. However, as specified the alias may actually end up referring to std::uses_allocator (because <memory> is included), not to std::experimental::uses_allocator, as may have been intended.

[2018-07-20 Priority set to 3 after reflector discussion]

Proposed resolution:


3136(i). [fund.ts.v3] LFTSv3 awkward wording in propagate_const requirements

Section: 3.2.2.2.1 [fund.ts.v3::propagate_const.class_type_requirements] Status: New Submitter: Thomas Köppe Opened: 2018-07-02 Last modified: 2018-07-20

Priority: 3

View all issues with New status.

Discussion:

Addresses: fund.ts.v3

In the LFTSv3 prospective-working-paper N4758, [propagate_const.class_type_requirements] uses a strange turn of phrase:

"In this sub-clause, t denotes a non-const lvlaue of type T, ct is a const T& bound to t, […]"

The last bit is strange: "ct is a const T& bound to t" is not how we usually say things. The specification-variables usually denote values, and values can't be references. Perhaps we could just say, "ct is as_const(t)"?

[2018-07-20 Priority set to 3 after reflector discussion]

Proposed resolution:

This wording is relative to N4758.

  1. Edit 3.2.2.2.1 [fund.ts.v3::propagate_const.class_type_requirements] as indicated:

    -1- If T is class type then it shall satisfy the following requirements. In this sub-clause t denotes a non-const lvalue of type T, ct is a const T& bound to tas_const(t), element_type denotes an object type.


3138(i). There is no such thing as assertion-level

Section: 16.8.2 [support.contract.cviol] Status: LEWG Submitter: Tim Song Opened: 2018-07-10 Last modified: 2018-11-28

Priority: 2

View other active issues in [support.contract.cviol].

View all other issues in [support.contract.cviol].

View all issues with LEWG status.

Discussion:

contract_violation has an assertion_level member function that is specified to return text "describing the assertion-level of the violated contract". There is, however, no assertion-level in 9.11.4 [dcl.attr.contract], only contract-level.

By itself, this can probably be handled editorially as a typo, but we may want to reconsider the name of the function, which currently mirrors the (incorrect) name of the nonterminal.

[2018-11 Reflector prioritization]

Set Priority to 2

Proposed resolution:


3139(i). contract_violation's special member functions

Section: 16.8.2 [support.contract.cviol] Status: New Submitter: Tim Song Opened: 2018-07-10 Last modified: 2018-11-28

Priority: 1

View other active issues in [support.contract.cviol].

View all other issues in [support.contract.cviol].

View all issues with New status.

Discussion:

contract_violation is currently defined as:

class contract_violation {
public:
  uint_least32_t line_number() const noexcept;
  string_view file_name() const noexcept;
  string_view function_name() const noexcept;
  string_view comment() const noexcept;
  string_view assertion_level() const noexcept;
};

which implies a full set of defaulted special member functions with entirely unclear semantics, and can arguably be read to imply that it is an aggregate.

It seems unlikely that we meant for users to default construct or copy contract_violation objects, but if we did want to do that, we need to at least specify the behavior of a default constructed contract_violation object and how copying and moving work with respect to the lifetime of the strings referenced by the string_views returned by its member functions.

[2018-11 Reflector prioritization]

Set Priority to 1

Proposed resolution:


3141(i). CopyConstructible doesn't preserve source values

Section: 17.6 [concepts.object] Status: New Submitter: Casey Carter Opened: 2018-07-07 Last modified: 2018-11-28

Priority: 2

View all issues with New status.

Discussion:

The design intent of the CopyConstructible concept has always been that the source of the constructions it requires be left unmodified. Before P0541R1 reformulated the Constructible concept in terms of is_constructible, the wording which is now in 17.2 [concepts.equality]/5:

This document uses a notational convention to specify which expressions declared in a requires-expression modify which inputs: except where otherwise specified, […] Operands that are constant lvalues or rvalues are required to not be modified.
indirectly enforced that requirement. Unfortunately, nothing in the current wording in 17.4.14 [concept.copyconstructible] enforces that requirement.

[2018-11 Reflector prioritization]

Set Priority to 2

Proposed resolution:

This wording is relative to N4762.

Change 17.4.14 [concept.copyconstructible] as indicated:

template<class T>
  concept CopyConstructible =
    MoveConstructible<T> &&
    Constructible<T, T&> && ConvertibleTo<T&, T> &&
    Constructible<T, const T&> && ConvertibleTo<const T&, T> &&
    Constructible<T, const T> && ConvertibleTo<const T, T>;

-1- If T is an object type, then let v be an lvalue of type (possibly const) T or an rvalue of type const T. CopyConstructible<T> is satisfied only if

(1.1) — After the definition T u = v;, u is equal to v and v is unmodified.

(1.2) — T(v) is equal to v and does not modify v.


3142(i). std::foo<incomplete> should be ill-formed NDR

Section: 15.5.4.8 [res.on.functions] Status: New Submitter: Casey Carter Opened: 2018-07-07 Last modified: 2018-11-28

Priority: 3

View all other issues in [res.on.functions].

View all issues with New status.

Discussion:

15.5.4.8 [res.on.functions]/2 states:

-2- In particular, the effects are undefined in the following cases:

[…]

(2.5) — if an incomplete type (6.7 [basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.

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.

[2018-11 Reflector prioritization]

Set Priority to 3

Proposed resolution:

This wording is relative to N4762.

Change 15.5.4.8 [res.on.functions] as indicated:

(2.5) — if an incomplete type (6.7 [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.7 [basic.types]) as a template argument is ill-formed with no diagnostic required.


3143(i). monotonic_buffer_resource growth policy is unclear

Section: 19.12.6 [mem.res.monotonic.buffer] Status: New Submitter: Jonathan Wakely Opened: 2018-07-20 Last modified: 2018-07-23

Priority: Not Prioritized

View all issues with New status.

Discussion:

During the discussion of LWG 3120 it was pointed out that the current wording in 19.12.6 [mem.res.monotonic.buffer] is contradictory. The introductory text for the class says "Each additional buffer is larger than the previous one, following a geometric progression" but the spec for do_allocate doesn't agree.

Firstly, it's impossible for the implementation to ensure a single geometric progression, because the size of the next buffer can be arbitrarily large. If the caller asks for an allocation that is N times bigger than the previous buffer, the next buffer will be at least N times larger than the previous one. If N is larger than the implementation-defined growth factor it's not a geometric progression.

Secondly, it's not even clear that each additional buffer will be larger than the previous one. Given a monotonic_buffer_resource object with little remaining space in current_buffer, a request to allocate 10*next_buffer_size will:

"set current_buffer to upstream_rsrc->allocate(n, m), where n is not less than max(bytes, next_buffer_size) and m is not less than alignment, and increase next_buffer_size by an implementation-defined growth factor (which need not be integral), then allocate the return block from the newly-allocated current_buffer."

The effects are to allocate a new buffer of at least max(10*next_buffer_size, next_buffer_size) bytes, and then do next_buffer_size *= growth_factor. If growth_factor < 10 then the next allocated buffer might be smaller than the last one. This means that although next_buffer_size itself follows a geometric progression, the actual size of any single allocated buffer can be much larger than next_buffer_size. A graph of the allocated sizes looks like a geometric progression with spikes where an allocation size is larger than next_buffer_size.

If the intention is to set next_buffer_size = max(n, next_buffer_size * growth_factor) so that every allocation from upstream is larger than the previous one, then we need a change to the Effects: to actually say that. Rather than a geometric progression with anomalous spikes, this would produce a number of different geometric progressions with discontinuous jumps between them.

If the spiky interpretation is right then we need to weaken the "Each additional buffer is larger" statement. Either way, we need to add a caveat to the "following a geometric progression" text because that isn't true for the spiky interpretation or the jumpy interpretation.

Thirdly, the Effects: says that the size of the allocated block, n, is not less than max(bytes, next_buffer_size). This seems to allow an implementation to choose to do n = ceil2(max(bytes, next_buffer_size)) if it wishes (maybe because allocating sizes that are a power of 2 simplifies the monotonic_buffer_resource implementation, or allows reducing the bookkeeping overhead). This still results in an approximate geometric progression (under either the spiky or jumpy interpretation) but the graph has steps rather than being a smooth curve (but always above the curve). This is another way that "Each additional buffer is larger than the previous one" is not guaranteed. Even if max(bytes, next_buffer_size) is greater on every call, for a growth factor between 1.0 and 2.0 the result of ceil2 might be the same for two successive buffers. I see no reason to forbid this, but Pablo suggested it's not allowed because it doesn't result in exponential growth (which I disagree with). If this is supposed to be forbidden, the wording needs to be fixed to forbid it.

Proposed resolution:


3144(i). span does not have a const_pointer typedef

Section: 21.7.3.1 [span.overview] Status: New Submitter: Louis Dionne Opened: 2018-07-23 Last modified: 2018-08-10

Priority: Not Prioritized

View all other issues in [span.overview].

View all issues with New status.

Discussion:

std::span does not have a typedef for const_pointer and const_reference. According to Marshall Clow, this is merely an oversight.

Proposed resolution:

This wording is relative to N4750.

  1. Change 21.7.3.1 [span.overview], class template span synopsis, as indicated:

    namespace std {
      template<class ElementType, ptrdiff_t Extent = dynamic_extent>
      class span {
      public:
        // constants and types
        using element_type = ElementType;
        using value_type = remove_cv_t<ElementType>;
        using index_type = ptrdiff_t;
        using difference_type = ptrdiff_t;
        using pointer = element_type*;
        using const_pointer = const element_type*;
        using reference = element_type&;
        using const_reference = const element_type&;
        using iterator = implementation-defined;
        using const_iterator = implementation-defined;
        using reverse_iterator = reverse_iterator<iterator>;
        using const_reverse_iterator = reverse_iterator<const_iterator>;
        static constexpr index_type extent = Extent;
      
        […]
      };
      […]
    }
    

3146(i). Excessive unwrapping in std::ref/cref

Section: 19.14.5.5 [refwrap.helpers] Status: New Submitter: Agustín K-ballo Bergé Opened: 2018-07-10 Last modified: 2018-08-27

Priority: 3

View all issues with New status.

Discussion:

The overloads of std::ref/cref that take a reference_wrapper as argument are defined as calling std::ref/cref recursively, whereas the return type is defined as unwrapping just one level. Calling these functions with arguments of multiple level of wrapping leads to ill-formed programs:

int i = 0;
std::reference_wrapper<int> ri(i);
std::reference_wrapper<std::reference_wrapper<int>> rri(ri);
std::ref(rri); // error within 'std::ref'

[Note: these overloads were added by issue resolution 10.29 for TR1, which can be found at N1688, at Redmond 2004]

[2018-08-20 Priority set to 3 after reflector discussion]

Proposed resolution:

This wording is relative to N4762.

  1. Change 19.14.5.5 [refwrap.helpers] as indicated:

    template<class T> reference_wrapper<T> ref(reference_wrapper<T> t) noexcept;
    

    -2- Returns: ref(t.get())t.

    […]
    template<class T> reference_wrapper<const T> cref(reference_wrapper<T> t) noexcept;
    

    -4- Returns: cref(t.get())t.


3149(i). DefaultConstructible should require default initialization

Section: 17.4.12 [concept.defaultconstructible] Status: Open Submitter: Casey Carter Opened: 2018-08-09 Last modified: 2018-11-11

Priority: 2

View all issues with Open status.

Discussion:

DefaultConstructible<T> is equivalent to Constructible<T> (17.4.11 [concept.constructible]), which is equivalent to is_constructible_v<T> (19.15.4.3 [meta.unary.prop]). Per 19.15.4.3 [meta.unary.prop] paragraph 8:

The predicate condition for a template specialization is_­constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:

T t(declval<Args>()...);
DefaultConstructible<T> requires that objects of type T can be value-initialized, rather than default-initialized as intended.

The library needs a constraint that requires object types to be default-initializable: the "rangified" versions of the algorithms in 19.10.11.3 [uninitialized.construct.default] proposed in P0896 "The One Ranges Proposal", for example. Users will also want a mechanism to provide such a constraint, and they're likely to choose DefaultConstructible despite its subtle unsuitability.

There are two alternative solutions: (1) change DefaultConstructible to require default-initialization, (2) change is_default_constructible_v to require default-initializaton and specify the concept in terms of the trait. (2) is probably too breaking a change to be feasible.

[2018-08-20 Priority set to 2 after reflector discussion]

Previous resolution [SUPERSEDED]:

[2018-08-23 Tim provides updated P/R based on Batavia discussion]

[2018-10-28 Casey expands the problem statement and the P/R]

During Batavia review of P0896R3, Tim Song noted that {} is not necessarily a valid initializer for a DefaultConstructible type. In this sample program (see Compiler Explorer):

struct S0 { explicit S0() = default; };
struct S1 { S0 x; }; // Note: aggregate
S1 x;   // Ok
S1 y{}; // ill-formed; copy-list-initializes x from {}
S1 can be default-initialized, but not list-initialized from an empty braced-init-list. The consensus among those present was that DefaultConstructible should prohibit this class of pathological types by requiring that initialization form to be valid.

Proposed resolution:

This wording is relative to N4762.


3150(i). UniformRandomBitGenerator should validate min and max

Section: 25.6.2.3 [rand.req.urng] Status: New Submitter: Casey Carter Opened: 2018-08-09 Last modified: 2018-08-27

Priority: 3

View other active issues in [rand.req.urng].

View all other issues in [rand.req.urng].

View all issues with New status.

Discussion:

25.6.2.3 [rand.req.urng] paragraph 2 specifies axioms for the UniformRandomBitGenerator concept:

2 Let g be an object of type G. G models UniformRandomBitGenerator only if

(2.1) — both G::min() and G::max() are constant expressions (7.7 [expr.const]),

(2.2) — G::min() < G::max(),

(2.3) — G::min() <= g(),

(2.4) — g() <= G::max(), and

(2.5) — g() has amortized constant complexity.

Bullets 2.1 and 2.2 are both compile-time requirements that ought to be validated by the concept.

[2018-08-20 Priority set to 3 after reflector discussion]

Proposed resolution:

This wording is relative to N4762.


3151(i). ConvertibleTo rejects conversions from array and function types

Section: 17.4.4 [concept.convertibleto] Status: New Submitter: Casey Carter Opened: 2018-08-09 Last modified: 2018-08-27

Priority: 3

View all issues with New status.

Discussion:

In the definition of ConvertibleTo in 17.4.4 [concept.convertibleto]:

template<class From, class To>
  concept ConvertibleTo =
    is_convertible_v<From, To> &&
    requires(From (&f)()) {
      static_cast<To>(f());
    };

f is an arbitrary function that returns type From. Since functions cannot return array or function types (9.2.3.5 [dcl.fct] paragraph 11), ConvertibleTo cannot be satisfied when From is an array or function type regardless of the type of To. This is incompatibility with is_convertible_v was not an intentional design feature, so it should be corrected. (Note that any change made here must take care to avoid breaking the ConvertibleTo<T, void> cases.)

[2018-08-20 Priority set to 3 after reflector discussion]

Proposed resolution:

[Drafting Note: I've used declval here, despite that "Concepts mean we never have to use declval again!" because the alternative is less readable:]

requires(add_rvalue_reference_t<From> (&f)()) {
  static_cast<To>(f());
};

This wording is relative to N4762.


3152(i). common_type and common_reference have flaws in common

Section: 19.15.7.6 [meta.trans.other] Status: New Submitter: Casey Carter Opened: 2018-08-10 Last modified: 2018-08-27

Priority: 3

View all other issues in [meta.trans.other].

View all issues with New status.

Discussion:

19.15.7.6 [meta.trans.other] p5 characterizes the requirements for program-defined specializations of common_type with the sentence:

Such a specialization need not have a member named type, but if it does, that member shall be a typedef-name for an accessible and unambiguous cv-unqualified non-reference type C to which each of the types T1 and T2 is explicitly convertible.

This sentence - which 19.15.7.6 [meta.trans.other] p7 largely duplicates to specify requirements on program-defined specializations of basic_common_reference - has two problems:

  1. The grammar term "typedef-name" is overconstraining; there's no reason to prefer a typedef-name here to an actual type, and

  2. "accessible" and "unambiguous" are not properties of types, they are properties of names and base classes.

  3. While we're here, we may as well strike the unused name C which both Note B and Note D define for the type denoted by type.

[2018-08 Batavia Monday issue prioritization]

Priority set to 3

Proposed resolution:

This wording is relative to N4762.


3155(i). tuple<any, any>{allocator_arg_t, an_allocator}

Section: 19.5.3.1 [tuple.cnstr] Status: Open Submitter: Jonathan Wakely Opened: 2018-08-18 Last modified: 2018-11-11

Priority: 3

View other active issues in [tuple.cnstr].

View all other issues in [tuple.cnstr].

View all issues with Open status.

Discussion:

For a 2-element std::tuple, attempting to call the "allocator-extended default constructor" might actually pass the allocator_arg tag and the allocator to the tuple element constructors:

tuple<any, any> t{allocator_arg, allocator<int>{}};
assert(std::get<0>(t).has_value());

This assertion should pass according to the standard, but users might expect the elements to be default constructed. If you really wanted to construct the elements with the tag and the allocator, you could do:

tuple<any, any> t{{allocator_arg}, {allocator<int>{}}};

or

tuple<any, any> t{tuple<allocator_arg_t, allocator<int>>{allocator_arg, allocator<int>{}}};

The deduction guides for std::tuple always treat {allocator_arg_t, an_allocator} as the allocator-extended default constructor, so this creates an empty tuple:

tuple t{allocator_arg, allocator<int>{}};

And this is needed to create tuple<any, any>:

tuple t{allocator_arg, allocator<int>{}, any{}, any{}};

The proposed resolution seems consistent with that, always calling an allocator-extended constructor for {allocator_arg_t, a}, instead of the tuple(UTypes&&...) constructor.

Ville Voutilainen:

This was discussed in this reflector thread, where Andrzej convinced me to change libstdc++ tuple.

[2018-08-20, Daniel comments]

The wording changes by this issue are very near to those suggested for LWG 3121.

[2018-08 Batavia Monday issue prioritization]

Priority set to 0, status to 'Tentatively Ready'. Alisdair to write a paper about SFINAE constraints on the Allocator-aware tuple constructors.

[2018-08 Batavia Friday]

Tim Song found a 3-element case of this issue. Status back to 'Open'

tuple<any,any,any>(allocator_arg_t, a, tuple)

[2018-09 Reflector prioritization]

Set Priority to 3

Proposed resolution:

This wording is relative to N4762.


3156(i). ForwardIterator should only mean forward iterator

Section: 19.10.11 [specialized.algorithms] Status: New Submitter: Casey Carter Opened: 2018-09-06 Last modified: 2018-10-07

Priority: 3

View other active issues in [specialized.algorithms].

View all other issues in [specialized.algorithms].

View all issues with New status.

Discussion:

19.10.11 [specialized.algorithms] para 1.2 describes how the specialized algorithms with a template parameter named ForwardIterator impose requirements on the type passed as argument for that parameter: it must meet the Cpp17ForwardIterator requirements, which is consistent with how the rest of the Library uses the template parameter name ForwardIterator, and many of the required operations on that type must not throw exceptions, which is not consistent with how the rest of the Library uses that name.

To avoid confusion and keep the meaning of requirements imposed by template parameter names crisp, the specialized memory algorithms should use a different template parameter name for this different set of requirements.

Note that the proposed change has no normative effect; it's simply a clarification of the existing wording.

[2018-09 Reflector prioritization]

Set Priority to 3

Proposed resolution:

This wording is relative to N4762.


3157(i). Allocator destroy and fancy pointer operations must be non-throwing

Section: 15.5.3.5 [allocator.requirements] Status: New Submitter: Billy O'Neal III Opened: 2018-09-07 Last modified: 2018-10-07

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 15.5.5.12 [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.


3158(i). tuple(allocator_arg_t, const Alloc&) should be conditionally explicit

Section: 19.5.3.1 [tuple.cnstr] Status: New Submitter: Jonathan Wakely Opened: 2018-08-21 Last modified: 2018-10-07

Priority: 3

View other active issues in [tuple.cnstr].

View all other issues in [tuple.cnstr].

View all issues with New status.

Discussion:

std::tuple's allocator-extended constructors say "Effects: Equivalent to the preceding constructors except that each element is constructed with uses-allocator construction". That's not true for the first one, as shown by:

#include <tuple>

struct X { explicit X() { } };

std::tuple<X> f() { return {}; }
std::tuple<X> g() { return { std::allocator_arg, std::allocator<int>{} }; }

The function f() doesn't compile because of the explicit constructor, but g() does, despite using the same constructor for X. The conditional explicit-ness is not equivalent.

Also, the editor requested that we change "implicitly default-constructible" to use words that mean something. He suggested "copy-list-initializable from an empty list".

[2018-09 Reflector prioritization]

Set Priority to 3

Proposed resolution:

This wording is relative to N4762.


3159(i). §[unique.ptr.single] requirements on deleter may be too strict

Section: 19.11.1.2 [unique.ptr.single] Status: New Submitter: Jonathan Wakely Opened: 2018-09-17 Last modified: 2018-10-07

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:

19.11.1.2 [unique.ptr.single] p1 says:

The default type for the template parameter D is default_delete. A client-supplied template argument D shall be a function object type (19.14), 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.

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 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:


3161(i). Container adapters mandate use of emplace_back but don't require it

Section: 21.6.6 [stack], 21.6.4 [queue] Status: New Submitter: Marshall Clow Opened: 2018-10-02 Last modified: 2018-10-07

Priority: Not Prioritized

View all other issues in [stack].

View all issues with New status.

Discussion:

21.6.6 [stack] p1 says:

Any sequence container supporting operations back(), push_back() and pop_back() can be used to instantiate stack.

but then in 21.6.6.1 [stack.defn] we have the following code:

template<class... Args>
  decltype(auto) emplace(Args&&... args)
    { return c.emplace_back(std::forward<Args>(args)...); }

The same pattern appears in 21.6.4 [queue].

I see two ways to resolve this:

The first is to add emplace_back() to the list of requirements for underlying containers for stack and queue

The second is to replace the calls to c.emplace_back(std::forward<Args>(args)...) with c.emplace(c.end(), std::forward<Args>(args)...). We can do this w/o messing with the list above because emplace is part of the sequence container requirements, while emplace_back is not. I checked the libc++ implementation of vector, deque, and list, and they all do the same thing for emplace(end(), ...) and emplace_back(...).

Proposed resolution:

This wording is relative to N4762.

I have prepared two mutually exclusive options.
Option A a requirement for emplace_back to the underlying container.
Option B one replaces the calls to emplace_back with calls to emplace.

Option A

Option B


3162(i). system_error::system_error(error_code ec) not explicit

Section: 18.5.7.2 [syserr.syserr.members] Status: New Submitter: Peter Dimov Opened: 2018-10-02 Last modified: 2018-10-07

Priority: Not Prioritized

View other active issues in [syserr.syserr.members].

View all other issues in [syserr.syserr.members].

View all issues with New status.

Discussion:

The constructor for std::system_error taking a single argument of type std::error_code is not marked explicit, which allows implicit conversions from error_code to system_error. I think that this is an oversight and not intentional, and that we should make this constructor explicit.

Proposed resolution:

This wording is relative to N4762.

  1. Change 18.5.7.1 [syserr.syserr.overview] p2, class system_error synopsis, as indicated

    namespace std {
      class system_error : public runtime_error {
      public:
        system_error(error_code ec, const string& what_arg);
        system_error(error_code ec, const char* what_arg);
        explicit system_error(error_code ec);
        system_error(int ev, const error_category& ecat, const string& what_arg);
        system_error(int ev, const error_category& ecat, const char* what_arg);
        system_error(int ev, const error_category& ecat);
        const error_code& code() const noexcept;
        const char* what() const noexcept override;
      };
    }
    
  2. Change 18.5.7.2 [syserr.syserr.members] as indicated

    explicit system_error(error_code ec);
    

    -5- Effects: […]

    -6- Ensures: […]


3165(i). All starts_with() overloads should be called "begins_with"

Section: 20.3.2 [basic.string], 20.4.2 [string.view.template] Status: LEWG Submitter: Gennaro Prota Opened: 2018-10-22 Last modified: 2018-11-28

Priority: 2

View all other issues in [basic.string].

View all issues with LEWG status.

Discussion:

Throughout the standard library, the opposite of the verb "to end" is "to begin", not to "to start". In this respect, all the overloads of std::basic_string::starts_with and std::basic_string_view::starts_with are named inconsistently. These calls were added in P0457R2, which refers to analogous functions in Java, Python, Qt, LLVM, WebKit, all of which use the term "start". But, in our opinion, the C++ standard library should first be consistent with itself.

11-2018: Referred to LEWG after reflector discussion.

Proposed resolution:

This wording is relative to N4778.

  1. Change 20.3.2 [basic.string], class template basic_string synopsis, as indicated:

    bool beginstarts_with(basic_string_view<charT, traits> x) const noexcept;
    bool beginstarts_with(charT x) const noexcept;
    bool beginstarts_with(const charT* x) const;
    
  2. Change 20.3.2.7.5 [string.starts.with] as indicated

    bool beginstarts_with(basic_string_view<charT, traits> x) const noexcept;
    bool beginstarts_with(charT x) const noexcept;
    bool beginstarts_with(const charT* x) const;
    

    -1- Effects: Equivalent to:

    return basic_string_view<charT, traits>(data(), size()).beginstarts_with(x);
    
  3. Change 20.4.2 [string.view.template], class template basic_string_view synopsis, as indicated:

    constexpr bool beginstarts_with(basic_string_view x) const noexcept;
    constexpr bool beginstarts_with(charT x) const noexcept;
    constexpr bool beginstarts_with(const charT* x) const;
    
  4. Change 20.4.2.6 [string.view.ops] as indicated:

    constexpr bool beginstarts_with(basic_string_view x) const noexcept;
    

    -20- Effects: Equivalent to: return compare(0, npos, x) == 0;

    constexpr bool beginstarts_with(charT x) const noexcept;
    

    -21- Effects: Equivalent to: return beginstarts_with(basic_string_view(&x, 1));

    constexpr bool beginstarts_with(const charT* x) const;
    

    -22- Effects: Equivalent to: return beginstarts_with(basic_string_view(x));


3166(i). No such descriptive element as Value:

Section: 19.4.4 [pair.astuple], 19.7.4 [variant.helper], 21.3.7.6 [array.tuple] Status: New Submitter: Walter Brown Opened: 2018-11-08 Last modified: 2018-11-28

Priority: 3

View all other issues in [pair.astuple].

View all issues with New status.

Discussion:

In N4778, 19.4.4 [pair.astuple], 19.7.4 [variant.helper], and 21.3.7.6 [array.tuple], are partly specified via a fictitious descriptive element Value: Moreover, [span.tuple] is on track to do likewise in the near future.

Let's invent such a Value: element and properly document it within 15.4.1.4 [structure.specifications], or else let's respecify the offending uses.

[2018-11 Reflector prioritization]

Set Priority to 3

Proposed resolution:


3167(i). [fund.ts.v3] Does observer_ptr support function types?

Section: 5.2 [fund.ts.v3::memory.observer.ptr] Status: New Submitter: Alisdair Meredith Opened: 2018-11-14 Last modified: 2018-11-28

Priority: 3

View all issues with New 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

Proposed resolution:


3168(i). Expects: element should be specified in one place

Section: 15.4.1.4 [structure.specifications], 15.5.4.11 [res.on.required] Status: New Submitter: Geoffrey Romer Opened: 2018-11-21 Last modified: 2018-11-28

Priority: 2

View other active issues in [structure.specifications].

View all other issues in [structure.specifications].

View all issues with New status.

Discussion:

15.4.1.4 [structure.specifications]/p3.4 specifies the Expects: element as "the conditions (sometimes termed preconditions) that the function assumes to hold whenever it is called". This is nonsensical (functions can't "assume" things because they're not sentient), and more to the point it says nothing about what happens if those conditions don't hold. This is a serious problem because the whole point of introducing Expects: was to correct the vagueness of Requires: on exactly that point.

15.5.4.11 [res.on.required]/p2 is more explicit: "Violation of any preconditions specified in a function's Expects: element results in undefined behavior." However, I think putting the actual meaning of the Expects: element in a subclause called "Requires paragraph", 21 pages away from where Expects: is nominally specified, is asking too much of the reader. Splitting the specification of Requires: into two places 21 pages apart also seems needlessly obtuse, but that's less pressing since Requires: should be going away soon.

[2018-11 Reflector prioritization]

Set Priority to 2

Proposed resolution:

This wording is relative to N4778.

[Drafting Note: I have prepared two mutually exclusive options, depicted below by Option A and Option B, respectively. In case the committee would prefer to leave "Requires" alone, Option B describes just the "Expects" edits, as an alternate P/R]

Option A

  1. Change 15.4.1.4 [structure.specifications] as indicated:

    -3- Descriptions of function semantics contain the following elements (as appropriate):(footnote)

    1. (3.1) — Requires: the preconditions for calling the function.the conditions that are required to hold when the function is called in order for the call to successfully complete. [Note: When these conditions are violated, the function's Throws: element may specify throwing an exception. Otherwise, the behavior is undefined. — end note]

    2. (3.2) — Constraints: […]

    3. (3.3) — Mandates: […]

    4. (3.4) — Expects: the conditions (sometimes termed preconditions) that the function assumes to hold whenever it is calledare required to hold when the function is called in order for the call to have well-defined behavior. [Example: An implementation might express such conditions via an attribute such as [[expects]] (9.11.4 [dcl.attr.contract]). However, some such conditions might not lend themselves to expression via code. — end example]

  2. Delete 15.5.4.11 [res.on.required] in it's entirety as indicated:

    15.5.4.11 Requires paragraph [res.on.required]

    -1- Violation of any preconditions specified in a function's Requires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception when the precondition is violated.

    -2- Violation of any preconditions specified in a function's Expects: element results in undefined behavior.

Option B

  1. Change 15.4.1.4 [structure.specifications] as indicated:

    -3- Descriptions of function semantics contain the following elements (as appropriate):(footnote)

    1. (3.1) — Requires: the preconditions for calling the function.

    2. (3.2) — Constraints: […]

    3. (3.3) — Mandates: […]

    4. (3.4) — Expects: the conditions (sometimes termed preconditions) that the function assumes to hold whenever it is calledare required to hold when the function is called in order for the call to have well-defined behavior. [Example: An implementation might express such conditions via an attribute such as [[expects]] (9.11.4 [dcl.attr.contract]). However, some such conditions might not lend themselves to expression via code. — end example]

  2. Change 15.5.4.11 [res.on.required] as indicated:

    -1- Violation of any preconditions specified in a function's Requires: element results in undefined behavior unless the function's Throws: element specifies throwing an exception when the precondition is violated.

    -2- Violation of any preconditions specified in a function's Expects: element results in undefined behavior.


3169(i). ranges permutation generators discard useful information

Section: 24.7.12 [alg.permutation.generators] Status: New Submitter: Casey Carter Opened: 2018-11-26 Last modified: 2018-11-28

Priority: Not Prioritized

View all issues with New status.

Discussion:

In the Ranges design, algorithms that necessarily traverse an entire range - consequently discovering the end iterator value - return that iterator value unless the algorithm's sole purpose is to return a derived value, for example, ranges::count. ranges::next_permutation and ranges::prev_permutation necessarily traverse the entirety of their range argument, but are currently specified to discard the end iterator value and return only a bool indicating whether they found a next (respectively previous) permutation or "reset" the range to the first (respectively last) permutation. They should instead return an aggregate composed of both that bool and the end iterator value to be consistent with the other range algorithms.

Proposed resolution:

This wording is relative to the post-San Diego working draft.