| Doc. no. | N3018=10-0008 |
| Date: | 2010-02-14 |
| Project: | Programming Language C++ |
| Reply to: | Howard Hinnant <howard.hinnant@gmail.com> |
Reference ISO/IEC IS 14882:2003(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 ANSI (J16) and ISO (WG21) C++ Standards Committee. Issues represent potential defects in the ISO/IEC IS 14882:2003(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:2003(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.
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.
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).
NAD Concepts - The LWG has reached consensus that the issue is NAD for now, but represents a real issue when the library is done with language-supported concepts.
NAD Future - In addition to the regular status, the LWG believes that this issue should be revisited at the next revision of the standard.
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).
DR - (Defect Report) - The full J16 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.
TC1 - (Technical Corrigenda 1) - The full WG21 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.
CD1 - (Committee Draft 2008) - The full WG21 committee has voted to accept the Defect Report's Proposed Resolution into the Fall 2008 Committee Draft.
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.
WP - (Working Paper) - The proposed resolution has not been accepted as a Technical Corrigendum, but the full WG21 committee has voted to apply the Defect Report's Proposed Resolution to the working paper.
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.
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 J16 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 (TC), or are closed without action other than a Record of Response (RR ). The intent of this LWG process is that only issues which are truly defects in the Standard move to the formal ISO DR status.
Section: 20.3.4 [pairs] Status: Ready Submitter: Martin Sebor Opened: 2001-01-14 Last modified: 2009-10-26
View other active issues in [pairs].
View all other issues in [pairs].
View all issues with Ready status.
Discussion:
The synopsis of the header <utility> in 20.3 [utility] lists the complete set of equality and relational operators for pair but the section describing the template and the operators only describes operator==() and operator<(), and it fails to mention any requirements on the template arguments. The remaining operators are not mentioned at all.
[ 2009-09-27 Alisdair reopens. ]
The issue is a lack of wording specifying the semantics of std::pair relational operators. The rationale is that this is covered by catch-all wording in the relops component, and that as relops directly precedes pair in the document this is an easy connection to make.
Reading the current working paper I make two observations:
- relops no longer immediately precedes pair in the order of specification. However, even if it did, there is a lot of pair specification itself between the (apparently) unrelated relops and the relational operators for pair. (The catch-all still requires operator== and operator< to be specified explicitly)
- No other library component relies on the catch-all clause. The following all explicitly document all six relational operators, usually in a manner that could have deferred to the relops clause.
tuple unique_ptr duration time_point basic_string queue stack move_iterator reverse_iterator regex submatch thread::idThe container components provide their own (equivalent) definition in 23.2.1 [container.requirements.general] Table 90 -- Container requirements and do so do not defer to relops.
Shared_ptr explicitly documents operator!= and does not supply the other 3 missing operators (>,>=,<=) so does not meet the reqirements of the relops clause.
Weak_ptr only supports operator< so would not be covered by relops.
At the very least I would request a note pointing to the relops clause we rely on to provide this definition. If this route is taken, I would recommend reducing many of the above listed clauses to a similar note rather than providing redundant specification.
My preference would be to supply the 4 missing specifications consistent with the rest of the library.
[ 2009-10-11 Daniel opens 1233 which deals with the same issue as it pertains to unique_ptr. ]
[ 2009-10 Santa Cruz: ]
Move to Ready
Proposed resolution:
After p20 20.3.4 [pairs] add:
template <class T1, class T2> bool operator!=(const pair<T1,T2>& x, const pair<T1,T2>& y);Returns: !(x==y)template <class T1, class T2> bool operator> (const pair<T1,T2>& x, const pair<T1,T2>& y);Returns: y < xtemplate <class T1, class T2> bool operator>=(const pair<T1,T2>& x, const pair<T1,T2>& y);Returns: !(x < y)template <class T1, class T2> bool operator<=(const pair<T1,T2>& x, const pair<T1,T2>& y);Returns: !(y < x)
Rationale:
20.3.1 [operators] paragraph 10 already specifies the semantics. That paragraph says that, if declarations of operator!=, operator>, operator<=, and operator>= appear without definitions, they are defined as specified in 20.3.1 [operators]. There should be no user confusion, since that paragraph happens to immediately precede the specification of pair.
Section: 24.2.4 [bidirectional.iterators], 24.2.5 [random.access.iterators] Status: Open Submitter: John Potter Opened: 2001-01-22 Last modified: 2009-10-26
View all other issues in [bidirectional.iterators].
View all issues with Open status.
Discussion:
In section 24.2.4 [bidirectional.iterators], Table 75 gives the return type of *r-- as convertible to T. This is not consistent with Table 74 which gives the return type of *r++ as T&. *r++ = t is valid while *r-- = t is invalid.
In section 24.2.5 [random.access.iterators], Table 76 gives the return type of a[n] as convertible to T. This is not consistent with the semantics of *(a + n) which returns T& by Table 74. *(a + n) = t is valid while a[n] = t is invalid.
Discussion from the Copenhagen meeting: the first part is uncontroversial. The second part, operator[] for Random Access Iterators, requires more thought. There are reasonable arguments on both sides. Return by value from operator[] enables some potentially useful iterators, e.g. a random access "iota iterator" (a.k.a "counting iterator" or "int iterator"). There isn't any obvious way to do this with return-by-reference, since the reference would be to a temporary. On the other hand, reverse_iterator takes an arbitrary Random Access Iterator as template argument, and its operator[] returns by reference. If we decided that the return type in Table 76 was correct, we would have to change reverse_iterator. This change would probably affect user code.
History: the contradiction between reverse_iterator and the Random Access Iterator requirements has been present from an early stage. In both the STL proposal adopted by the committee (N0527==94-0140) and the STL technical report (HPL-95-11 (R.1), by Stepanov and Lee), the Random Access Iterator requirements say that operator[]'s return value is "convertible to T". In N0527 reverse_iterator's operator[] returns by value, but in HPL-95-11 (R.1), and in the STL implementation that HP released to the public, reverse_iterator's operator[] returns by reference. In 1995, the standard was amended to reflect the contents of HPL-95-11 (R.1). The original intent for operator[] is unclear.
In the long term it may be desirable to add more fine-grained iterator requirements, so that access method and traversal strategy can be decoupled. (See "Improved Iterator Categories and Requirements", N1297 = 01-0011, by Jeremy Siek.) Any decisions about issue 299 should keep this possibility in mind.
Further discussion: I propose a compromise between John Potter's resolution, which requires T& as the return type of a[n], and the current wording, which requires convertible to T. The compromise is to keep the convertible to T for the return type of the expression a[n], but to also add a[n] = t as a valid expression. This compromise "saves" the common case uses of random access iterators, while at the same time allowing iterators such as counting iterator and caching file iterators to remain random access iterators (iterators where the lifetime of the object returned by operator*() is tied to the lifetime of the iterator).
Note that the compromise resolution necessitates a change to reverse_iterator. It would need to use a proxy to support a[n] = t.
Note also there is one kind of mutable random access iterator that will no longer meet the new requirements. Currently, iterators that return an r-value from operator[] meet the requirements for a mutable random access iterartor, even though the expression a[n] = t will only modify a temporary that goes away. With this proposed resolution, a[n] = t will be required to have the same operational semantics as *(a + n) = t.
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-09-18 Alisdair adds: ]
Why can't we write through the reference returned from operator[] on a random access iterator?
Recommended solution:
In table Table 104 -- Random access iterator requirements, replace
a[n] : convertible toconst T &T& if X is mutable, otherwise convertible to const T&
[ 2009-10 Santa Cruz: ]
Leave Open. Alisdair to spearhead a paper on revivification.
Proposed resolution:
In section 24.1.4 [lib.bidirectdional.iterators], change the return type in table 75 from "convertible to T" to T&.
In section 24.1.5 [lib.random.access.iterators], change the operational semantics for a[n] to " the r-value of a[n] is equivalent to the r-value of *(a + n)". Add a new row in the table for the expression a[n] = t with a return type of convertible to T and operational semantics of *(a + n) = t.
[Lillehammer: Real problem, but should be addressed as part of iterator redesign]
Rationale:
[ San Francisco: ]
Solved by N2758.
Section: 27.7.2.4 [ostream::sentry] Status: Open Submitter: Martin Sebor Opened: 2003-01-05 Last modified: 2009-10-20
View all other issues in [ostream::sentry].
View all issues with Open status.
Discussion:
17.4.4.8, p3 prohibits library dtors from throwing exceptions.
27.6.2.3, p4 says this about the ostream::sentry dtor:
-4- If ((os.flags() & ios_base::unitbuf) && !uncaught_exception())
is true, calls os.flush().
27.6.2.6, p7 that describes ostream::flush() says:
-7- If rdbuf() is not a null pointer, calls rdbuf()->pubsync().
If that function returns ?-1 calls setstate(badbit) (which
may throw ios_base::failure (27.4.4.3)).
That seems like a defect, since both pubsync() and setstate() can throw an exception.
[ The contradiction is real. Clause 17 says destructors may never throw exceptions, and clause 27 specifies a destructor that does throw. In principle we might change either one. We're leaning toward changing clause 17: putting in an "unless otherwise specified" clause, and then putting in a footnote saying the sentry destructor is the only one that can throw. PJP suggests specifying that sentry::~sentry() should internally catch any exceptions it might cause. ]
[ See 418 and 622 for related issues. ]
[ 2009-07 Frankfurt ]
Move to Review. Add "Throws: nothing" to the specification of ostream::sentry::~sentry().
[ 2009-10-13 Daniel adds: ]
The proposed resolution of 835 is written to match the outcome of this issue.
[ 2009 Santa Cruz: ]
Move to Open. Our intent is to solve this issue with 835.
Proposed resolution:
Add after 27.7.2.4 [ostream::sentry] p17:
~sentry();-17- If ((os.flags() & ios_base::unitbuf) && !uncaught_exception()) is true, calls os.flush().
Throws: Nothing.
Section: 24.2 [iterator.requirements] Status: Open Submitter: Nathan Myers Opened: 2003-06-03 Last modified: 2009-11-03
View other active issues in [iterator.requirements].
View all other issues in [iterator.requirements].
View all issues with Open status.
Discussion:
I've been discussing iterator semantics with Dave Abrahams, and a surprise has popped up. I don't think this has been discussed before.
X [iterator.concepts] says that the only operation that can be performed on "singular" iterator values is to assign a non-singular value to them. (It doesn't say they can be destroyed, and that's probably a defect.) Some implementations have taken this to imply that there is no need to initialize the data member of a reverse_iterator<> in the default constructor. As a result, code like
std::vector<std::reverse_iterator<char*> > v(7); v.reserve(1000);
invokes undefined behavior, because it must default-initialize the vector elements, and then copy them to other storage. Of course many other vector operations on these adapters are also left undefined, and which those are is not reliably deducible from the standard.
I don't think that 24.1 was meant to make standard-library iterator types unsafe. Rather, it was meant to restrict what operations may be performed by functions which take general user- and standard iterators as arguments, so that raw pointers would qualify as iterators. However, this is not clear in the text, others have come to the opposite conclusion.
One question is whether the standard iterator adaptors have defined copy semantics. Another is whether they have defined destructor semantics: is
{ std::vector<std::reverse_iterator<char*> > v(7); }
undefined too?
Note this is not a question of whether algorithms are allowed to rely on copy semantics for arbitrary iterators, just whether the types we actually supply support those operations. I believe the resolution must be expressed in terms of the semantics of the adapter's argument type. It should make clear that, e.g., the reverse_iterator<T> constructor is actually required to execute T(), and so copying is defined if the result of T() is copyable.
Issue 235, which defines reverse_iterator's default constructor more precisely, has some relevance to this issue. However, it is not the whole story.
The issue was whether
reverse_iterator() { }
is allowed, vs.
reverse_iterator() : current() { }
The difference is when T is char*, where the first leaves the member uninitialized, and possibly equal to an existing pointer value, or (on some targets) may result in a hardware trap when copied.
8.5 paragraph 5 seems to make clear that the second is required to satisfy DR 235, at least for non-class Iterator argument types.
But that only takes care of reverse_iterator, and doesn't establish a policy for all iterators. (The reverse iterator adapter was just an example.) In particular, does my function
template <typename Iterator>
void f() { std::vector<Iterator> v(7); }
evoke undefined behavior for some conforming iterator definitions? I think it does, now, because vector<> will destroy those singular iterator values, and that's explicitly disallowed.
24.1 shouldn't give blanket permission to copy all singular iterators, because then pointers wouldn't qualify as iterators. However, it should allow copying of that subset of singular iterator values that are default-initialized, and it should explicitly allow destroying any iterator value, singular or not, default-initialized or not.
[ We don't want to require all singular iterators to be copyable, because that is not the case for pointers. However, default construction may be a special case. Issue: is it really default construction we want to talk about, or is it something like value initialization? We need to check with core to see whether default constructed pointers are required to be copyable; if not, it would be wrong to impose so strict a requirement for iterators. ]
[ 2009-05-10 Alisdair provided wording. ]
The comments regarding destroying singular iterators have already been resolved. That just leaves copying (with moving implied).
[ 2009-07 Frankfurt ]
This is related to LWG 1012.
Note that there is a bug in the proposed resolution to LWG 1012. The change to [reverse.iter.con] should be modified so that the word "default" in the second sentence of the Effects clause is replaced by "value."
We believe that the proposed fix to LWG 1012 (now corrected) is sufficient to solve the problem for reverse_iterator. However, Alisdair pointed out that LWG 1012 does not solve the general problem for authors of iterator adaptors.
There are some problems with the proposed resolution. The phrase "safely copyable" is not a term of art. Also, it mentions a DefaultConstructible? concept.
Move to Review after Alisdair updates the wording.
[ 2009-07-31 Alisdair revised wording: ]
[ 2009-08-17 Alisdair and Daniel collaborate on slightly revised wording. This issue depends upon 724 ]
[ 2009-10-14 Daniel adds: ]
There is a clear dependency on 1213, because the term "singular", which is used as part of the resolution, is not properly defined yet.
[ 2009-10 Santa Cruz: ]
Moved to Open. Alisdair will provide improved wording to make this have "value semantics" and otherwise behave like a valid iterator.
Proposed resolution:
Add a new paragrpah to Iterator concepts 24.2 [iterator.requirements] after para 5 (the one describing singular iterators)
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 container. 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. [Example: After the declaration of an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular value of a pointer. — end example] 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. In this case the singular value is overwritten the same way as any other value. Dereferenceable values are always non-singular.
After value-initialization, any iterator that satisfies the DefaultConstructible requirements ([defaultconstructible]) shall not introduce undefined behaviour when used as the source of a copy or move operation, even if it would otherwise be singular. [Note: This guarantee is not offered for default-initialization (8.5 [dcl.init]), although the distinction only matters for types with trivial default constructors such as pointers. — end note]
Section: 22.4.2.1.2 [facet.num.get.virtuals] Status: Open Submitter: Martin Sebor Opened: 2003-09-18 Last modified: 2010-02-06
View other active issues in [facet.num.get.virtuals].
View all other issues in [facet.num.get.virtuals].
View all issues with Open status.
Discussion:
The requirements specified in Stage 2 and reiterated in the rationale of DR 221 (and echoed again in DR 303) specify that num_get<charT>:: do_get() compares characters on the stream against the widened elements of "012...abc...ABCX+-"
An implementation is required to allow programs to instantiate the num_get template on any charT that satisfies the requirements on a user-defined character type. These requirements do not include the ability of the character type to be equality comparable (the char_traits template must be used to perform tests for equality). Hence, the num_get template cannot be implemented to support any arbitrary character type. The num_get template must either make the assumption that the character type is equality-comparable (as some popular implementations do), or it may use char_traits<charT> to do the comparisons (some other popular implementations do that). This diversity of approaches makes it difficult to write portable programs that attempt to instantiate the num_get template on user-defined types.
[Kona: the heart of the problem is that we're theoretically supposed to use traits classes for all fundamental character operations like assignment and comparison, but facets don't have traits parameters. This is a fundamental design flaw and it appears all over the place, not just in this one place. It's not clear what the correct solution is, but a thorough review of facets and traits is in order. The LWG considered and rejected the possibility of changing numeric facets to use narrowing instead of widening. This may be a good idea for other reasons (see issue 459), but it doesn't solve the problem raised by this issue. Whether we use widen or narrow the num_get facet still has no idea which traits class the user wants to use for the comparison, because only streams, not facets, are passed traits classes. The standard does not require that two different traits classes with the same char_type must necessarily have the same behavior.]
Informally, one possibility: require that some of the basic character operations, such as eq, lt, and assign, must behave the same way for all traits classes with the same char_type. If we accept that limitation on traits classes, then the facet could reasonably be required to use char_traits<charT>.
[ 2009-07 Frankfurt ]
There was general agreement that the standard only needs to specify the behavior when the character type is char or wchar_t.
Beman: we don't need to worry about C++1x because there is a non-zero possibility that we would have a replacement facility for iostreams that would solve these problems.
We need to change the following sentence in [locale.category], paragraph 6 to specify that C is char and wchar_t:
"A template formal parameter with name C represents the set of all possible specializations on a parameter that satisfies the requirements for a character on which any member of the iostream components can be instantiated."
We also need to specify in 27 that the basic character operations, such as eq, lt, and assign use std::char_traits.
Daniel volunteered to provide wording.
[ 2009-09-19 Daniel provided wording. ]
[ 2009-10 Santa Cruz: ]
Leave as Open. Alisdair and/or Tom will provide wording based on discussions. We want to clearly state that streams and locales work just on char and wchar_t (except where otherwise specified).
[ 2010-02-06 Tom updated the proposed wording. ]
[ The original proposed wording is preserved here: ]
Change 22.3.1.1.1 [locale.category]/6:
[..] A template formal parameter with name C represents the set of all possible specializations on a char or wchar_t parameterthat satisfies the requirements for a character on which any of the iostream components can be instantiated. [..]Add the following sentence to the end of 22.4.2 [category.numeric]/2:
[..] These specializations refer to [..], and also for the ctype<> facet to perform character classification. Implementations are encouraged but not required to use the char_traits<charT> functions for all comparisons and assignments of characters of type charT that do not belong to the set of required specializations.Change 22.4.2.1.2 [facet.num.get.virtuals]/3:
Stage 2: If in==end then stage 2 terminates. Otherwise a charT is taken from in and local variables are initialized as if by
char_type ct = *in; using tr = char_traits<char_type>; const char_type* pos = tr::find(atoms, sizeof(src) - 1, ct); char c = src[find(atoms, atoms + sizeof(src) - 1, ct) - atomspos ? pos - atoms : sizeof(src) - 1]; if (tr::eq(ct,ct ==use_facet<numpunct<charT>(loc).decimal_point())) c = '.'; bool discard = tr::eq(ct,ct ==use_facet<numpunct<charT>(loc).thousands_sep()) && use_facet<numpunct<charT> >(loc).grouping().length() != 0;where the values src and atoms are defined as if by: [..]
[Remark of the author: I considered to replace the initialization "char_type ct = *in;" by the sequence "char_type ct; tr::assign(ct, *in);", but decided against it, because it is a copy-initialization context, not an assignment]
Add the following sentence to the end of 22.4.5 [category.time]/1:
[..] Their members use [..] , to determine formatting details. Implementations are encouraged but not required to use the char_traits<charT> functions for all comparisons and assignments of characters of type charT that do not belong to the set of required specializations.Change 22.4.5.1.1 [locale.time.get.members]/8 bullet 4:
The next element of fmt is equal to '%'For the next element c of fmt char_traits<char_type>::eq(c, use_facet<ctype<char_type>>(f.getloc()).widen('%')) == true, [..]Add the following sentence to the end of 22.4.6 [category.monetary]/2:
Their members use [..] to determine formatting details. Implementations are encouraged but not required to use the char_traits<charT> functions for all comparisons and assignments of characters of type charT that do not belong to the set of required specializations.Change 22.4.6.1.2 [locale.money.get.virtuals]/4:
[..] The value units is produced as if by:
for (int i = 0; i < n; ++i) buf2[i] = src[char_traits<charT>::find(atoms,atoms+sizeof(src), buf1[i]) - atoms]; buf2[n] = 0; sscanf(buf2, "%Lf", &units);Change 22.4.6.2.2 [locale.money.put.virtuals]/1:
[..] for character buffers buf1 and buf2. If for the first character c in digits or buf2is equal to ct.widen('-')char_traits<charT>::eq(c, ct.widen('-')) == true, [..]Add a footnote to the first sentence of 27.7.1.2.2 [istream.formatted.arithmetic]/1:
As in the case of the inserters, these extractors depend on the locale's num_get<> (22.4.2.1) object to perform parsing the input stream data.(footnote) [..]
footnote) If the traits of the input stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Add a footnote to the second sentence of 27.7.2.6.2 [ostream.inserters.arithmetic]/1:
Effects: The classes num_get<> and num_put<> handle locale-dependent numeric formatting and parsing. These inserter functions use the imbued locale value to perform numeric formatting.(footnote) [..]
footnote) If the traits of the output stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Add a footnote after the first sentence of 27.7.4 [ext.manip]/4:
Returns: An object of unspecified type such that if in is an object of type basic_istream<charT, traits> then the expression in >> get_money(mon, intl) behaves as if it called f(in, mon, intl), where the function f is defined as:(footnote) [..]
footnote) If the traits of the input stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Add a footnote after the first sentence of 27.7.4 [ext.manip]/5:
Returns: An object of unspecified type such that if out is an object of type basic_ostream<charT, traits> then the expression out << put_money(mon, intl) behaves as a formatted input function that calls f(out, mon, intl), where the function f is defined as:(footnote) [..]
footnote) If the traits of the output stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
13) Add a footnote after the first sentence of 27.7.4 [ext.manip]/8:
Returns: An object of unspecified type such that if in is an object of type basic_istream<charT, traits> then the expression in >>get_time(tmb, fmt) behaves as if it called f(in, tmb, fmt), where the function f is defined as:(footnote) [..]
footnote) If the traits of the input stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Add a footnote after the first sentence of 27.7.4 [ext.manip]/10:
Returns: An object of unspecified type such that if out is an object of type basic_ostream<charT, traits> then the expression out <<put_time(tmb, fmt) behaves as if it called f(out, tmb, fmt), where the function f is defined as:(footnote) [..]
footnote) If the traits of the output stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Proposed resolution:
Change 22.3.1.1.1 [locale.category]/6:
[..] A template formal parameter with name C represents the setof all possible specializations on aof types containing char, wchar_t, and any other implementation-defined character typeparameterthat satisfies the requirements for a character on which any of the iostream components can be instantiated. [..]
Add the following sentence to the end of 22.4.2 [category.numeric]/2:
[..] These specializations refer to [..], and also for the ctype<> facet to perform character classification. [Note: Implementations are encouraged but not required to use the char_traits<charT> functions for all comparisons and assignments of characters of type charT that do not belong to the set of required specializations - end note].
Change 22.4.2.1.2 [facet.num.get.virtuals]/3:
Stage 2: If in==end then stage 2 terminates. Otherwise a charT is taken from in and local variables are initialized as if by
char_type ct = *in; using tr = char_traits<char_type>; const char_type* pos = tr::find(atoms, sizeof(src) - 1, ct); char c = src[find(atoms, atoms + sizeof(src) - 1, ct) - atomspos ? pos - atoms : sizeof(src) - 1]; if (tr::eq(ct,ct ==use_facet<numpunct<charT>(loc).decimal_point())) c = '.'; bool discard = tr::eq(ct,ct ==use_facet<numpunct<charT>(loc).thousands_sep()) && use_facet<numpunct<charT> >(loc).grouping().length() != 0;where the values src and atoms are defined as if by: [..]
[Remark of the author: I considered to replace the initialization "char_type ct = *in;" by the sequence "char_type ct; tr::assign(ct, *in);", but decided against it, because it is a copy-initialization context, not an assignment]
Add the following sentence to the end of 22.4.5 [category.time]/1:
[..] Their members use [..] , to determine formatting details. [Note: Implementations are encouraged but not required to use the char_traits<charT> functions for all comparisons and assignments of characters of type charT that do not belong to the set of required specializations - end note].
Change 22.4.5.1.1 [locale.time.get.members]/8 bullet 4:
Add the following sentence to the end of 22.4.6 [category.monetary]/2:
Their members use [..] to determine formatting details. [Note: Implementations are encouraged but not required to use the char_traits<charT> functions for all comparisons and assignments of characters of type charT that do not belong to the set of required specializations - end note].
Change 22.4.6.1.2 [locale.money.get.virtuals]/4:
[..] The value units is produced as if by:
for (int i = 0; i < n; ++i) buf2[i] = src[char_traits<charT>::find(atoms,atoms+sizeof(src), buf1[i]) - atoms]; buf2[n] = 0; sscanf(buf2, "%Lf", &units);
Change 22.4.6.2.2 [locale.money.put.virtuals]/1:
[..] for character buffers buf1 and buf2. If for the first character c in digits or buf2is equal to ct.widen('-')char_traits<charT>::eq(c, ct.widen('-')) == true, [..]
Add a new paragraph after the first paragraph of 27.2.2 [iostreams.limits.pos]/1:
In the classes of clause 27, a template formal parameter with name charT represents one of the set of types containing char, wchar_t, and any other implementation-defined character type that satisfies the requirements for a character on which any of the iostream components can be instantiated.
Add a footnote to the first sentence of 27.7.1.2.2 [istream.formatted.arithmetic]/1:
As in the case of the inserters, these extractors depend on the locale's num_get<> (22.4.2.1) object to perform parsing the input stream data.(footnote) [..]
footnote) If the traits of the input stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Add a footnote to the second sentence of 27.7.2.6.2 [ostream.inserters.arithmetic]/1:
Effects: The classes num_get<> and num_put<> handle locale-dependent numeric formatting and parsing. These inserter functions use the imbued locale value to perform numeric formatting.(footnote) [..]
footnote) If the traits of the output stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Add a footnote after the first sentence of 27.7.4 [ext.manip]/4:
Returns: An object of unspecified type such that if in is an object of type basic_istream<charT, traits> then the expression in >> get_money(mon, intl) behaves as if it called f(in, mon, intl), where the function f is defined as:(footnote) [..]
footnote) If the traits of the input stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Add a footnote after the first sentence of 27.7.4 [ext.manip]/5:
Returns: An object of unspecified type such that if out is an object of type basic_ostream<charT, traits> then the expression out << put_money(mon, intl) behaves as a formatted input function that calls f(out, mon, intl), where the function f is defined as:(footnote) [..]
footnote) If the traits of the output stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Add a footnote after the first sentence of 27.7.4 [ext.manip]/8:
Returns: An object of unspecified type such that if in is an object of type basic_istream<charT, traits> then the expression in >>get_time(tmb, fmt) behaves as if it called f(in, tmb, fmt), where the function f is defined as:(footnote) [..]
footnote) If the traits of the input stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Add a footnote after the first sentence of 27.7.4 [ext.manip]/10:
Returns: An object of unspecified type such that if out is an object of type basic_ostream<charT, traits> then the expression out <<put_time(tmb, fmt) behaves as if it called f(out, tmb, fmt), where the function f is defined as:(footnote) [..]
footnote) If the traits of the output stream has different semantics for lt(), eq(), and assign() than char_traits<char_type>, this may give surprising results.
Section: 26.6.2.4 [valarray.sub] Status: Open Submitter: Martin Sebor Opened: 2003-09-18 Last modified: 2009-11-04
View all issues with Open status.
Discussion:
The standard fails to specify the behavior of valarray::operator[](slice) and other valarray subset operations when they are passed an "invalid" slice object, i.e., either a slice that doesn't make sense at all (e.g., slice (0, 1, 0) or one that doesn't specify a valid subset of the valarray object (e.g., slice (2, 1, 1) for a valarray of size 1).
[Kona: the LWG believes that invalid slices should invoke undefined behavior. Valarrays are supposed to be designed for high performance, so we don't want to require specific checking. We need wording to express this decision.]
[ Bellevue: ]
Please note that the standard also fails to specify the behavior of slice_array and gslice_array in the valid case. Bill Plauger will endeavor to provide revised wording for slice_array and gslice_array.
[ post-Bellevue: Bill provided wording. ]
[ 2009-07 Frankfurt ]
Move to Ready.
[ 2009-11-04 Pete opens: ]
The resolution to LWG issue 430 has not been applied --- there have been changes to the underlying text, and the resolution needs to be reworked.
Proposed resolution:
Insert after 26.6.2.4 [valarray.sub], paragraph 1:
The member operator is overloaded to provide several ways to select sequences of elements from among those controlled by *this. The first group of five member operators work in conjunction with various overloads of operator= (and other assigning operators) to allow selective replacement (slicing) of the controlled sequence. The selected elements must exist.
The first member operator selects element off. For example:
valarray<char> v0("abcdefghijklmnop", 16); v0[3] = 'A'; // v0 == valarray<char>("abcAefghijklmnop", 16)The second member operator selects those elements of the controlled sequence designated by slicearr. For example:
valarray<char> v0("abcdefghijklmnop", 16); valarray<char> v1("ABCDE", 5); v0[slice(2, 5, 3)] = v1; // v0 == valarray<char>("abAdeBghCjkDmnEp", 16)The third member operator selects those elements of the controlled sequence designated by gslicearr. For example:
valarray<char> v0("abcdefghijklmnop", 16); valarray<char> v1("ABCDEF", 6); const size_t lv[] = {2, 3}; const size_t dv[] = {7, 2}; const valarray<size_t> len(lv, 2), str(dv, 2); v0[gslice(3, len, str)] = v1; // v0 == valarray<char>("abcAeBgCijDlEnFp", 16)The fourth member operator selects those elements of the controlled sequence designated by boolarr. For example:
valarray<char> v0("abcdefghijklmnop", 16); valarray<char> v1("ABC", 3); const bool vb[] = {false, false, true, true, false, true}; v0[valarray<bool>(vb, 6)] = v1; // v0 == valarray<char>("abABeCghijklmnop", 16)The fifth member operator selects those elements of the controlled sequence designated by indarr. For example:
valarray<char> v0("abcdefghijklmnop", 16); valarray<char> v1("ABCDE", 5); const size_t vi[] = {7, 5, 2, 3, 8}; v0[valarray<size_t>(vi, 5)] = v1; // v0 == valarray<char>("abCDeBgAEjklmnop", 16)The second group of five member operators each construct an object that represents the value(s) selected. The selected elements must exist.
The sixth member operator returns the value of element off. For example:
valarray<char> v0("abcdefghijklmnop", 16); // v0[3] returns 'd'The seventh member operator returns an object of class valarray<Ty> containing those elements of the controlled sequence designated by slicearr. For example:
valarray<char> v0("abcdefghijklmnop", 16); // v0[slice(2, 5, 3)] returns valarray<char>("cfilo", 5)The eighth member operator selects those elements of the controlled sequence designated by gslicearr. For example:
valarray<char> v0("abcdefghijklmnop", 16); const size_t lv[] = {2, 3}; const size_t dv[] = {7, 2}; const valarray<size_t> len(lv, 2), str(dv, 2); // v0[gslice(3, len, str)] returns // valarray<char>("dfhkmo", 6)The ninth member operator selects those elements of the controlled sequence designated by boolarr. For example:
valarray<char> v0("abcdefghijklmnop", 16); const bool vb[] = {false, false, true, true, false, true}; // v0[valarray<bool>(vb, 6)] returns // valarray<char>("cdf", 3)The last member operator selects those elements of the controlled sequence designated by indarr. For example:
valarray<char> v0("abcdefghijklmnop", 16); const size_t vi[] = {7, 5, 2, 3, 8}; // v0[valarray<size_t>(vi, 5)] returns // valarray<char>("hfcdi", 5)
Section: 24.2 [iterator.requirements], 23.2 [container.requirements] Status: Open Submitter: Andy Koenig Opened: 2003-12-16 Last modified: 2009-11-03
View other active issues in [iterator.requirements].
View all other issues in [iterator.requirements].
View all issues with Open status.
Discussion:
What requirements does the standard place on equality comparisons between iterators that refer to elements of different containers. For example, if v1 and v2 are empty vectors, is v1.end() == v2.end() allowed to yield true? Is it allowed to throw an exception?
The standard appears to be silent on both questions.
[Sydney: The intention is that comparing two iterators from different containers is undefined, but it's not clear if we say that, or even whether it's something we should be saying in clause 23 or in clause 24. Intuitively we might want to say that equality is defined only if one iterator is reachable from another, but figuring out how to say it in any sensible way is a bit tricky: reachability is defined in terms of equality, so we can't also define equality in terms of reachability. ]
[ 2009-07 Frankfurt ]
Daniel volunteered to work on this.
[ 2009-09-20 Daniel provided wording. ]
[ 2009-10 Santa Cruz: ]
Leave as Open. Alisdair has volunteered to refine the wording.
Proposed resolution:
Insert a new paragraph between 24.2 [iterator.requirements]/7+8:
[..] The result of the application of functions in the library to invalid ranges is undefined.
The result of directly or indirectly evaluating any comparison function or the binary - operator with two iterator values as arguments that were obtained from two different ranges r1 and r2 (including their past-the-end values) which are not subranges of one common range is undefined, unless explicitly described otherwise.
Section: 18.8.1 [exception] Status: Ready Submitter: Martin Sebor Opened: 2004-06-28 Last modified: 2009-10-26
View all issues with Ready status.
Discussion:
[lib.exception] specifies the following:
exception (const exception&) throw();
exception& operator= (const exception&) throw();
-4- Effects: Copies an exception object.
-5- Notes: The effects of calling what() after assignment
are implementation-defined.
First, does the Note only apply to the assignment operator? If so, what are the effects of calling what() on a copy of an object? Is the returned pointer supposed to point to an identical copy of the NTBS returned by what() called on the original object or not?
Second, is this Note intended to extend to all the derived classes in section 19? I.e., does the standard provide any guarantee for the effects of what() called on a copy of any of the derived class described in section 19?
Finally, if the answer to the first question is no, I believe it constitutes a defect since throwing an exception object typically implies invoking the copy ctor on the object. If the answer is yes, then I believe the standard ought to be clarified to spell out exactly what the effects are on the copy (i.e., after the copy ctor was called).
[Redmond: Yes, this is fuzzy. The issue of derived classes is fuzzy too.]
[ Batavia: Howard provided wording. ]
[ Bellevue: ]
Eric concerned this is unimplementable, due to nothrow guarantees. Suggested implementation would involve reference counting.
Is the implied reference counting subtle enough to call out a note on implementation? Probably not.
If reference counting required, could we tighten specification further to require same pointer value? Probably an overspecification, especially if exception classes defer evalutation of final string to calls to what().
Remember issue moved open and not resolved at Batavia, but cannot remember who objected to canvas a disenting opinion - please speak up if you disagree while reading these minutes!
Move to Ready as we are accepting words unmodified.
[ Sophia Antipolis: ]
The issue was pulled from Ready. It needs to make clear that only homogenous copying is intended to be supported, not coping from a derived to a base.
[ Batavia (2009-05): ]
Howard supplied the following replacement wording for paragraph 7 of the proposed resolution:
-7- Postcondition: what() shall return the same NTBS as would be obtained by using static_cast to cast the rhs to the same types as the lhs and then calling what() on that possibly sliced object.Pete asks what "the same NTBS" means.
[ 2009-07-30 Niels adds: ]
Further discussion in the thread starting with c++std-lib-24512.
[ 2009-09-24 Niels provided updated wording: ]
I think the resolution should at least guarantee that the result of what() is independent of whether the compiler does copy-elision. And for any class derived from std::excepion that has a constructor that allows specifying a what_arg, it should make sure that the text of a user-provided what_arg is preserved, when the object is copied. Note that all the implementations I've tested already appear to satisfy the proposed resolution, including MSVC 2008 SP1, Apache stdcxx-4.2.1, GCC 4.1.2, GCC 4.3.2, and CodeGear C++ 6.13.
The proposed resolution was updated with help from Daniel Krügler; the update aims to clarify that the proposed postcondition only applies to homogeneous copying.
[ 2009-10 Santa Cruz: ]
Moved to Ready after inserting "publicly accessible" in two places.
Proposed resolution:
Change 18.8.1 [exception] to:
-1- The class exception defines the base class for the types of objects thrown as exceptions by C++ standard library components, and certain expressions, to report errors detected during program execution.
Each standard library class T that derives from class exception shall have a publicly accessible copy constructor and a publicly accessible copy assignment operator that do not exit with an exception. These member functions shall preserve the following postcondition: If two objects lhs and rhs both have dynamic type T, and lhs is a copy of rhs, then strcmp(lhs.what(), rhs.what()) == 0.
...
exception(const exception& rhs) throw(); exception& operator=(const exception& rhs) throw();-4- Effects: Copies an exception object.
-5- Remarks: The effects of calling what() after assignment are implementation-defined.-5- Postcondition: If *this and rhs both have dynamic type exception then strcmp(what(), rhs.what()) == 0.
Section: 22.4.1.1 [locale.ctype] Status: Ready Submitter: Martin Sebor Opened: 2004-07-01 Last modified: 2009-10-21
View all issues with Ready status.
Discussion:
Most ctype member functions come in two forms: one that operates on a single character at a time and another form that operates on a range of characters. Both forms are typically described by a single Effects and/or Returns clause.
The Returns clause of each of the single-character non-virtual forms suggests that the function calls the corresponding single character virtual function, and that the array form calls the corresponding virtual array form. Neither of the two forms of each virtual member function is required to be implemented in terms of the other.
There are three problems:
1. One is that while the standard does suggest that each non-virtual member function calls the corresponding form of the virtual function, it doesn't actually explicitly require it.
Implementations that cache results from some of the virtual member functions for some or all values of their arguments might want to call the array form from the non-array form the first time to fill the cache and avoid any or most subsequent virtual calls. Programs that rely on each form of the virtual function being called from the corresponding non-virtual function will see unexpected behavior when using such implementations.
2. The second problem is that either form of each of the virtual functions can be overridden by a user-defined function in a derived class to return a value that is different from the one produced by the virtual function of the alternate form that has not been overriden.
Thus, it might be possible for, say, ctype::widen(c) to return one value, while for ctype::widen(&c, &c + 1, &wc) to set wc to another value. This is almost certainly not intended. Both forms of every function should be required to return the same result for the same character, otherwise the same program using an implementation that calls one form of the functions will behave differently than when using another implementation that calls the other form of the function "under the hood."
3. The last problem is that the standard text fails to specify whether one form of any of the virtual functions is permitted to be implemented in terms of the other form or not, and if so, whether it is required or permitted to call the overridden virtual function or not.
Thus, a program that overrides one of the virtual functions so that it calls the other form which then calls the base member might end up in an infinite loop if the called form of the base implementation of the function in turn calls the other form.
Lillehammer: Part of this isn't a real problem. We already talk about caching. 22.1.1/6 But part is a real problem. ctype virtuals may call each other, so users don't know which ones to override to avoid avoid infinite loops.
This is a problem for all facet virtuals, not just ctype virtuals, so we probably want a blanket statement in clause 22 for all facets. The LWG is leaning toward a blanket prohibition, that a facet's virtuals may never call each other. We might want to do that in clause 27 too, for that matter. A review is necessary. Bill will provide wording.
[ 2009-07 Frankfurt, Howard provided wording directed by consensus. ]
[ 2009-10 Santa Cruz: ]
Move to Ready.
Proposed resolution:
Add paragraph 3 to 22.4 [locale.categories]:
-3- Within this clause it is unspecified if one virtual function calls another virtual function.
Rationale:
We are explicitly not addressing bullet item #2, thus giving implementors more latitude. Users will have to override both virtual functions, not just one.
Section: 24.2.2 [output.iterators] Status: Ready Submitter: Chris Jefferson Opened: 2004-10-13 Last modified: 2009-10-26
View all other issues in [output.iterators].
View all issues with Ready status.
Discussion:
The note on 24.1.2 Output iterators insufficiently limits what can be performed on output iterators. While it requires that each iterator is progressed through only once and that each iterator is written to only once, it does not require the following things:
Note: Here it is assumed that x is an output iterator of type X which has not yet been assigned to.
a) That each value of the output iterator is written to: The standard allows: ++x; ++x; ++x;
b) That assignments to the output iterator are made in order X a(x); ++a; *a=1; *x=2; is allowed
c) Chains of output iterators cannot be constructed: X a(x); ++a; X b(a); ++b; X c(b); ++c; is allowed, and under the current wording (I believe) x,a,b,c could be written to in any order.
I do not believe this was the intension of the standard?
[Lillehammer: Real issue. There are lots of constraints we intended but didn't specify. Should be solved as part of iterator redesign.]
[ 2009-07 Frankfurt ]
Bill provided wording according to consensus.
[ 2009-07-21 Alisdair requests change from Review to Open. See thread starting with c++std-lib-24459 for discussion. ]
[ 2009-10 Santa Cruz: ]
Modified wording. Set to Review.
[ 2009-10 Santa Cruz: ]
Move to Ready after looking at again in a larger group in Santa Cruz.
Proposed resolution:
Change Table 101 — Output iterator requirements in 24.2.2 [output.iterators]:
Table 101 — Output iterator requirements Expression Return type Operational semantics Assertion/note pre-/post-condition X(a) a = t is equivalent to X(a) = t. note: a destructor is assumed. X u(a);
X u = a;*r = o result is not used Post: r is not required to be dereferenceable. r is incrementable. ++r X& &r == &++r Post: r is dereferenceable, unless otherwise specified. r is not required to be incrementable. r++ convertible to const X& {X tmp = r;
++r;
return tmp;}Post: r is dereferenceable, unless otherwise specified. r is not required to be incrementable. *r++ = o; result is not used
Section: 26.7.3 [partial.sum] Status: Ready Submitter: Marc Schoolderman Opened: 2006-02-06 Last modified: 2009-10-24
View all issues with Ready status.
Discussion:
There are some problems in the definition of partial_sum and adjacent_difference in 26.4 [lib.numeric.ops]
Unlike accumulate and inner_product, these functions are not parametrized on a "type T", instead, 26.4.3 [lib.partial.sum] simply specifies the effects clause as;
Assigns to every element referred to by iterator i in the range [result,result + (last - first)) a value correspondingly equal to
((...(* first + *( first + 1)) + ...) + *( first + ( i - result )))
And similarly for BinaryOperation. Using just this definition, it seems logical to expect that:
char i_array[4] = { 100, 100, 100, 100 };
int o_array[4];
std::partial_sum(i_array, i_array+4, o_array);
Is equivalent to
int o_array[4] = { 100, 100+100, 100+100+100, 100+100+100+100 };
i.e. 100, 200, 300, 400, with addition happening in the result type, int.
Yet all implementations I have tested produce 100, -56, 44, -112, because they are using an accumulator of the InputIterator's value_type, which in this case is char, not int.
The issue becomes more noticeable when the result of the expression *i + *(i+1) or binary_op(*i, *i-1) can't be converted to the value_type. In a contrived example:
enum not_int { x = 1, y = 2 };
...
not_int e_array[4] = { x, x, y, y };
std::partial_sum(e_array, e_array+4, o_array);
Is it the intent that the operations happen in the input type, or in the result type?
If the intent is that operations happen in the result type, something like this should be added to the "Requires" clause of 26.4.3/4 [lib.partial.sum]:
The type of *i + *(i+1) or binary_op(*i, *(i+1)) shall meet the requirements of CopyConstructible (20.1.3) and Assignable (23.1) types.
(As also required for T in 26.4.1 [lib.accumulate] and 26.4.2 [lib.inner.product].)
The "auto initializer" feature proposed in N1894 is not required to implement partial_sum this way. The 'narrowing' behaviour can still be obtained by using the std::plus<> function object.
If the intent is that operations happen in the input type, then something like this should be added instead;
The type of *first shall meet the requirements of CopyConstructible (20.1.3) and Assignable (23.1) types. The result of *i + *(i+1) or binary_op(*i, *(i+1)) shall be convertible to this type.
The 'widening' behaviour can then be obtained by writing a custom proxy iterator, which is somewhat involved.
In both cases, the semantics should probably be clarified.
26.4.4 [lib.adjacent.difference] is similarly underspecified, although all implementations seem to perform operations in the 'result' type:
unsigned char i_array[4] = { 4, 3, 2, 1 };
int o_array[4];
std::adjacent_difference(i_array, i_array+4, o_array);
o_array is 4, -1, -1, -1 as expected, not 4, 255, 255, 255.
In any case, adjacent_difference doesn't mention the requirements on the value_type; it can be brought in line with the rest of 26.4 [lib.numeric.ops] by adding the following to 26.4.4/2 [lib.adjacent.difference]:
The type of *first shall meet the requirements of CopyConstructible (20.1.3) and Assignable (23.1) types."
[ Berlin: Giving output iterator's value_types very controversial. Suggestion of adding signatures to allow user to specify "accumulator". ]
[ Bellevue: ]
The intent of the algorithms is to perform their calculations using the type of the input iterator. Proposed wording provided.
[ Sophia Antipolis: ]
We did not agree that the proposed resolution was correct. For example, when the arguments are types (float*, float*, double*), the highest-quality solution would use double as the type of the accumulator. If the intent of the wording is to require that the type of the accumulator must be the input_iterator's value_type, the wording should specify it.
[ 2009-05-09 Alisdair adds: ]
Now that we have the facility, the 'best' accumulator type could probably be deduced as:
std::common_type<InIter::value_type, OutIter::reference>::typeThis type would then have additional requirements of constructability and incrementability/assignability.
If this extracting an accumulator type from a pair/set of iterators (with additional requirements on that type) is a problem for multiple functions, it might be worth extracting into a SharedAccumulator concept or similar.
I'll go no further in writing up wording now, until the group gives a clearer indication of preferred direction.
[ 2009-07 Frankfurt ]
The proposed resolution isn't quite right. For example, "the type of *first" should be changed to "iterator::value_type" or similar. Daniel volunteered to correct the wording.
[ 2009-07-29 Daniel corrected wording. ]
[ 2009-10 Santa Cruz: ]
Move to Ready.
Proposed resolution:
Change 26.7.3 [partial.sum]/1 as indicated:
Effects: Let VT be InputIterator's value type. For a nonempty range, initializes an accumulator acc of type VT with *first and performs *result = acc. For every iterator i in [first + 1, last) in order, acc is then modified by acc = acc + *i or acc = binary_op(acc, *i) and is assigned to *(result + (i - first)).
Assigns to every element referred to by iterator i in the range [result,result + (last - first)) a value correspondingly equal to((...(*first + *(first + 1)) + ...) + *(first + (i - result)))
orbinary_op(binary_op(..., binary_op(*first, *(first + 1)),...), *(first + (i - result)))
Change 26.7.3 [partial.sum]/3 as indicated:
Complexity: Exactly max((last - first) - 1, 0) applications ofbinary_opthe binary operation.
Change 26.7.3 [partial.sum]/4 as indicated:
Requires: VT shall be constructible from the type of *first, the result of acc + *i or binary_op(acc, *i) shall be implicitly convertible to VT, and the result of the expression acc shall be writable to the result output iterator. In the ranges [first,last] and [result,result + (last - first)] [..]
Change 26.7.4 [adjacent.difference]/1 as indicated:
Effects: Let VT be InputIterator's value type. For a nonempty range, initializes an accumulator acc of type VT with *first and performs *result = acc. For every iterator i in [first + 1, last) in order, initializes a value val of type VT with *i, assigns the result of val - acc or binary_op(val, acc) to *(result + (i - first)) and modifies acc = std::move(val).
Assigns to every element referred to by iterator i in the range [result + 1, result + (last - first)) a value correspondingly equal to*(first + (i - result)) - *(first + (i - result) - 1)
orbinary_op(*(first + (i - result)), *(first + (i - result) - 1)).
result gets the value of *first.
Change 26.7.4 [adjacent.difference]/2 as indicated:
Requires: VT shall be MoveAssignable ([moveassignable]) and shall be constructible from the type of *first. The result of the expression acc and the result of the expression val - acc or binary_op(val, acc) shall be writable to the result output iterator. In the ranges [first,last] [..]
Change 26.7.4 [adjacent.difference]/5 as indicated:
Complexity: Exactly max((last - first) - 1, 0) applications ofbinary_opthe binary operation.
Section: 25.4 [alg.sorting] Status: Tentatively Ready Submitter: Martin Sebor Opened: 2006-02-05 Last modified: 2010-01-22
View all other issues in [alg.sorting].
View all issues with Tentatively Ready status.
Discussion:
In 25, p8 we allow BinaryPredicates to return a type that's convertible to bool but need not actually be bool. That allows predicates to return things like proxies and requires that implementations be careful about what kinds of expressions they use the result of the predicate in (e.g., the expression in if (!pred(a, b)) need not be well-formed since the negation operator may be inaccessible or return a type that's not convertible to bool).
Here's the text for reference:
...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 if (binary_pred(*first1, first2)){...}.
In 25.3, p2 we require that the Compare function object return true of false, which would seem to preclude such proxies. The relevant text is here:
Compare is used as a function object which returns true if the first argument is less than the second, and false otherwise...
[ Portland: Jack to define "convertible to bool" such that short circuiting isn't destroyed. ]
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-10 Santa Cruz: ]
Move to Review once wording received. Stefanus to send proposed wording.
[ 2009-10 Santa Cruz: ]
Move to Review once wording received. Stefanus to send proposed wording.
[ 2009-10-24 Stefanus supplied wording. ]
Move to Review once wording received. Stefanus to send proposed wording. Old proposed wording here:I think we could fix this by rewording 25.3, p2 to read somthing like:
-2- Compare is
used as a function object which returns true if the first argumenta BinaryPredicate. The return value of the function call operator applied to an object of type Compare, when converted to type bool, 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.
[ 2010-01-17: ]
Howard expresses concern that the current direction of the proposed wording outlaws expressions such as:
if (!comp(x, y))Daniel provides wording which addresses that concern.
The previous wording is saved here:
Change 25.4 [alg.sorting] p2:
Compare is used as a function object. The return value of the function call operator applied to an object of type Compare, when converted to type bool, yields true if the first argument of the callwhich returns true if the first argumentis 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.
[ 2010-01-22 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]
Proposed resolution:
Change 25.1 [algorithms.general]/7+8 as indicated. [This change is recommended to bring the return value requirements of BinaryPredicate and Compare in sync.]
7 The Predicate parameter is used whenever an algorithm expects a function object 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
if (pred(*first)){...}pred(*first) contextually converted to bool (4 [conv]). The function object pred shall not apply any nonconstant function through the dereferenced iterator. This function object may be a pointer to function, or an object of a type with an appropriate function call operator.8 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
if (binary_pred(*first1, *first2)){...}binary_pred(*first1, *first2) contextually converted to bool (4 [conv]). BinaryPredicate always takes the first iterator type as its first argument, that is, in those cases when T value is part of the signature, it should work correctly in thecontext of if (binary_pred(*first1, value)){...}construct binary_pred(*first1, value) contextually converted to bool (4 [conv]). binary_pred shall not apply any non-constant function through the dereferenced iterators.
Change 25.4 [alg.sorting]/2 as indicated:
2 Compare isused asa function object type (20.7 [function.objects]). The return value of the function call operation applied to an object of type Compare, when contextually converted to type bool (4 [conv]), yields true if the first argument of the callwhich returns true if the first argumentis 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.
Section: 23.2.5 [unord.req] Status: Tentatively Ready Submitter: Joaquín M López Muñoz Opened: 2006-06-13 Last modified: 2010-02-11
View other active issues in [unord.req].
View all other issues in [unord.req].
View all issues with Tentatively Ready status.
Discussion:
See N2023 for full discussion.
[ 2009-12-11 Paolo opens: ]
I'm asking for DR 579 to be re-opened, basing on recent discussions on the library reflector, see Message c++std-lib-26040 and replies.
[ 2010-02-07 Paolo updates wording. ]
As pointed out by Chris in c++std-lib-26040, that an erase(unordered_container, iterator) returning an iterator can easily implemented in user code, if needed; that actually returning an iterator costs nothing for the overload taking two iterators, thus that proposed change is only for consistency; that forward_list::erase_after also returns void (for different reasons, granted, but isn't that any "erase" function in the containers uniformly returns an iterator); that, also in thread started by Chris' message, Alberto pointed out that the proxy idea isn't a good one; that users both of the GNU and Boost implementations are reporting serious performance problems with the current version returning an iterator.
[ 2010-02-07 Original wording saved here: ]
Option 1:
The problem can be eliminated by omitting the requirement that a.erase(q) return an iterator. This is, however, in contrast with the equivalent requirements for other standard containers.
Option 2:
a.erase(q) can be made to compute the next iterator only when explicitly requested: the technique consists in returning a proxy object implicitly convertible to iterator, so that
iterator q1=a.erase(q);works as expected, while
a.erase(q);does not ever invoke the conversion-to-iterator operator, thus avoiding the associated computation. To allow this technique, some sections of TR1 along the line "return value is an iterator..." should be changed to "return value is an unspecified object implicitly convertible to an iterator..." Although this trick is expected to work transparently, it can have some collateral effects when the expression a.erase(q) is used inside generic code.
[ 2010-02-09 Moved to Tentatively Ready after 6 positive votes on c++std-lib. ]
Rationale:
N2023 was discussed in Portland and the consensus was that there appears to be no need for either change proposed in the paper. The consensus opinion was that since the iterator could serve as its own proxy, there appears to be no need for the change. In general, "converts to" is undesirable because it interferes with template matching.
Post Toronto: There does not at this time appear to be consensus with the Portland consensus.
[ Bellevue: ]
The Bellevue review of this issue reached consensus with the Portland consensus, in contravention of the Toronto non-consensus. Common implementations have the iterator readily available, and most common uses depend on the iterator being returned.
****
The rationale for the change in direction here is best summarized by Paolo's 2010-02-07 comment.
Proposed resolution:
In 23.2.5 [unord.req], Table 98, change return type from iterator to void for the following rows:
Table 98 — Unordered associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity a.erase(q) iteratorvoidErases the element pointed to by q. Return value is the iterator immediately following q prior to the erasure.Average case O(1), worst case O(a.size()). a.erase(q1, q2) iteratorvoidErases all elements in the range [q1, q2). Return value is the iterator immediately following the erased elements prior to the erasure.Average case linear in distance(q1, q2), worst case O(a.size()).
Adjust the declarations accordingly in 23.5.1 [unord.map], 23.5.2 [unord.multimap], 23.5.3 [unord.set], and 23.5.4 [unord.multiset].
iteratorvoid erase(const_iterator position); ...iteratorvoid erase(const_iterator first, const_iterator last);
Section: 20.2.1 [utility.arg.requirements] Status: Open Submitter: Niels Dekker Opened: 2006-11-02 Last modified: 2010-02-03
View other active issues in [utility.arg.requirements].
View all other issues in [utility.arg.requirements].
View all issues with Open status.
Discussion:
It seems undesirable to define the Swappable requirement in terms of CopyConstructible and Assignable requirements. And likewise, once the MoveConstructible and MoveAssignable requirements (N1860) have made it into the Working Draft, it seems undesirable to define the Swappable requirement in terms of those requirements. Instead, it appears preferable to have the Swappable requirement defined exclusively in terms of the existence of an appropriate swap function.
Section 20.1.4 [lib.swappable] of the current Working Draft (N2009) says:
The Swappable requirement is met by satisfying one or more of the following conditions:
- T is Swappable if T satisfies the CopyConstructible requirements (20.1.3) and the Assignable requirements (23.1);
- T is Swappable if a namespace scope function named swap exists in the same namespace as the definition of T, such that the expression swap(t,u) is valid and has the semantics described in Table 33.
I can think of three disadvantages of this definition:
If a client's type T satisfies the first condition (T is both CopyConstructible and Assignable), the client cannot stop T from satisfying the Swappable requirement without stopping T from satisfying the first condition.
A client might want to stop T from satisfying the Swappable requirement, because swapping by means of copy construction and assignment might throw an exception, and she might find a throwing swap unacceptable for her type. On the other hand, she might not feel the need to fully implement her own swap function for this type. In this case she would want to be able to simply prevent algorithms that would swap objects of type T from being used, e.g., by declaring a swap function for T, and leaving this function purposely undefined. This would trigger a link error, if an attempt would be made to use such an algorithm for this type. For most standard library implementations, this practice would indeed have the effect of stopping T from satisfying the Swappable requirement.
A client's type T that does not satisfy the first condition can not be made Swappable by providing a specialization of std::swap for T.
While I'm aware about the fact that people have mixed feelings about providing a specialization of std::swap, it is well-defined to do so. It sounds rather counter-intuitive to say that T is not Swappable, if it has a valid and semantically correct specialization of std::swap. Also in practice, providing such a specialization will have the same effect as satisfying the Swappable requirement.
For a client's type T that satisfies both conditions of the Swappable requirement, it is not specified which of the two conditions prevails. After reading section 20.1.4 [lib.swappable], one might wonder whether objects of T will be swapped by doing copy construction and assignments, or by calling the swap function of T.
I'm aware that the intention of the Draft is to prefer calling the swap function of T over doing copy construction and assignments. Still in my opinion, it would be better to make this clear in the wording of the definition of Swappable.
I would like to have the Swappable requirement defined in such a way that the following code fragment will correctly swap two objects of a type T, if and only if T is Swappable:
using std::swap; swap(t, u); // t and u are of type T.
This is also the way Scott Meyers recommends calling a swap function, in Effective C++, Third Edition, item 25.
Most aspects of this issue have been dealt with in a discussion on comp.std.c++ about the Swappable requirement, from 13 September to 4 October 2006, including valuable input by David Abrahams, Pete Becker, Greg Herlihy, Howard Hinnant and others.
[ San Francisco: ]
Recommend NAD. Solved by N2774.
[ 2009-07 Frankfurt ]
Moved to Open. Waiting for non-concepts draft.
[ 2009-11-08 Howard adds: ]
This issue is very closely related to 742.
[ 2010-02-03 Sean Hunt adds: ]
While reading N3000, I independently came across Issue 594. Having seen that it's an issue under discussion, I think the proposed wording needs fixing to something more like "...function call swap(t,u) that includes std::swap in its overload set is valid...", because "...is valid within the namespace std..." does not allow other libraries to simply use the Swappable requirement by referring to the standard's definition, since they cannot actually perform any calls within std.
This wording I suggested would also make overloads visible in the same scope as the `using std::swap` valid for Swappable requirements; a more complex wording limiting the non-ADL overload set to std::swap might be required.
Proposed resolution:
Change section 20.1.4 [lib.swappable] as follows:
The Swappable requirement is met by satisfying
one or more of the following conditions:the following condition:
T is Swappable if T satisfies the CopyConstructible requirements (20.1.3) and the Assignable requirements (23.1);T is Swappable if a namespace scope function named swap exists in the same namespace as the definition of T, such that the expression swap(t,u) is valid and has the semantics described in Table 33.T is Swappable if an unqualified function call swap(t,u) is valid within the namespace std, and has the semantics described in Table 33.
Section: 17 [library] Status: Open Submitter: Martin Sebor Opened: 2007-01-20 Last modified: 2009-10-20
View other active issues in [library].
View all other issues in [library].
View all issues with Open status.
Duplicate of: 895
Discussion:
Many member functions of basic_string are overloaded,
with some of the overloads taking a string argument,
others value_type*, others size_type, and
others still iterators. Often, the requirements on one of
the overloads are expressed in the form of Effects,
Throws, and in the Working Paper
(N2134)
also Remark clauses, while those on the rest of the overloads
via a reference to this overload and using a Returns clause.
The difference between the two forms of specification is that per 17.5.1.4 [structure.specifications], p3, an Effects clause specifies "actions performed by the functions," i.e., its observable effects, while a Returns clause is "a description of the return value(s) of a function" that does not impose any requirements on the function's observable effects.
Since only Notes are explicitly defined to be informative and all other paragraphs are explicitly defined to be normative, like Effects and Returns, the new Remark clauses also impose normative requirements.
So by this strict reading of the standard there are some member
functions of basic_string that are required to throw an
exception under some conditions or use specific traits members while
many other otherwise equivalent overloads, while obliged to return the
same values, aren't required to follow the exact same requirements
with regards to the observable effects.
Here's an example of this problem that was precipitated by the change from informative Notes to normative Remarks (presumably made to address 424):
In the Working Paper, find(string, size_type) contains a
Remark clause (which is just a Note in the current
standard) requiring it to use traits::eq().
find(const charT *s, size_type pos) is specified to
return find(string(s), pos) by a Returns clause
and so it is not required to use traits::eq(). However,
the Working Paper has replaced the original informative Note
about the function using traits::length() with a
normative requirement in the form of a Remark. Calling
traits::length() may be suboptimal, for example when the
argument is a very long array whose initial substring doesn't appear
anywhere in *this.
Here's another similar example, one that existed even prior to the introduction of Remarks:
insert(size_type pos, string, size_type, size_type) is
required to throw out_of_range if pos >
size().
insert(size_type pos, string str) is specified to return
insert(pos, str, 0, npos) by a Returns clause and
so its effects when pos > size() are strictly speaking
unspecified.
I believe a careful review of the current Effects and Returns clauses is needed in order to identify all such problematic cases. In addition, a review of the Working Paper should be done to make sure that the newly introduced normative Remark clauses do not impose any undesirable normative requirements in place of the original informative Notes.
[ Batavia: Alan and Pete to work. ]
[ Bellevue: Marked as NAD Editorial. ]
[ Post-Sophia Antipolis: Martin indicates there is still work to be done on this issue. Reopened. ]
[ Batavia (2009-05): ]
Tom proposes we say that, unless specified otherwise, it is always the caller's responsibility to verify that supplied arguments meet the called function's requirements. If further semantics are specified (e.g., that the function throws under certain conditions), then it is up to the implementer to check those conditions. Alan feels strongly that our current use of Requires in this context is confusing, especially now that requires is a new keyword.
[ 2009-07 Frankfurt ]
Move to Tentatively NAD.
[ 2009 Santa Cruz: ]
Move to Open. Martin will work on proposed wording.
Proposed resolution:
Section: 25 [algorithms] Status: Tentatively NAD Submitter: James Kanze Opened: 2007-01-31 Last modified: 2010-02-03
View other active issues in [algorithms].
View all other issues in [algorithms].
View all issues with Tentatively NAD status.
Discussion:
The general requirements for BinaryPredicate (in 25 [algorithms]/8) contradict the implied specific requirements for some functions. In particular, it says that:
[...] 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 if (binary_pred (*first1 , *first2 )){...}. BinaryPredicate always takes the first iterator type as its first argument, that is, in those cases when T value is part of the signature, it should work correctly in the context of if (binary_pred (*first1 , value)){...}.
In the description of upper_bound (25.4.3.2 [upper.bound]/2), however, the use is described as "!comp(value, e)", where e is an element of the sequence (a result of dereferencing *first).
In the description of lexicographical_compare, we have both "*first1 < *first2" and "*first2 < *first1" (which presumably implies "comp( *first1, *first2 )" and "comp( *first2, *first1 )".
Logically, the BinaryPredicate is used as an ordering relationship, with the semantics of "less than". Depending on the function, it may be used to determine equality, or any of the inequality relationships; doing this requires being able to use it with either parameter first. I would thus suggest that the requirement be:
Alternatively, one could specify an order for each function. IMHO, this would be more work for the committee, more work for the implementors, and of no real advantage for the user: some functions, such as lexicographical_compare or equal_range, will still require both functions, and it seems like a much easier rule to teach that both functions are always required, rather than to have a complicated list of when you only need one, and which one.
[ Toronto: Moved to Open. ConceptGCC seems to get lower_bound and upper_bound to work withoutt these changes. ]
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-10 Santa Cruz: ]
Move to Review. The small problem with the "iterator type" will be fixed. The cited functions (lower_bound, uppwer_bound, equal_range) don't actually use BinaryPredicate , and where it is used, it is consistent with [algorithm]/8, so the main complaint of the issue is moot.
[ 2010-01-16 Beman clarified wording. ]
[ 2010-01-31: Moved to Tentatively NAD after 5 positive votes on c++std-lib. Rationale added below. ]
Rationale:
[ post San Francisco: ]
Solved by N2759.
2010-01-31: The draft standard is well specified as is, and this specification is desired. Issues 556 and 870 solve the remaining unclearness regarding the meaning of BinaryPredicate.
Proposed resolution:
Change 25 [algorithms] paragraph 8 as indicated:
8 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. BinaryPredicate always takes the first iterator value_type as one of its arguments; which argument is unspecified.
In other words, ifIf an algorithm takes BinaryPredicate binary_pred as its argument and first1 and first2 as its iterator arguments, it should work correctly both in the construct if (binary_pred(*first1, *first2)){...} and if (binary_pred (*first2, *first1)){...}.BinaryPredicate always takes the first iterator type as its first argument, that is, inIn those cases when T value is part of the signature, it should work correctly in the context of if (binary_pred(*first1, value)){...} and of if (binary_pred (value, *first1)){...}.binary_pred shall not apply any non-constant function through the dereferenced iterators.[Note: if the two types are not identical, and neither is convertable to the other, this may require that the BinaryPredicate be a functional object with two overloaded operator()() functions. — end note]
Section: 22.4.2.2.2 [facet.num.put.virtuals] Status: Ready Submitter: John Salmon Opened: 2007-04-20 Last modified: 2009-10-21
View other active issues in [facet.num.put.virtuals].
View all other issues in [facet.num.put.virtuals].
View all issues with Ready status.
Discussion:
I am trying to understand how TR1 supports hex float (%a) output.
As far as I can tell, it does so via the following:
8.15 Additions to header <locale> [tr.c99.locale]
In subclause 22.4.2.2.2 [facet.num.put.virtuals], Table 58 Floating-point conversions, after the line: floatfield == ios_base::scientific %E
add the two lines:
floatfield == ios_base::fixed | ios_base::scientific && !uppercase %a floatfield == ios_base::fixed | ios_base::scientific %A 2
[Note: The additional requirements on print and scan functions, later in this clause, ensure that the print functions generate hexadecimal floating-point fields with a %a or %A conversion specifier, and that the scan functions match hexadecimal floating-point fields with a %g conversion specifier. end note]
Following the thread, in 22.4.2.2.2 [facet.num.put.virtuals], we find:
For conversion from a floating-point type, if (flags & fixed) != 0 or if str.precision() > 0, then str.precision() is specified in the conversion specification.
This would seem to imply that when floatfield == fixed|scientific, the precision of the conversion specifier is to be taken from str.precision(). Is this really what's intended? I sincerely hope that I'm either missing something or this is an oversight. Please tell me that the committee did not intend to mandate that hex floats (and doubles) should by default be printed as if by %.6a.
[ Howard: I think the fundamental issue we overlooked was that with %f, %e, %g, the default precision was always 6. With %a the default precision is not 6, it is infinity. So for the first time, we need to distinguish between the default value of precision, and the precision value 6. ]
[ 2009-07 Frankfurt ]
Leave this open for Robert and Daniel to work on.
Straw poll: Disposition?
- Default is %.6a (i.e. NAD): 2
- Always %a (no precision): 6
- precision(-1) == %a: 3
Daniel and Robert have direction to write up wording for the "always %a" solution.
[ 2009-07-15 Robert provided wording. ]
[ 2009-10 Santa Cruz: ]
Move to Ready.
Proposed resolution:
Change 22.4.2.2.2 [facet.num.put.virtuals], Stage 1, under p5 (near the end of Stage 1):
For conversion from a floating-point type, str.precision() is specified as precision in the conversion specification if floatfield != (ios_base::fixed | ios_base::scientific), else no precision is specified.
[ Kona (2007): Robert volunteers to propose wording. ]
Section: 23.5 [unord] Status: Tentatively Ready Submitter: Howard Hinnant Opened: 2007-05-05 Last modified: 2010-02-11
View other active issues in [unord].
View all other issues in [unord].
View all issues with Tentatively Ready status.
Discussion:
Move semantics are missing from the unordered containers. The proposed resolution below adds move-support consistent with N1858 and the current working draft.
The current proposed resolution simply lists the requirements for each function. These might better be hoisted into the requirements table for unordered associative containers. Futhermore a mild reorganization of the container requirements could well be in order. This defect report is purposefully ignoring these larger issues and just focusing on getting the unordered containers "moved".
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-10-17 Removed rvalue-swaps from wording. ]
[ 2009-10 Santa Cruz: ]
Move to Review. Alisdair will review proposed wording.
[ 2009-10-29 Daniel updates wording. ]
[ 2010-01-26 Alisdair updates wording. ]
[ 2010-02-10 Howard updates wording to reference the unordered container requirements table (modified by 704) as much as possible. ]
[ Voted to WP in Bellevue. ]
[ post Bellevue, Pete notes: ]
Please remind people who are reviewing issues to check that the text modifications match the current draft. Issue 676, for example, adds two overloads for unordered_map::insert taking a hint. One takes a const_iterator and returns a const_iterator, and the other takes an iterator and returns an iterator. This was correct at the time the issue was written, but was changed in Toronto so there is only one hint overload, taking a const_iterator and returning an iterator.
This issue is not ready. In addition to the relatively minor signature problem I mentioned earlier, it puts requirements in the wrong places. Instead of duplicating requirements throughout the template specifications, it should put them in the front matter that talks about requirements for unordered containers in general. This presentation problem is editorial, but I'm not willing to do the extensive rewrite that it requires. Please put it back into Open status.
[ 2010-02-11 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]
Rationale:
[ San Francisco: ]
Solved by N2776.
[ Rationale is obsolete. ]
Proposed resolution:
unordered_map
Change 23.5.1 [unord.map]:
class unordered_map
{
...
unordered_map(const unordered_map&);
unordered_map(unordered_map&&);
unordered_map(const Allocator&);
unordered_map(const unordered_map&, const Allocator&);
unordered_map(unordered_map&&, const Allocator&);
...
unordered_map& operator=(const unordered_map&);
unordered_map& operator=(unordered_map&&);
...
// modifiers
...
std::pair<iterator, bool> insert(const value_type& obj);
template <class P> pair<iterator, bool> insert(P&& obj);
iterator insert(const_iterator hint, const value_type& obj);
template <class P> iterator insert(const_iterator hint, P&& obj);
...
mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);
...
};
Add to 23.5.1.2 [unord.map.elem]:
mapped_type& operator[](const key_type& k);...
Requires: key_type shall be CopyConstructible and mapped_type shall be DefaultConstructible.
Complexity: Average case O(1), worst case O(size()).
mapped_type& operator[](key_type&& k);Requires: key_type shall be MoveConstructible and mapped_type shall be DefaultConstructible.
Effects: If the unordered_map does not already contain an element whose key is equivalent to k , inserts the value value_type(std::move(k), mapped_type()).
Returns: A reference to x.second, where x is the (unique) element whose key is equivalent to k.
Complexity: Average case O(1), worst case O(size()).
Add new section [unord.map.modifiers]:
template <class P> pair<iterator, bool> insert(P&& x);Requires: value_type is constructible from std::forward<P>(x).
Effects: Inserts x converted to value_type if and only if there is no element in the container with key equivalent to the key of value_type(x).
Returns: The bool component of the returned pair indicates whether the insertion takes place, and the iterator component points to the element with key equivalent to the key of value_type(x).
Complexity: Average case O(1), worst case O(size()).
Remarks: P shall be implicitly convertible to value_type, else this signature shall not participate in overload resolution.
template <class P> iterator insert(const_iterator hint, P&& x);Requires: value_type is constructible from std::forward<P>(x).
Effects: Inserts x converted to value_type if and only if there is no element in the container with key equivalent to the key of value_type(x). The iterator hint is a hint pointing to where the search should start. Implementations are permitted to ignore the hint.
Returns: An iterator pointing to the element with key equivalent to the key of value_type(x).
Complexity: Average case O(1), worst case O(size()).
Remarks: P shall be implicitly convertible to value_type, else this signature shall not participate in overload resolution.
unordered_multimap
Change 23.5.2 [unord.multimap]:
class unordered_multimap
{
...
unordered_multimap(const unordered_multimap&);
unordered_multimap(unordered_multimap&&);
unordered_multimap(const Allocator&);
unordered_multimap(const unordered_multimap&, const Allocator&);
unordered_multimap(unordered_multimap&&, const Allocator&);
...
unordered_multimap& operator=(const unordered_multimap&);
unordered_multimap& operator=(unordered_multimap&&);
...
// modifiers
...
iterator insert(const value_type& obj);
template <class P> iterator insert(P&& obj);
iterator insert(const_iterator hint, const value_type& obj);
template <class P> iterator insert(const_iterator hint, P&& obj);
...
};
Add new section [unord.multimap.modifiers]:
template <class P> iterator insert(P&& x);Requires: value_type is constructible from std::forward<P>(x).
Effects: Inserts x converted to value_type.
Returns: An iterator pointing to the element with key equivalent to the key of value_type(x).
Complexity: Average case O(1), worst case O(size()).
Remarks: P shall be implicitly convertible to value_type, else this signature shall not participate in overload resolution.
template <class P> iterator insert(const_iterator hint, P&& x);Requires: value_type is constructible from std::forward<P>(x).
Effects: Inserts x converted to value_type if and only if there is no element in the container with key equivalent to the key of value_type(x). The iterator hint is a hint pointing to where the search should start. Implementations are permitted to ignore the hint.
Returns: An iterator pointing to the element with key equivalent to the key of value_type(x).
Complexity: Average case O(1), worst case O(size()).
Remarks: P shall be implicitly convertible to value_type, else this signature shall not participate in overload resolution.
unordered_set
Change 23.5.3 [unord.set]:
class unordered_set
{
...
unordered_set(const unordered_set&);
unordered_set(unordered_set&&);
unordered_set(const Allocator&);
unordered_set(const unordered_set&, const Allocator&);
unordered_set(unordered_set&&, const Allocator&);
...
unordered_set& operator=(const unordered_set&);
unordered_set& operator=(unordered_set&&);
...
// modifiers
...
std::pair<iterator, bool> insert(const value_type& obj);
pair<iterator, bool> insert(value_type&& obj);
iterator insert(const_iterator hint, const value_type& obj);
iterator insert(const_iterator hint, value_type&& obj);
...
};
unordered_multiset
Change 23.5.4 [unord.multiset]:
class unordered_multiset
{
...
unordered_multiset(const unordered_multiset&);
unordered_multiset(unordered_multiset&&);
unordered_multiset(const Allocator&);
unordered_multiset(const unordered_multiset&, const Allocator&);
unordered_multiset(unordered_multiset&&, const Allocator&);
...
unordered_multiset& operator=(const unordered_multiset&);
unordered_multiset& operator=(unordered_multiset&&);
...
// modifiers
...
iterator insert(const value_type& obj);
iterator insert(value_type&& obj);
iterator insert(const_iterator hint, const value_type& obj);
iterator insert(const_iterator hint, value_type&& obj);
...
};
Section: 23.2 [container.requirements] Status: Tentatively Ready Submitter: Howard Hinnant Opened: 2007-05-20 Last modified: 2010-02-11
View other active issues in [container.requirements].
View all other issues in [container.requirements].
View all issues with Tentatively Ready status.
Discussion:
The move-related changes inadvertently overwrote the intent of 276. Issue 276 removed the requirement of CopyAssignable from most of the member functions of node-based containers. But the move-related changes unnecessarily introduced the MoveAssignable requirement for those members which used to require CopyAssignable.
We also discussed (c++std-lib-18722) the possibility of dropping MoveAssignable from some of the sequence requirements. Additionally the in-place construction work may further reduce requirements. For purposes of an easy reference, here are the minimum sequence requirements as I currently understand them. Those items in requirements table in the working draft which do not appear below have been purposefully omitted for brevity as they do not have any requirements of this nature. Some items which do not have any requirements of this nature are included below just to confirm that they were not omitted by mistake.
| X u(a) | value_type must be CopyConstructible |
| X u(rv) | array requires value_type to be CopyConstructible |
| a = u | Sequences require value_type to be CopyConstructible and CopyAssignable. Associative containers require value_type to be CopyConstructible. |
| a = rv | array requires value_type to be CopyAssignable. Sequences containers with propagate_on_container_move_assignment == false allocators require value_type to be MoveConstructible and MoveAssignable. Associative containers with propagate_on_container_move_assignment == false allocators require value_type to be MoveConstructible. |
| swap(a,u) | array requires value_type to be Swappable. |
| X(n) | value_type must be DefaultConstructible |
| X(n, t) | value_type must be CopyConstructible |
| X(i, j) | Sequences require value_type to be constructible from *i. Additionally if input_iterators are used, vector and deque require MoveContructible and MoveAssignable. |
| a.insert(p, t) | The value_type must be CopyConstructible. The sequences vector and deque also require the value_type to be CopyAssignable. |
| a.insert(p, rv) | The value_type must be MoveConstructible. The sequences vector and deque also require the value_type to be MoveAssignable. |
| a.insert(p, n, t) | The value_type must be CopyConstructible. The sequences vector and deque also require the value_type to be CopyAssignable. |
| a.insert(p, i, j) | If the iterators return an lvalue the value_type must be CopyConstructible. The sequences vector and deque also require the value_type to be CopyAssignable when the iterators return an lvalue. If the iterators return an rvalue the value_type must be MoveConstructible. The sequences vector and deque also require the value_type to be MoveAssignable when the iterators return an rvalue. |
| a.erase(p) | The sequences vector and deque require the value_type to be MoveAssignable. |
| a.erase(q1, q2) | The sequences vector and deque require the value_type to be MoveAssignable. |
| a.clear() | |
| a.assign(i, j) | If the iterators return an lvalue the value_type must be CopyConstructible and CopyAssignable. If the iterators return an rvalue the value_type must be MoveConstructible and MoveAssignable. |
| a.assign(n, t) | The value_type must be CopyConstructible and CopyAssignable. |
| a.resize(n) | The value_type must be DefaultConstructible. The sequence vector also requires the value_type to be MoveConstructible. |
| a.resize(n, t) | The value_type must be CopyConstructible. |
| a.front() | |
| a.back() | |
| a.push_front(t) | The value_type must be CopyConstructible. |
| a.push_front(rv) | The value_type must be MoveConstructible. |
| a.push_back(t) | The value_type must be CopyConstructible. |
| a.push_back(rv) | The value_type must be MoveConstructible. |
| a.pop_front() | |
| a.pop_back() | |
| a[n] | |
| a.at[n] |
| X(i, j) | If the iterators return an lvalue the value_type must be CopyConstructible. If the iterators return an rvalue the value_type must be MoveConstructible. |
| a_uniq.insert(t) | The value_type must be CopyConstructible. |
| a_uniq.insert(rv) | The key_type and the mapped_type (if it exists) must be MoveConstructible. |
| a_eq.insert(t) | The value_type must be CopyConstructible. |
| a_eq.insert(rv) | The key_type and the mapped_type (if it exists) must be MoveConstructible. |
| a.insert(p, t) | The value_type must be CopyConstructible. |
| a.insert(p, rv) | The key_type and the mapped_type (if it exists) must be MoveConstructible. |
| a.insert(i, j) | If the iterators return an lvalue the value_type must be CopyConstructible. If the iterators return an rvalue the key_type and the mapped_type (if it exists) must be MoveConstructible.. |
| X(i, j, n, hf, eq) | If the iterators return an lvalue the value_type must be CopyConstructible. If the iterators return an rvalue the value_type must be MoveConstructible. |
| a_uniq.insert(t) | The value_type must be CopyConstructible. |
| a_uniq.insert(rv) | The key_type and the mapped_type (if it exists) must be MoveConstructible. |
| a_eq.insert(t) | The value_type must be CopyConstructible. |
| a_eq.insert(rv) | The key_type and the mapped_type (if it exists) must be MoveConstructible. |
| a.insert(p, t) | The value_type must be CopyConstructible. |
| a.insert(p, rv) | The key_type and the mapped_type (if it exists) must be MoveConstructible. |
| a.insert(i, j) | If the iterators return an lvalue the value_type must be CopyConstructible. If the iterators return an rvalue the key_type and the mapped_type (if it exists) must be MoveConstructible.. |
| map[lvalue-key] | The key_type must be CopyConstructible. The mapped_type must be DefaultConstructible and MoveConstructible. |
| map[rvalue-key] | The key_type must be MoveConstructible. The mapped_type must be DefaultConstructible and MoveConstructible. |
[ Kona (2007): Howard and Alan to update requirements table in issue with emplace signatures. ]
[ Bellevue: This should be handled as part of the concepts work. ]
[ 2009-07-20 Reopened by Howard: ]
This is one of the issues that was "solved by concepts" and is now no longer solved.
In a nutshell, concepts adopted the "minimum requirements" philosophy outlined in the discussion of this issue, and enforced it. My strong suggestion is that we translate the concepts specification into documentation for the containers.
What this means for vendors is that they will have to implement container members being careful to only use those characteristics of a type that the concepts specification formally allowed. Note that I am not talking about enable_if'ing everything. I am simply suggesting that (for example) we tell the vendor he can't call T's copy constructor or move constructor within the emplace member function, etc.
What this means for customers is that they will be able to use types within C++03 containers which are sometimes not CopyConstructible, and sometimes not even MoveConstructible, etc.
[ 2009-10 Santa Cruz: ]
Leave open. Howard to provide wording.
[ 2010-02-06 Howard provides wording. ]
[ 2010-02-08 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]
[ 2010-02-10 Howard opened. I neglected to reduce the requirements on value_type for the insert function of the ordered and unordered associative containers when the argument is an rvalue. Fixed it. ]
[ 2010-02-11 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]
Rationale:
[ post San Francisco: ]
Solved by N2776.
This rationale is obsolete.
Proposed resolution:
Change 23.2.1 [container.requirements.general]/4:
4 In Tables 91 and 92, X denotes a container class containing objects of type T, a and b denote values of type X, u denotes an identifier, r denotesan lvalue or a const rvaluea non-const value of type X, and rv denotes a non-const rvalue of type X.
Change the following rows in Table 91 — Container requirements 23.2.1 [container.requirements.general]:
Table 91 — Container requirements Expression Return type Assertion/note
pre-/post-conditionComplexity X::value_type T Requires: T is Destructible. compile time
Change 23.2.1 [container.requirements.general]/10:
Unless otherwise specified (see 23.2.4.1, 23.2.5.1, 23.3.2.3, and 23.3.6.4) all container types defined in this Clause meet the following additional requirements:
- ..
- no erase(), clear(), pop_back() or pop_front() function throws an exception.
- ...
Insert a new paragraph prior to 23.2.1 [container.requirements.general]/14:
The descriptions of the requirements of the type T in this section use the terms CopyConstructible, MoveConstructible, constructible from *i, and constructible from args. These terms are equivalent to the following expression using the appropriate arguments:
allocator_traits<allocator_type>::construct(x.get_allocator(), q, args...);where x is a non-const lvalue of some container type X and q has type X::value_type*.
[Example: The container is going to move construct a T, so will call:
allocator_traits<allocator_type>::construct(get_allocator(), q, std::move(t));The default implementation of construct will call:
::new (q) T(std::forward<T>(t)); // where forward is the same as move here, cast to rvalueBut the allocator author may override the above definition of construct and do the construction of T by some other means. — end example]
14 ...
Add to 23.2.1 [container.requirements.general]/14:
14 In Table 93, X denotes an allocator-aware container class with a value_type of T using allocator of type A, u denotes a variable, a and b denote non-const lvalues of type X, t denotes an lvalue or a const rvalue of type X, rv denotes a non-const rvalue of type X, m is a value of type A, and Q is an allocator type.
Change or add the following rows in Table 93 — Allocator-aware container requirements in 23.2.1 [container.requirements.general]:
Table 93 — Allocator-aware container requirements Expression Return type Assertion/note
pre-/post-conditionComplexity X(t, m)
X u(t, m);Requires: T is CopyConstructible.
post: u == t,
get_allocator() == mlinear X(rv, m)
X u(rv, m);Requires: T is MoveConstructible.
post: u shall have the same elements, or copies of the elements, that rv had before this construction,
get_allocator() == mconstant if m == rv.get_allocator(), otherwise linear a = t X& Requires: T is CopyConstructible and CopyAssignable
post: a == t.linear a = rv X& Requires: If allocator_traits< allocator_type > ::propagate_on_container_move_assignment ::value is false, T is MoveConstructible and MoveAssignable.
All existing elements of a are either move assigned to or destroyed.
a shall be equal to the value that rv had before this assignmentlinear a.swap(b); void exchanges the contents of a and b constant
Change the following rows in Table 94 — Sequence container requirements (in addition to container) in 23.2.3 [sequence.reqmts]:
Table 94 — Sequence container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-conditionX(i, j)
X a(i, j)Requires: If the iterator's dereference operation returns an lvalue or a const rvalue, T shall be CopyConstructible.T shall be constructible from *i.
If the iterator does not meet the forward iterator requirements (24.2.3 [forward.iterators]), then vector also requires T to be MoveConstructible.
Each iterator in the range [i,j) shall be dereferenced exactly once.
post: size() == distance between i and j
Constructs a sequence container equal to the range [i, j)a = il; X& Requires: T is CopyConstructible and CopyAssignable.
a = X(il);
Assigns the range [il.begin(), il.end()) into a. All existing elements of a are either assigned or destroyed.
rReturns *this;a.emplace(p, args); iterator Requires: ConstructibleAsElement<A, T, Args>.T is constructible from args. vector and deque also require T to be MoveConstructible and MoveAssignable. Inserts an object of type T constructed with std::forward<Args>(args)... before p.a.insert(p, t); iterator Requires: ConstructibleAsElement<A, T, Args> and T shall be CopyAssignable.T shall be CopyConstructible. vector and deque also require T to be CopyAssignable. Inserts a copy t before p.a.insert(p, rv); iterator Requires: ConstructibleAsElement<A, T, T&&> and T shall be MoveAssignable.T shall be MoveConstructible. vector and deque also require T to be MoveAssignable. Inserts a copy rv before p.a.insert(p, i, j) iterator Requires: If the iterator's dereference operation returns an lvalue or a const rvalue, T shall be CopyConstructible.T shall be constructible from *i.
If the iterator does not meet the forward iterator requirements (24.2.3 [forward.iterators]), then vector also requires T to be MoveConstructible and MoveAssignable.
Each iterator in the range [i,j) shall be dereferenced exactly once.
pre: i and j are not iterators into a.
Inserts copies of elements in [i, j) before pa.erase(q); iterator Requires: T and T shall be MoveAssignable.vector and deque require T to be MoveAssignable. Erases the element pointed to by q.a.erase(q1, q2); iterator Requires: T and T shall be MoveAssignable.vector and deque require T to be MoveAssignable. Erases the elements in the range [q1, q2).a.clear(); void erase(begin(), end())
Destroys all elements in a. Invalidates all references, pointers, and iterators referring to the elements of a and may invalidate the past-the-end iterator.
post:size() == 0a.empty() == truea.assign(i, j) void Requires: If the iterator's dereference operation returns an lvalue or a const rvalue, T shall be CopyConstructible and CopyAssignable.T shall be constructible and assignable from *i. If the iterator does not meet the forward iterator requirements (24.2.3 [forward.iterators]), then vector also requires T to be MoveConstructible.
Each iterator in the range [i,j) shall be dereferenced exactly once.
pre: i, j are not iterators into a.
Replaces elements in a with a copy of [i, j).
Change the following rows in Table 95 — Optional sequence container operations in 23.2.3 [sequence.reqmts]:
Table 95 — Optional sequence container operations Expression Return type Operational semantics Container a.emplace_front(args) void a.emplace(a.begin(), std::forward<Args>(args)...)
Prepends an object of type T constructed with std::forward<Args>(args)....
Requires:ConstructibleAsElement<A, T, Args>T shall be constructible from args.list, deque, forward_list a.emplace_back(args) void a.emplace(a.end(), std::forward<Args>(args)...)
Appends an object of type T constructed with std::forward<Args>(args)....
Requires:ConstructibleAsElement<A, T, Args>T shall be constructible from args. vector also requires T to be MoveConstructible.list, deque, vector a.push_front(t) void a.insert(a.begin(), t)
Prepends a copy of t.
Requires:ConstructibleAsElement<A, T, T> and T shall be CopyAssignable.T shall be CopyConstructible.list, deque, forward_list a.push_front(rv) void a.insert(a.begin(), t)
Prepends a copy of rv.
Requires:ConstructibleAsElement<A, T, T&&> and T shall be MoveAssignable.T shall be MoveConstructible.list, deque, forward_list a.push_back(t) void a.insert(a.end(), t)
Appends a copy of t.
Requires:ConstructibleAsElement<A, T, T> and T shall be CopyAssignable.T shall be CopyConstructible.vector, list, deque, basic_string a.push_back(rv) void a.insert(a.end(), t)
Appends a copy of rv.
Requires:ConstructibleAsElement<A, T, T&&> and T shall be MoveAssignable.T shall be MoveConstructible.vector, list, deque, basic_string a.pop_front() void a.erase(a.begin())
Destroys the first element.
Requires: a.empty() shall be false.list, deque, forward_list a.pop_back() void { iterator tmp = a.end();
--tmp;
a.erase(tmp); }
Destroys the last element.
Requires: a.empty() shall be false.vector, list, deque, basic_string
Insert a new paragraph prior to 23.2.4 [associative.reqmts]/7, and edit paragraph 7:
The associative containers meet all of the requirements of Allocator-aware containers (23.2.1 [container.requirements.general]), except for the containers map and multimap, the requirements placed on value_type in Table 93 apply instead directly to key_type and mapped_type. [Note: For example key_type and mapped_type are sometimes required to be CopyAssignable even though the value_type (pair<const key_type, mapped_type>) is not CopyAssignable. — end note]
7 In Table 96, X denotes an associative container class, a denotes a value of X, a_uniq denotes a value of X when X supports unique keys, a_eq denotes a value of X when X supports multiple keys, u denotes an identifier,
r denotes an lvalue or a const rvalue of type X, rv denotes a non-const rvalue of type X,i and j satisfy input iterator requirements and refer to elements implicitly convertible to value_type, [i,j) denotes a valid range, p denotes a valid const iterator to a, q denotes a valid dereferenceable const iterator to a, [q1, q2) denotes a valid range of const iterators in a, il designates an object of type initializer_list<value_type>, t denotes a value of X::value_type, k denotes a value of X::key_type and c denotes a value of type X::key_compare. A denotes the storage allocator used by X, if any, or std::allocator<X::value_type> otherwise, and m denotes an allocator of a type convertible to A.
Change or add the following rows in Table 96 — Associative container requirements (in addition to container) in 23.2.4 [associative.reqmts]:
Table 96 — Associative container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-conditionComplexity X::key_type Key Requires: Key is CopyConstructible and CopyAssignableDestructiblecompile time X::mapped_type (map and multimap only) T Requires: T is Destructible compile time X(c)
X a(c);Requires: ConstructibleAsElement<A, key_compare, key_compare>.
key_compare is CopyConstructible.
Constructs an empty container.
Uses a copy of c as a comparison object.constant X()
X a;Requires: ConstructibleAsElement<A, key_compare, key_compare>.
key_compare is DefaultConstructible.
Constructs an empty container.
Uses Compare() as a comparison object.constant X(i, j, c)
X a(i, j, c);Requires: ConstructibleAsElement<A, key_compare, key_compare>.
key_compare is CopyConstructible. value_type shall be constructible from *i.
Constructs an empty container ans inserts elements from the range [i, j) into it; uses c as a comparison object.N log N in general (N is the distance from i to j); linear if [i, j) is sorted with value_comp() X(i, j)
X a(i, j);Requires: ConstructibleAsElement<A, key_compare, key_compare>.
value_type shall be constructible from *i. key_compare is DefaultConstructible.
Same as above, but uses Compare() as a comparison object.same as above a = il X& a = X(il);
return *this;
Requires: T is CopyConstructible and CopyAssignable.
Assigns the range [il.begin(), il.end()) into a. All existing elements of a are either assigned or destroyed.Same as a = X(il).N log N in general (N is il.size() added to the existing size of a); linear if [il.begin(), il.end()) is sorted with value_comp()a_uniq.emplace(args) pair<iterator, bool> Requires: T shall be constructible from args
inserts a T 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.logarithmic a_eq.emplace(args) iterator Requires: T shall be constructible from args
inserts a T object t constructed with std::forward<Args>(args)... and returns the iterator pointing to the newly inserted element.logarithmic a_uniq.insert(t) pair<iterator, bool> Requires: T shall be MoveConstructible if t is a non-const rvalue expression, else T shall be CopyConstructible.
inserts t 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.logarithmic a_eq.insert(t) iterator Requires: T shall be MoveConstructible if t is a non-const rvalue expression, else T shall be CopyConstructible.
inserts t and returns the iterator pointing to the newly inserted element. If a range containing elements equivalent to t exists in a_eq, t is inserted at the end of that range.logarithmic a.insert(p, t) iterator Requires: T shall be MoveConstructible if t is a non-const rvalue expression, else T shall be CopyConstructible.
inserts t if and only if there is no element with key equivalent to the key of t in containers with unique keys; always inserts t in containers with equivalent keys; always returns the iterator pointing to the element with key equivalent to the key of t. t is inserted as close as possible to the position just prior to p.logarithmic in general, but amortized constant if t is inserted right before p. a.insert(i, j) void Requires: T shall be constructible from *i.
pre: i, j are not iterators into a. 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 in containers with unique keys; always inserts that element in containers with equivalent keys.N log(size() + N ) (N is the distance from i to j)
Insert a new paragraph prior to 23.2.5 [unord.req]/9:
The unordered associative containers meet all of the requirements of Allocator-aware containers (23.2.1 [container.requirements.general]), except for the containers unordered_map and unordered_multimap, the requirements placed on value_type in Table 93 apply instead directly to key_type and mapped_type. [Note: For example key_type and mapped_type are sometimes required to be CopyAssignable even though the value_type (pair<const key_type, mapped_type>) is not CopyAssignable. — end note]
9 ...
Change or add the following rows in Table 98 — Unordered associative container requirements (in addition to container) in 23.2.5 [unord.req]:
Table 98 — Unordered associative container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-conditionComplexity X::key_type Key Requires: Key shall be CopyAssignable and CopyConstructibleDestructiblecompile time X::mapped_type (unordered_map and unordered_multimap only) T Requires:T is Destructible compile time X(n, hf, eq)
X a(n, hf, eq)X Requires: hasher and key_equal are CopyConstructible. Constructs an empty container with at least n buckets, using hf as the hash function and eq as the key equality predicate. O(N) X(n, hf)
X a(n, hf)X Requires: hasher is CopyConstructible and key_equal is DefaultConstructible. Constructs an empty container with at least n buckets, using hf as the hash function and key_equal() as the key equality predicate. O(N) X(n)
X a(n)X Requires: hasher and key_equal are DefaultConstructible. Constructs an empty container with at least n buckets, using hasher() as the hash function and key_equal() as the key equality predicate. O(N) X()
X aX Requires: hasher and key_equal are DefaultConstructible. Constructs an empty container an unspecified number of buckets, using hasher() as the hash function and key_equal() as the key equality predicate. constant X(i, j, n, hf, eq)
X a(i, j, n, hf, eq)X Requires: value_type is constructible from *i. hasher and key_equal are CopyConstructible.
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 O(N) (N is distance(i, j)), worst case O(N2) X(i, j, n, hf)
X a(i, j, n, hf)X Requires: value_type is constructible from *i. hasher is CopyConstructible and key_equal is DefaultConstructible.
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 O(N) (N is distance(i, j)), worst case O(N2) X(i, j, n)
X a(i, j, n)X Requires: value_type is constructible from *i. hasher and key_equal are DefaultConstructible.
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 O(N) (N is distance(i, j)), worst case O(N2) X(i, j)
X a(i, j)X Requires: value_type is constructible from *i. hasher and key_equal are DefaultConstructible.
Constructs an empty container with an unspecified number of 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 O(N) (N is distance(i, j)), worst case O(N2) X(b)
X a(b)X Copy constructor. In addition to the contained elementsrequirements of Table 93 (23.2.1 [container.requirements.general]), copies the hash function, predicate, and maximum load factor.Average case linear in b.size(), worst case quadratic. a = b X& Copy assignment operator. In addition to the contained elementsrequirements of Table 93 (23.2.1 [container.requirements.general]), copies the hash function, predicate, and maximum load factor.Average case linear in b.size(), worst case quadratic. a = il X& a = X(il); return *this;
Requires: T is CopyConstructible and CopyAssignable.
Assigns the range [il.begin(), il.end()) into a. All existing elements of a are either assigned or destroyed.Average case linear in il.size(), worst case quadratic. a_uniq.emplace(args) pair<iterator, bool> Requires: T shall be constructible from args
inserts a T 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.Average case O(1), worst case O(a_uniq.size()). a_eq.emplace(args) iterator Requires: T shall be constructible from args
inserts a T object t constructed with std::forward<Args>(args)... and returns the iterator pointing to the newly inserted element.Average case O(1), worst case O(a_eq.size()). a.emplace_hint(p, args) iterator Requires: T shall be constructible from args
equivalent to a.emplace( std::forward<Args>(args)...). Return value is an iterator pointing to the element with the key equivalent to the newly inserted element. The const_iterator p is a hint pointing to where the search should start. Implementations are permitted to ignore the hint.Average case O(1), worst case O(a.size()). a_uniq.insert(t) pair<iterator, bool> Requires: T shall be MoveConstructible if t is a non-const rvalue expression, else T shall be CopyConstructible.
Inserts t 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 indicates whether the insertion takes place, and the iterator component points to the element with key equivalent to the key of t.Average case O(1), worst case O(a_uniq.size()). a_eq.insert(t) iterator Requires: T shall be MoveConstructible if t is a non-const rvalue expression, else T shall be CopyConstructible.
Inserts t, and returns an iterator pointing to the newly inserted element.Average case O(1), worst case O(a_uniq.size()). a.insert(q, t) iterator Requires: T shall be MoveConstructible if t is a non-const rvalue expression, else T shall be CopyConstructible.
Equivalent to a.insert(t). Return value is an iterator pointing to the element with the key equivalent to that of t. The iterator q is a hint pointing to where the search should start. Implementations are permitted to ignore the hint.Average case O(1), worst case O(a_uniq.size()). a.insert(i, j) void Requires: T shall be constructible from *i.
Pre: i and j are not iterators in a. Equivalent to a.insert(t) for each element in [i,j).Average case O(N), where N is distance(i, j). Worst case O(N * a.size()).
Remove the entire section 23.3.2.1 [deque.cons].
[ This section is already specified by the requirements tables. ]
Change 23.3.3 [forwardlist]/2:
2 A forward_list satisfies all of the requirements of a container (table 91), except that the size() member function is not provided. A forward_list also satisfies all of the requirements of an allocator-aware container (table 93). And forward_list provides the assign member functions as specified in Table 94, Sequence container requirements, and several of the optional sequence container requirements (Table 95). Descriptions are provided here only for operations on forward_list that are not described in that table or for operations where there is additional semantic information.
Remove the entire section 23.3.3.1 [forwardlist.cons].
[ This section is already specified by the requirements tables, and is actually incorrect for the requirements on value_type under assign (lacks CopyAssignable). ]
Remove from 23.3.3.4 [forwardlist.modifiers] the redundant definitions for:
template <class... Args> void emplace_front(Args&&... args); void push_front(const T& x); void push_front(T&& x); void pop_front();
Add a new paragraph after 23.3.3.4 [forwardlist.modifiers]/23:
void clear();23 Effects: Erases all elements in the range [begin(),end()).
Remarks: Does not invalidate past-the-end iterators.
Remove the entire section 23.3.4.1 [list.cons].
[ This section is already specified by the requirements tables, and is actually incorrect for the requirements on value_type under assign (lacks CopyAssignable). ]
Remove the redundant section 23.3.6.1 [vector.cons].
Change 23.3.6.2 [vector.capacity]/13:
void resize(size_type sz, const T& c);13 Requires: T shall be CopyConstructible. If value_type has a move constructor, that constructor shall not throw any exceptions.
In 23.5.3 [unord.set] and 23.5.4 [unord.multiset] substitute "Key" for "Value".
[ The above substitution is normative as it ties into the requirements table. ]
Section: 20.2.1 [utility.arg.requirements] Status: Tentatively Ready Submitter: Pablo Halpern Opened: 2007-09-12 Last modified: 2010-02-04
View other active issues in [utility.arg.requirements].
View all other issues in [utility.arg.requirements].
View all issues with Tentatively Ready status.
Discussion:
The DefaultConstructible requirement is referenced in several places in the August 2007 working draft N2369, but is not defined anywhere.
[ Bellevue: ]
Walking into the default/value-initialization mess...
Why two lines? Because we need both expressions to be valid.
AJM not sure what the phrase "default constructed" means. This is unfortunate, as the phrase is already used 24 times in the library!
Example: const int would not accept first line, but will accept the second.
This is an issue that must be solved by concepts, but we might need to solve it independantly first.
It seems that the requirements are the syntax in the proposed first column is valid, but not clear what semantics we need.
A table where there is no post-condition seems odd, but appears to sum up our position best.
At a minimum an object is declared and is destuctible.
Move to open, as no-one happy to produce wording on the fly.
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-08-17 Daniel adds "[defaultconstructible]" to table title. 408 depends upon this issue. ]
[ 2009-08-18 Alisdair adds: ]
Looking at the proposed table in this issue, it really needs two rows:
Table 33: DefaultConstructible requirements [defaultconstructible] expression post-condition T t; t is default-initialized. T{} Object of type T is value-initialized. Note I am using the new brace-initialization syntax that is unambiguous in all use cases (no most vexing parse.)
[ 2009-10-03 Daniel adds: ]
The suggested definition T{} describing it as value-initialization is wrong, because it belongs to list-initialization which would - as the current rules are - always prefer a initializer-list constructor over a default-constructor. I don't consider this as an appropriate definition of DefaultConstructible. My primary suggestion is to ask core, whether the special case T{} (which also easily leads to ambiguity situations for more than one initializer-list in a class) would always prefer a default-constructor - if any - before considering an initializer-list constructor or to provide another syntax form to prefer value-initialization over list-initialization. If that fails I would fall back to suggest to use the expression T() instead of T{} with all it's disadvantages for the meaning of the expression
T t();
[ 2009-10 Santa Cruz: ]
Leave Open. Core is looking to make Alisdair's proposed resolution correct.
[ 2010-01-24 At Alisdiar's request, moved his proposal into the proposed wording seciton. The old wording is preserved here: ]
In section 20.2.1 [utility.arg.requirements], before table 33, add the following table:
Table 33: DefaultConstructible requirements [defaultconstructible]
expression
post-condition
T t;
T()T is default constructed.
[ 2010-02-04: Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]
Rationale:
[ San Francisco: ]
We believe concepts will solve this problem (N2774).
[ Rationale is obsolete. ]
Proposed resolution:
In section 20.2.1 [utility.arg.requirements], before table 33, add the following table:
Table 33: DefaultConstructible requirements [defaultconstructible] expression post-condition T t; Object t is default-initialized. T u{}; Object u is value-initialized. T()
T{}A temporary object of type T is value-initialized.
Section: 28.11.4 [re.alg.replace] Status: Tentatively NAD Submitter: Stephan T. Lavavej Opened: 2007-09-22 Last modified: 2010-01-27
View other active issues in [re.alg.replace].
View all other issues in [re.alg.replace].
View all issues with Tentatively NAD status.
Discussion:
Two overloads of regex_replace() are currently provided:
template <class OutputIterator, class BidirectionalIterator,
class traits, class charT>
OutputIterator
regex_replace(OutputIterator out,
BidirectionalIterator first, BidirectionalIterator last,
const basic_regex<charT, traits>& e,
const basic_string<charT>& fmt,
regex_constants::match_flag_type flags =
regex_constants::match_default);
template <class traits, class charT>
basic_string<charT>
regex_replace(const basic_string<charT>& s,
const basic_regex<charT, traits>& e,
const basic_string<charT>& fmt,
regex_constants::match_flag_type flags =
regex_constants::match_default);
The absence of const charT * overloads prevents ordinary-looking code from compiling, such as:
const string s("kitten");
const regex r("en");
cout << regex_replace(s, r, "y") << endl;
The compiler error message will be something like "could not deduce template argument for 'const std::basic_string<_Elem> &' from 'const char[1]'".
Users expect that anything taking a basic_string<charT> can also take a const charT *. In their own code, when they write a function taking std::string (or std::wstring), they can pass a const char * (or const wchar_t *), thanks to basic_string's implicit constructor. Because the regex algorithms are templated on charT, they can't rely on basic_string's implicit constructor (as the compiler error message indicates, template argument deduction fails first).
If a user figures out what the compiler error message means, workarounds are available - but they are all verbose. Explicit template arguments could be given to regex_replace(), allowing basic_string's implicit constructor to be invoked - but charT is the last template argument, not the first, so this would be extremely verbose. Therefore, constructing a basic_string from each C string is the simplest workaround.
[ Sophia Antipolis: ]
We note that Boost already has these overloads. However, the proposed wording is provided only for 28.11.4 [re.alg.replace]; wording is needed for the synopsis as well. We also note that this has impact on match_results::format, which may require further overloads.
[ 2009-07 Frankfurt: ]
Daniel to tweak for us.
[ 2009-07-25 Daniel tweaks both this issue and 727. ]
This is solved by the proposed resolution of 727.
[ 2009-10 Santa Cruz: ]
Leave Open. Though we believe this is solved by the proposed resolution to 727.
[ 2010-01-27 Moved to Tentatively NAD after 5 positive votes on c++std-lib. Rationale added below. ]
Rationale:
Solved by 727.
Proposed resolution:
Provide additional overloads for regex_replace(): one additional overload of the iterator-based form (taking const charT* fmt), and three additional overloads of the convenience form (one taking const charT* str, another taking const charT* fmt, and the third taking both const charT* str and const charT* fmt). 28.11.4 [re.alg.replace]:
template <class OutputIterator, class BidirectionalIterator, class traits, class charT> OutputIterator regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const basic_regex<charT, traits>& e, const basic_string<charT>& fmt, regex_constants::match_flag_type flags = regex_constants::match_default); template <class OutputIterator, class BidirectionalIterator, class traits, class charT> OutputIterator regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const basic_regex<charT, traits>& e, const charT* fmt, regex_constants::match_flag_type flags = regex_constants::match_default);...
template <class traits, class charT> basic_string<charT> regex_replace(const basic_string<charT>& s, const basic_regex<charT, traits>& e, const basic_string<charT>& fmt, regex_constants::match_flag_type flags = regex_constants::match_default); template <class traits, class charT> basic_string<charT> regex_replace(const basic_string<charT>& s, const basic_regex<charT, traits>& e, const charT* fmt, regex_constants::match_flag_type flags = regex_constants::match_default); template <class traits, class charT> basic_string<charT> regex_replace(const charT* s, const basic_regex<charT, traits>& e, const basic_string<charT>& fmt, regex_constants::match_flag_type flags = regex_constants::match_default); template <class traits, class charT> basic_string<charT> regex_replace(const charT* s, const basic_regex<charT, traits>& e, const charT* fmt, regex_constants::match_flag_type flags = regex_constants::match_default);
Section: 28.11.4 [re.alg.replace] Status: Tentatively Ready Submitter: Stephan T. Lavavej Opened: 2007-09-22 Last modified: 2010-01-27
View other active issues in [re.alg.replace].
View all other issues in [re.alg.replace].
View all issues with Tentatively Ready status.
Discussion:
regex_match() and regex_search() take const basic_string<charT, ST, SA>&. regex_replace() takes const basic_string<charT>&. This prevents regex_replace() from accepting basic_strings with custom traits and allocators.
Overloads of regex_replace() taking basic_string should be additionally templated on class ST, class SA and take const basic_string<charT, ST, SA>&. Consistency with regex_match() and regex_search() would place class ST, class SA as the first template arguments; compatibility with existing code using TR1 and giving explicit template arguments to regex_replace() would place class ST, class SA as the last template arguments.
[ Batavia (2009-05): ]
Bill comments, "We need to look at the depth of this change."
Pete remarks that we are here dealing with a convenience function that saves a user from calling the iterato-based overload.
Move to Open.
[ 2009-07 Frankfurt: ]
Howard to ask Stephan Lavavej to provide wording.
[ 2009-07-17 Stephan provided wording. ]
[ 2009-07-25 Daniel tweaks both this issue and 726. ]
One relevant part of the proposed resolution below suggests to add a new overload of the format member function in the match_results class template that accepts two character pointers defining the begin and end of a format range. A more general approach could have proposed a pair of iterators instead, but the used pair of char pointers reflects existing practice. If the committee strongly favors an iterator-based signature, this could be simply changed. I think that the minimum requirement should be a BidirectionalIterator, but current implementations take advantage (at least partially) of the RandomAccessIterator sub interface of the char pointers.
Suggested Resolution:
[Moved into the proposed resloution]
[ 2009-07-30 Stephan agrees with Daniel's wording. Howard places Daniel's wording in the Proposed Resolution. ]
[ 2009-10 Santa Cruz: ]
Move to Review. Chair is anxious to move this to Ready in Pittsburgh.
[ 2010-01-27 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]
Proposed resolution:
Change 28.4 [re.syn] as indicated:
// 28.11.4, function template regex_replace:
template <class OutputIterator, class BidirectionalIterator,
class traits, class charT, class ST, class SA>
OutputIterator
regex_replace(OutputIterator out,
BidirectionalIterator first, BidirectionalIterator last,
const basic_regex<charT, traits>& e,
const basic_string<charT, ST, SA>& fmt,
regex_constants::match_flag_type flags =
regex_constants::match_default);
template <class OutputIterator, class BidirectionalIterator,
class traits, class charT>
OutputIterator
regex_replace(OutputIterator out,
BidirectionalIterator first, BidirectionalIterator last,
const basic_regex<charT, traits>& e,
const charT* fmt,
regex_constants::match_flag_type flags =
regex_constants::match_default);
template <class traits, class charT, class ST, class SA,
class FST, class FSA>
basic_string<charT, ST, SA>
regex_replace(const basic_string<charT, ST, SA>& s,
const basic_regex<charT, traits>& e,
const basic_string<charT, FST, FSA>& fmt,
regex_constants::match_flag_type flags =
regex_constants::match_default);
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);
template <class traits, class charT, class ST, class SA>
basic_string<charT>
regex_replace(const charT* s,
const basic_regex<charT, traits>& e,
const basic_string<charT, ST, SA>& fmt,
regex_constants::match_flag_type flags =
regex_constants::match_default);
template <class traits, class charT>
basic_string<charT>
regex_replace(const charT* s,
const basic_regex<charT, traits>& e,
const charT* fmt,
regex_constants::match_flag_type flags =
regex_constants::match_default);
Change 28.10 [re.results]/3, class template match_results as indicated:
template <class OutputIter>
OutputIter
format(OutputIter out,
const char_type* fmt_first, const char_type* fmt_last,
regex_constants::match_flag_type flags =
regex_constants::format_default) const;
template <class OutputIter, class ST, class SA>
OutputIter
format(OutputIter out,
const string_typebasic_string<char_type, ST, SA>& fmt,
regex_constants::match_flag_type flags =
regex_constants::format_default) const;
template <class ST, class SA>
string_typebasic_string<char_type, ST, SA>
format(const string_typebasic_string<char_type, ST, SA>& fmt,
regex_constants::match_flag_type flags =
regex_constants::format_default) const;
string_type
format(const char_type* fmt,
regex_constants::match_flag_type flags =
regex_constants::format_default) const;
Insert at the very beginning of 28.10.4 [re.results.form] the following:
template <class OutputIter> OutputIter format(OutputIter out, const char_type* fmt_first, const char_type* fmt_last, regex_constants::match_flag_type flags = regex_constants::format_default) const;1 Requires: The type OutputIter shall satisfy the requirements for an Output Iterator (24.2.2 [output.iterators]).
2 Effects: Copies the character sequence [fmt_first,fmt_last) to OutputIter out. Replaces each format specifier or escape sequence in the copied range with either the character(s) it represents or the sequence of characters within *this to which it refers. The bitmasks specified in flags determine which format specifiers and escape sequences are recognized.
3 Returns: out.
Change 28.10.4 [re.results.form], before p. 1 until p. 3 as indicated:
template <class OutputIter, class ST, class SA> OutputIter format(OutputIter out, conststring_typebasic_string<char_type, ST, SA>& fmt, regex_constants::match_flag_type flags = regex_constants::format_default) const;
1 Requires: The type OutputIter shall satisfy the requirements for an Output Iterator (24.2.3).2 Effects:
Copies the character sequence [fmt.begin(),fmt.end()) to OutputIter out. Replaces each format specifier or escape sequence in fmt with either the character(s) it represents or the sequence of characters within *this to which it refers. The bitmasks specified in flags determines what format specifiers and escape sequences are recognizedEquivalent to return format(out, fmt.data(), fmt.data() + fmt.size(), flags).
3 Returns: out.
Change 28.10.4 [re.results.form], before p. 4 until p. 4 as indicated:
template <class ST, class SA>string_typebasic_string<char_type, ST, SA> format(conststring_typebasic_string<char_type, ST, SA>& fmt, regex_constants::match_flag_type flags = regex_constants::format_default) const;Effects:
Returns a copy of the string fmt. Replaces each format specifier or escape sequence in fmt with either the character(s) it represents or the sequence of characters within *this to which it refers. The bitmasks specified in flags determines what format specifiers and escape sequences are recognized.Constructs an empty string result of type basic_string<char_type, ST, SA>, and calls format(back_inserter(result), fmt, flags).Returns: result
At the end of 28.10.4 [re.results.form] insert as indicated:
string_type format(const char_type* fmt, regex_constants::match_flag_type flags = regex_constants::format_default) const;Effects: Constructs an empty string result of type string_type, and calls format(back_inserter(result), fmt, fmt + char_traits<char_type>::length(fmt), flags).
Returns: result
Change 28.11.4 [re.alg.replace] before p. 1 as indicated:
template <class OutputIterator, class BidirectionalIterator, class traits, class charT, class ST, class SA> OutputIterator regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const basic_regex<charT, traits>& e, const basic_string<charT, ST, SA>& fmt, regex_constants::match_flag_type flags = regex_constants::match_default); template <class OutputIterator, class BidirectionalIterator, class traits, class charT> OutputIterator regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last, const basic_regex<charT, traits>& e, const charT* fmt, regex_constants::match_flag_type flags = regex_constants::match_default);Effects: [..]. If any matches are found then, for each such match, if !(flags & regex_constants::format_no_copy) calls std::copy(m.prefix().first, m.prefix().second, out), and then calls m.format(out, fmt, flags) for the first form of the function and m.format(out, fmt, fmt + char_traits<charT>::length(fmt), flags) for the second form. [..].
Change 28.11.4 [re.alg.replace] before p. 3 as indicated:
template <class traits, class charT, class ST, class SA, class FST, class FSA> basic_string<charT, ST, SA> regex_replace(const basic_string<charT, ST, SA>& s, const basic_regex<charT, traits>& e, const basic_string<charT, FST, FSA>& fmt, regex_constants::match_flag_type flags = regex_constants::match_default); 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);Effects: Constructs an empty string result of type basic_string<charT, ST, SA>, calls regex_replace(back_inserter(result), s.begin(), s.end(), e, fmt, flags), and then returns result.
At the end of 28.11.4 [re.alg.replace] add the following new prototype description:
template <class traits, class charT, class ST, class SA> basic_string<charT> regex_replace(const charT* s, const basic_regex<charT, traits>& e, const basic_string<charT, ST, SA>& fmt, regex_constants::match_flag_type flags = regex_constants::match_default); template <class traits, class charT> basic_string<charT> regex_replace(const charT* s, const basic_regex<charT, traits>& e, const charT* fmt, regex_constants::match_flag_type flags = regex_constants::match_default);Effects: Constructs an empty string result of type basic_string<charT>, calls regex_replace(back_inserter(result), s, s + char_traits<charT>::length(s), e, fmt, flags), and then returns result.
Section: 20.2.1 [utility.arg.requirements] Status: Open Submitter: Howard Hinnant Opened: 2007-10-10 Last modified: 2009-11-08
View other active issues in [utility.arg.requirements].
View all other issues in [utility.arg.requirements].
View all issues with Open status.
Discussion:
This issue was split from 672. 672 now just deals with changing the requirements of T in the Swappable requirement from CopyConstructible and CopyAssignable to MoveConstructible and MoveAssignable.
This issue seeks to widen the Swappable requirement to support proxy iterators. Here is example code:
namespace Mine {
template <class T>
struct proxy {...};
template <class T>
struct proxied_iterator
{
typedef T value_type;
typedef proxy<T> reference;
reference operator*() const;
...
};
struct A
{
// heavy type, has an optimized swap, maybe isn't even copyable or movable, just swappable
void swap(A&);
...
};
void swap(A&, A&);
void swap(proxy<A>, A&);
void swap(A&, proxy<A>);
void swap(proxy<A>, proxy<A>);
} // Mine
...
Mine::proxied_iterator<Mine::A> i(...)
Mine::A a;
swap(*i1, a);
The key point to note in the above code is that in the call to swap, *i1 and a are different types (currently types can only be Swappable with the same type). A secondary point is that to support proxies, one must be able to pass rvalues to swap. But note that I am not stating that the general purpose std::swap should accept rvalues! Only that overloaded swaps, as in the example above, be allowed to take rvalues.
That is, no standard library code needs to change. We simply need to have a more flexible definition of Swappable.
[ Bellevue: ]
While we believe Concepts work will define a swappable concept, we should still resolve this issue if possible to give guidance to the Concepts work.
Would an ambiguous swap function in two namespaces found by ADL break this wording? Suggest that the phrase "valid expression" means such a pair of types would still not be swappable.
Motivation is proxy-iterators, but facility is considerably more general. Are we happy going so far?
We think this wording is probably correct and probably an improvement on what's there in the WP. On the other hand, what's already there in the WP is awfully complicated. Why do we need the two bullet points? They're too implementation-centric. They don't add anything to the semantics of what swap() means, which is there in the post-condition. What's wrong with saying that types are swappable if you can call swap() and it satisfies the semantics of swapping?
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-10 Santa Cruz: ]
Leave as Open. Dave to provide wording.
[ 2009-11-08 Howard adds: ]
Updated wording to sync with N3000. Also this issue is very closely related to 594.
Proposed resolution:
Change 20.2.1 [utility.arg.requirements]:
-1- The template definitions in the C++ Standard Library refer to various named requirements whose details are set out in tables 31-38. In these tables, T and V are
is atypes to be supplied by a C++ program instantiating a template; a, b, and c are values of type const T; s and t are modifiable lvalues of type T; u is a value of type (possibly const) T;andrv is a non-const rvalue of type T; w is a value of type T; and v is a value of type V.
Table 37: Swappable requirements [swappable] expression Return type Post-condition swap( sw,tv)void tw has the value originally held byuv, anduv has the value originally held bytwThe Swappable requirement is met by satisfying one or more of the following conditions:
- T is Swappable if T and V are the same type and T satisfies the MoveConstructible requirements (Table 33) and the MoveAssignable requirements (Table 35);
- T is Swappable with V if a namespace scope function named swap exists in the same namespace as the definition of T or V, such that the expression swap(
sw,tv) is valid and has the semantics described in this table.- T is Swappable if T is an array type whose element type is Swappable.
Rationale:
[ post San Francisco: ]
Solved by N2758.
Section: 23 [containers] Status: Open Submitter: Alisdair Meredith Opened: 2008-01-14 Last modified: 2009-10-31
View other active issues in [containers].
View all other issues in [containers].
View all issues with Open status.
Discussion:
It appears most containers declare but do not define a member-swap function.
This is unfortunate, as all overload the swap algorithm to call the member-swap function! (required for swappable guarantees [Table 37] and Container Requirements [Table 87])
Note in particular that Table 87 gives semantics of a.swap(b) as swap(a,b), yet for all containers we define swap(a,b) to call a.swap(b) - a circular definition.
A quick survey of clause 23 shows that the following containers provide a definition for member-swap:
array queue stack vector
Whereas the following declare it, but do not define the semantics:
deque list map multimap multiset priority_queue set unordered_map unordered_multi_map unordered_multi_set unordered_set
Suggested resolution:
Provide a definition for each of the affected containers...
[ Bellevue: ]
Move to Open and ask Alisdair to provide wording.
[ 2009-07 Frankfurt: ]
Daniel to provide wording. N2590 is no longer applicable.
[ 2009-07-28 Daniel provided wording. ]
- It assumes that the proposed resolution for 883 is applied, which breaks the circularity of definition between member swap and free swap.
- It uses the notation of the pre-concept allocator trait allocator_propagation_map, which might be renamed after the next refactoring phase of generalized allocators.
- It requires that compare objects, key equal functions and hash functions in containers are swapped via unqualified free swap according to 594.
[ 2009-09-30 Daniel adds: ]
The outcome of this issue should be considered with the outcome of 1198 both in style and in content (e.g. bullet 9 suggests to define the semantic of void priority_queue::swap(priority_queue&) in terms of the member swap of the container).
[ 2009-10 Santa Cruz: ]
Looked at, but took no action on as it overlaps too much with N2982. Waiting for a new draft WP.
[ 2009-10 Santa Cruz: ]
Leave as open. Pablo to provide wording.
[ 2009-10-26 Pablo updated wording. Here is the wording he replaced: ]
Add a new Throws clause just after X [allocator.propagation.map]/5:
static void swap(Alloc& a, Alloc& b);Effects: [..]
Throws: Nothing.
[ This exception requirement is added, such that it's combination with the general container requirements of N2723 [container.requirements.general]/9 make it unambiguously clear that the following descriptions of "swaps the allocators" have the following meaning: (a) This swap is done by calling allocator_propagation_map<allocator_type>::swap and (b) This allocator swap does never propagate an exception ]
Change 23.2.4.1 [associative.reqmts.except]/3 as indicated:
For associative containers, no swap function throws an exception unless that exception is thrown by thecopy constructor or copy assignment operatorswap of the container's Pred objects(if any).Change 23.2.5.1 [unord.req.except]/3 as indicated:
For unordered associative containers, no swap function throws an exception unless that exception is thrown by thecopy constructor or copy assignment operatorswap of the container's Hash or Pred objects, respectively(if any).Insert a new paragraph just after 23.3 [sequences]/1:
In addition to being available via inclusion of the <algorithm> header, the swap function templates in 25.3.3 [alg.swap] are also available when the header <queue> is included.[ There is a new issue in process that will suggest a minimum header for swap and move. If this one is provided, this text can be removed and the header dependency should be added to <queue> ]
Add one further clause at the end of 23.3.1.2 [array.special]:
[This part is added, because otherwise array::swap would otherwise contradict the general contract of 23.2.1 [container.requirements.general] p. 10 b. 5]
Throws: Nothing, unless one of the element-wise swap calls throws an exception.
In 23.3.2 [deque], class template deque synopsis change as indicated:
void swap(deque<T,Alloc>&);At the end of 23.3.2.3 [deque.modifiers] add as indicated:
void swap(deque& x);Effects: Exchanges the contents and swaps the allocators of *this with that of x.
Complexity: Constant time.
In 23.3.3 [forwardlist], class template forward_list synposis change as indicated:
void swap(forward_list<T,Allocator>&);At the end of 23.3.3.4 [forwardlist.modifiers] add as indicated:
void swap(forward_list& x);Effects: Exchanges the contents and swaps the allocators of *this with that of x.
Complexity: Constant time.
In 23.3.4 [list], class template list synopsis change as indicated:
void swap(list<T,Allocator>&);At the end of 23.3.4.3 [list.modifiers] add as indicated:
void swap(list& x);Effects: Exchanges the contents and swaps the allocators of *this with that of x.
Complexity: Constant time.
At the end of 23.3.5.2.2 [priqueue.members] add a new prototype description:
void swap(priority_queue& q);Requires: Compare shall satisfy the Swappable requirements ( [swappable]).
[ This requirement is added to ensure that even a user defined swap which is found by ADL for Compare satisfies the Swappable requirements ]
Effects: this->c.swap(q.c); swap(this->comp, q.comp);
Throws: What and if c.swap(q.c) and swap(comp, q.comp) throws.
[ This part is added, because otherwise priority_queue::swap would otherwise contradict the general contract of 23.2.1 [container.requirements.general] p. 10 b. 5 ]
In 23.3.6 [vector], class template vector synopsis change as indicated:
void swap(vector<T,Allocator>&);Change 23.3.6.2 [vector.capacity]/8 as indicated:
void swap(vector<T,Allocator>& x);Effects: Exchanges the contents and capacity() and swaps the allocators of *this with that of x.Insert a new paragraph just before 23.4 [associative]/1:
In addition to being available via inclusion of the <algorithm> header, the swap function templates in 25.3.3 [alg.swap] are also available when any of the headers <map> or <set> are included.
In 23.4.1 [map], class template map synopsis change as indicated:
void swap(map<Key,T,Compare,Allocator>&);At the end of 23.4.1.3 [map.modifiers] add as indicated:
void swap(map& x);Requires: Compare shall satisfy the Swappable requirements ( [swappable]).
[ This requirement is added to ensure that even a user defined swap which is found by ADL for Compare satisfies the Swappable requirements ]
Effects: Exchanges the contents and swaps the allocators of *this with that of x, followed by an unqualified swap of the comparison objects of *this and x.
Complexity: Constant time
In 23.4.2 [multimap], class template multimap synopsis change as indicated:
void swap(multimap<Key,T,Compare,Allocator>&);At the end of 23.4.2.2 [multimap.modifiers] add as indicated:
void swap(multimap& x);Requires: Compare shall satisfy the Swappable requirements ( [swappable]).
Effects: Exchanges the contents and swaps the allocators of *this with that of x, followed by an unqualified swap of the comparison objects of *this and x.
Complexity: Constant time
In 23.4.3 [set], class template set synopsis change as indicated:
void swap(set<Key,Compare,Allocator>&);After section 23.4.3.1 [set.cons] add a new section set modifiers [set.modifiers] and add the following paragraphs:
void swap(set& x);Requires: Compare shall satisfy the Swappable requirements ( [swappable]).
Effects: Exchanges the contents and swaps the allocators of *this with that of x, followed by an unqualified swap of the comparison objects of *this and x.
Complexity: Constant time
In 23.4.4 [multiset], class template multiset synosis, change as indicated:
void swap(multiset<Key,Compare,Allocator>&);After section 23.4.4.1 [multiset.cons] add a new section multiset modifiers [multiset.modifiers] and add the following paragraphs:
void swap(multiset& x);Requires: Compare shall satisfy the Swappable requirements ( [swappable]).
Effects: Exchanges the contents and swaps the allocators of *this with that of x, followed by an unqualified swap of the comparison objects of *this and x.
Complexity: Constant time
Insert a new paragraph just before 23.5 [unord]/1:
In addition to being available via inclusion of the <algorithm> header, the swap function templates in 25.3.3 [alg.swap] are also available when any of the headers <unordered_map> or <unordered_set> are included.After section 23.5.1.2 [unord.map.elem] add a new section unordered_map modifiers [unord.map.modifiers] and add the following paragraphs:
void swap(unordered_map& x);Requires: Hash and Pred shall satisfy the Swappable requirements ( [swappable]).
[ This requirement is added to ensure that even a user defined swap which is found by ADL for Hash and Pred satisfies the Swappable requirements ]
Effects: Exchanges the contents and hash policy and swaps the allocators of *this with that of x, followed by an unqualified swap of the Pred objects and an unqualified swap of the Hash objects of *this and x.
Complexity: Constant time
After section 23.5.2.1 [unord.multimap.cnstr] add a new section unordered_multimap modifiers [unord.multimap.modifiers] and add the following paragraphs:
void swap(unordered_multimap& x);Requires: Hash and Pred shall satisfy the Swappable requirements ( [swappable]).
Effects: Exchanges the contents and hash policy and swaps the allocators of *this with that of x, followed by an unqualified swap of the Pred objects and an unqualified swap of the Hash objects of *this and x
Complexity: Constant time
After section 23.5.3.1 [unord.set.cnstr] add a new section unordered_set modifiers [unord.set.modifiers] and add the following paragraphs:
void swap(unordered_set& x);Requires: Hash and Pred shall satisfy the Swappable requirements ( [swappable]).
Effects: Exchanges the contents and hash policy and swaps the allocators of *this with that of x, followed by an unqualified swap of the Pred objects and an unqualified swap of the Hash objects of *this and x
Complexity: Constant time
After section 23.5.4.1 [unord.multiset.cnstr] add a new section unordered_multiset modifiers [unord.multiset.modifiers] and add the following paragraphs:
void swap(unordered_multiset& x);Requires: Hash and Pred shall satisfy the Swappable requirements ( [swappable]).
Effects: Exchanges the contents and hash policy and swaps the allocators of *this with that of x, followed by an unqualified swap of the Pred objects and an unqualified swap of the Hash objects of *this and x
Complexity: Constant time
[ 2009-10-30 Pablo and Daniel updated wording. ]
Proposed resolution:
[ This resolution is based on the September 2009 WP, N2960, except that it assumes that N2982 and issues 883 and 1232 have already been applied. Note in particular that Table 91 in N2960 is refered to as Table 90 because N2982 removed the old Table 90. This resolution also addresses issue 431. ]
In 23.2.1 [container.requirements.general], replace the a.swap(b) row in table 90, "container requirements" (was table 91 before the application of N2982 to the WP):
a.swap(b)voidswap(a,b)Exchange the contents of a and b.(Note A) swap(a,b)voida.swap(b)(Note A)
Modify the notes immediately following Table 90 in 23.2.1 [container.requirements.general] as follows (The wording below is after the application of N2982 to N2960. The editor might also want to combine Notes A and B into one.):
Notes: the algorithms
swap(),equal() and lexicographical_compare() are defined in Clause 25. Those entries marked "(Note A)" or "(Note B)"shouldhave linear complexity for array and constant complexity for all other standard containers.
In 23.2.1 [container.requirements.general], after paragraph 9, add:
The expression
a.swap(b), for containersaandbof a standard container type other thanarray, exchanges the values ofaandbwithout invoking any move, copy, or swap operations on the individual container elements. AnyCompare,Pred, orHashfunction objects belonging toaandbshall satisfy theSwappablerequirements and are exchanged by unqualified calls to non-memberswap. Ifallocator_traits<allocator_type>::propagate_on_container_swap::value == true, then the allocators ofaandbare also exchanged using an unqualified call to non-memberswap. Otherwise, the behavior is undefined unlessa.get_allocator() == b.get_allocator(). Each iterator refering to an element in one container before the swap shall refer to the same element in the other container after the swap. It is unspecified whether an iterator with valuea.end()before the swap will have valueb.end()after the swap. In addition to being available via inclusion of the<utility>header, theswapfunction template in 25.3.3 [alg.swap] is also available within the definition of every standard container'sswapfunction.
[ Note to the editor: Paragraph 2 starts with a sentence fragment, clearly from an editing or source-control error. ]
Modify 23.2.4.1 [associative.reqmts.except] as follows:
23.2.4.1 Exception safety guarantees 23.2.4.1 [associative.reqmts.except]
For associative containers, no
clear()function throws an exception.erase(k)does not throw an exception unless that exception is thrown by the container'sobject (if any).PredCompareFor associative containers, if an exception is thrown by any operation from within an
insert()function inserting a single element, theinsert()function has no effect.For associative containers, no
swapfunction throws an exception unless that exception is thrown by thecopy constructor or copy assignment operatorswap of the container'sobject (if any).PredCompare
Modify 23.2.5.1 [unord.req.except], paragraph 3 as follows:
For unordered associative containers, no
swapfunction throws an exception unless that exception is thrown by thecopy constructor or copy assignment operatorswap of the container'sHashorPredobject (if any).
Modify section 23.3.1.2 [array.special]:
array specialized algorithms 23.3.1.2 [array.special]
template <class T, size_t N> void swap(array<T,N>& x,array<T,N>& y);Effects:
swap_ranges(x.begin(), x.end(), y.begin() );x.swap(y);
Add a new section after 23.3.1.5 [array.fill] (Note to the editor: array::fill make use of a concept requirement that must be removed or changed to text.):
array::swap [array.swap]
void swap(array& y);Effects:
swap_ranges(this->begin(), this->end(), y.begin() );Throws: Nothing unless one of the element-wise swap calls throws an exception.
[Note: Unlike other containers'
swapfunctions,array::swaptakes linear, not constant, time, may exit via an exception, and does not cause iterators to become associated with the other container. — end note]
Insert a new paragraph just after 23.3.5 [container.adaptors]/1:
For container adaptors, no
swapfunction throws an exception unless that exception is thrown by the swap of the adaptor'sContainerorCompareobject (if any).
Section: 25.4.4 [alg.merge] Status: Tentatively Ready Submitter: Daniel Krügler Opened: 2008-01-25 Last modified: 2010-02-10
View all issues with Tentatively Ready status.
Discussion:
Though issue 283 has fixed many open issues, it seems that some are still open:
Both 25.3.4 [lib.alg.merge] in 14882:2003 and 25.4.4 [alg.merge] in N2461 have no Requires element and the Effects element contains some requirements, which is probably editorial. Worse is that:
[ Post Summit Alisdair adds: ]
Suggest:
(where last is equal to next(result, distance(first1, last1) + distance(first2, last2)), such that resulting range will be sorted in non-decreasing order; that is, for every iterator i in [result,last) other than result, the condition *i < *prev(i) or, respectively, comp(*i, *prev(i)) will be false.Note that this might still not be technically accurate in the case of InputIterators, depending on other resolutions working their way through the system (1011).
[ Post Summit Daniel adds: ]
If we want to use prev and next here (Note: merge is sufficiently satisfied with InputIterator) we should instead *add* more to 25 [algorithms]/6, but I can currently not propose any good wording for this.
[ Batavia (2009-05): ]
Pete points out the existing wording in [algorithms]/4 that permits the use of + in algorithm specifications.
Alisdair points out that that wording may not apply to input iterators.
Move to Review.
[ 2009-07 Frankfurt: ]
Move to Ready.
[ 2009-08-23 Daniel reopens: ]
The proposed wording must be rephrased, because the part
for every iterator i in [result,last) other than result, the condition *i < *(i - 1) or, respectively, comp(*i, *(i - 1)) will be false"isn't meaningful, because the range [result,last) is that of a pure OutputIterator, which is not readable in general.
[Howard: Proposed wording updated by Daniel, status moved from Ready to Review.]
[ 2009-10 Santa Cruz: ]
Matt has some different words to propose. Those words have been moved into the proposed wording section, and the original proposed wording now appears here:
In 25.4.4 [alg.merge] replace p.1+ 2:
Effects:
MergesCopies all the elements of the two sorted ranges [first1,last1) and [first2,last2) into the range [result,result + (last1 - first1) + (last2 - first2)) , such that resulting range will be sorted in non-decreasing order; that is for every pair of iterators i and j of either input ranges, where *i was copied to the output range before *j was copied to the output range, the condition *j < *i or, respectively, comp(*j, *i) will be false.Requires:The resulting range shall not overlap with either of the original ranges.
The list will be sorted in non-decreasing order according to the ordering defined by comp; that is, for every iterator i in [first,last) other than first, the condition *i < *(i - 1) or comp(*i, *(i - 1)) will be false.
[ 2010-02-10 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]
Proposed resolution:
Change 25.4.4 [alg.merge] 1 and 2:
1
Effects: Merges two sorted ranges [first1,last1) and [first2,last2) into the range [result, result + (last1 - first1) + (last2 - first2)).Effects: Copies all the elements of the two ranges [first1,last1) and [first2,last2) into the range [result, result_last), where result_last is result + (last1 - first1) + (last2 - first2), such that the resulting range satisfies is_sorted(result, result_last) or is_sorted(result, result_last, comp), respectively.
2 Requires: The ranges [first1,last1) and [first2,last2) shall be sorted with respect to operator< or comp. The resulting range shall not overlap with either of the original ranges.
The list will be sorted in non-decreasing order according to the ordering defined by comp; that is, for every iterator i in [first,last) other than first, the condition *i < *(i - 1) or comp(*i, *(i - 1)) will be false.
Change 25.4.4 [alg.merge]/6+7 as indicated [This ensures harmonization between inplace_merge and merge]
6 Effects: Merges two
sortedconsecutive ranges [first,middle) and [middle,last), putting the result of the merge into the range [first,last). The resulting range will be in non-decreasing order; that is, for every iterator i in [first,last) other than first, the condition *i < *(i - 1) or, respectively, comp(*i, *(i - 1)) will be false.7 Requires: The ranges [first,middle) and [middle,last) shall be sorted with respect to operator< or comp. The type of *first shall satisfy the Swappable requirements (37), the MoveConstructible requirements (Table 33), and the the MoveAssignable requirements (Table 35).
Section: 20.5 [tuple] Status: Open Submitter: Lawrence Crowl Opened: 2008-02-18 Last modified: 2009-10-23
View all other issues in [tuple].
View all issues with Open status.
Discussion:
Classes with trivial special member functions are inherently more efficient than classes without such functions. This efficiency is particularly pronounced on modern ABIs that can pass small classes in registers. Examples include value classes such as complex numbers and floating-point intervals. Perhaps more important, though, are classes that are simple collections, like pair and tuple. When the parameter types of these classes are trivial, the pairs and tuples themselves can be trivial, leading to substantial performance wins.
The current working draft make specification of trivial functions (where possible) much easer through defaulted and deleted functions. As long as the semantics of defaulted and deleted functions match the intended semantics, specification of defaulted and deleted functions will yield more efficient programs.
There are at least two cases where specification of an explicitly defaulted function may be desirable.
First, the std::pair template has a non-trivial default constructor, which prevents static initialization of the pair even when the types are statically initializable. Changing the definition to
pair() = default;
would enable such initialization. Unfortunately, the change is not semantically neutral in that the current definition effectively forces value initialization whereas the change would not value initialize in some contexts.
** Does the committee confirm that forced value initialization was the intent? If not, does the committee wish to change the behavior of std::pair in C++0x?
Second, the same default constructor issue applies to std::tuple. Furthermore, the tuple copy constructor is current non-trivial, which effectively prevents passing it in registers. To enable passing tuples in registers, the copy constructor should be make explicitly defaulted. The new declarations are:
tuple() = default; tuple(const tuple&) = default;
This changes is not implementation neutral. In particular, it prevents implementations based on pointers to the parameter types. It does however, permit implementations using the parameter types as bases.
** How does the committee wish to trade implementation efficiency versus implementation flexibility?
[ Bellevue: ]
General agreement; the first half of the issue is NAD.
Before voting on the second half, it was agreed that a "Strongly Favor" vote meant support for trivial tuples (assuming usual requirements met), even at the expense of other desired qualities. A "Weakly Favor" vote meant support only if not at the expense of other desired qualities.
Concensus: Go forward, but not at expense of other desired qualities.
It was agreed to Alisdair should fold this work in with his other pair/tuple action items, above, and that issue 801 should be "open", but tabled until Alisdair's proposals are disposed of.
[ 2009-05-27 Daniel adds: ]
This is partly solved by 1117.
[ 2009-07 Frankfurt: ]
Wait for dust to settle from fixing exception safety problem with rvalue refs.
[ 2009-07-20 Alisdair adds: ]
Basically, this issue is what should we do with the default constructor for pairs and tuples of trivial types. The motivation of the issue was to force static initialization rather than dynamic initialization, and was rejected in the case of pair as it would change the meaning of existing programs. The advice was "do the best we can" for tuple without changing existing meaning.
Frankfurt seems to simply wait and see the resolution on no-throw move constructors, which (I believe) is only tangentially related to this issue, but as good as any to defer until Santa Cruz.
Looking again now, I think constant (static) initialization for pair can be salvaged by making the default construct constexpr. I have a clarification from Core that this is intended to work, even if the constructor is not trivial/constexpr, so long as no temporaries are implied in the process (even if elided).
[ 2009-10 Santa Cruz: ]
Leave as open. Alisdair to provide wording.
Proposed resolution:
Section: 20.3.4 [pairs] Status: Tentatively Ready Submitter: Doug Gregor Opened: 2008-03-14 Last modified: 2010-02-09
View other active issues in [pairs].
View all other issues in [pairs].
View all issues with Tentatively Ready status.
Discussion:
#include <utility>
int main()
{
std::pair<char *, char *> p (0,0);
}
I just got a bug report about that, because it's valid C++03, but not C++0x. The important realization, for me, is that the emplace proposal---which made push_back variadic, causing the push_back(0) issue---didn't cause this break in backward compatibility. The break actually happened when we added this pair constructor as part of adding rvalue references into the language, long before variadic templates or emplace came along:
template<class U, class V> pair(U&& x, V&& y);
Now, concepts will address this issue by constraining that pair constructor to only U's and V's that can properly construct "first" and "second", e.g. (from N2322):
template<class U , class V > requires Constructible<T1, U&&> && Constructible<T2, V&&> pair(U&& x , V&& y );
[ San Francisco: ]
Suggested to resolve using pass-by-value for that case.
Side question: Should pair interoperate with tuples? Can construct a tuple of a pair, but not a pair from a two-element tuple.
Related to 885.
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-10 Santa Cruz: ]
Leave as open. Howard to provide wording.
[ 2010-02-06 Howard provided wording. ]
[ 2010-02-09 Moved to Tentatively Ready after 6 positive votes on c++std-lib. ]
Rationale:
[ San Francisco: ]
Solved by N2770.
[ The rationale is obsolete. ]
Proposed resolution:
Add a paragraph to 20.3.4 [pairs]:
template<class U, class V> pair(U&& x, V&& y);6 Effects: The constructor initializes first with std::forward<U>(x) and second with std::forward<V>(y).
Remarks: U shall be implicitly convertible to first_type and V shall be implicitly convertible to second_type, else this constructor shall not participate in overload resolution.
Section: 20.7.15.2.4 [func.wrap.func.inv] Status: Tentatively NAD Editorial Submitter: Alisdair Meredith Opened: 2008-03-16 Last modified: 2010-02-12
View all issues with Tentatively NAD Editorial status.
Discussion:
std::function and reference_closure should use "perfect forwarding" as described in the rvalue core proposal.
[ Sophia Antipolis: ]
According to Doug Gregor, as far as std::function is concerned, perfect forwarding can not be obtained because of type erasure. Not everyone agreed with this diagnosis of forwarding.
[ 2009-05-01 Howard adds: ]
Sebastian Gesemann brought to my attention that the CopyConstructible requirement on function's ArgTypes... is an unnecessary restriction.
template<Returnable R, CopyConstructible... ArgTypes> class function<R(ArgTypes...)> ...On further investigation, this complaint seemed to be the same issue as this one. I believe the reason CopyConstructible was put on ArgTypes in the first place was because of the nature of the invoke member:
template<class R, class ...ArgTypes> R function<R(ArgTypes...)>::operator()(ArgTypes... arg) const { if (f_ == 0) throw bad_function_call(); return (*f_)(arg...); }However now with rvalue-refs, "by value" no longer implies CopyConstructible (as Sebastian correctly points out). If rvalue arguments are supplied, MoveConstructible is sufficient. Furthermore, the constraint need not be applied in function if I understand correctly. Rather the client must apply the proper constraints at the call site. Therefore, at the very least, I recommend that CopyConstructible be removed from the template class function.
Furthermore we need to mandate that the invoker is coded as:
template<class R, class ...ArgTypes> R function<R(ArgTypes...)>::operator()(ArgTypes... arg) const { if (f_ == 0) throw bad_function_call(); return (*f_)(std::forward<ArgTypes>(arg)...); }Note that ArgTypes&& (the "perfect forwarding signature") is not appropriate here as this is not a deduced context for ArgTypes. Instead the client's arguments must implicitly convert to the non-deduced ArgType type. Catching these arguments by value makes sense to enable decay.
Next forward is used to move the ArgTypes as efficiently as possible, and also with minimum requirements (not CopyConstructible) to the type-erased functor. For object types, this will be a move. For reference type ArgTypes, this will be a copy. The end result must be that the following is a valid program:
#include <functional> #include <memory> #include <cassert> std::unique_ptr<int> f(std::unique_ptr<int> p, int& i) { ++i; return std::move(p); } int main() { int i = 2; std::function<std::unique_ptr<int>(std::unique_ptr<int>, int&> g(f); std::unique_ptr<int> p = g(std::unique_ptr<int>(new int(1)), i); assert(*p == 1); assert(i == 3); }[ Tested in pre-concepts rvalue-ref-enabled compiler. ]
In the example above, the first ArgType is unique_ptr<int> and the second ArgType is int&. Both must work!
[ 2009-05-27 Daniel adds: ]
in the 2009-05-01 comment of above mentioned issue Howard
- Recommends to replace the CopyConstructible requirement by a MoveConstructible requirement
- Says: "Furthermore, the constraint need not be applied in function if I understand correctly. Rather the client must apply the proper constraints at the call site"
I'm fine with (a), but I think comment (b) is incorrect, at least in the sense I read these sentences. Let's look at Howard's example code:
function<R(ArgTypes...)>::operator()(ArgTypes... arg) const { if (f_ == 0) throw bad_function_call(); return (*f_)(std::forward<ArgTypes>(arg)...); }In the constrained scope of this operator() overload the expression "(*f_)(std::forward<ArgTypes>(arg)...)" must be valid. How can it do so, if ArgTypes aren't at least MoveConstructible?
[ 2009-07 Frankfurt: ]
Leave this open and wait until concepts are removed from the Working Draft so that we know how to write the proposed resolution in terms of diffs to otherwise stable text.
[ 2009-10 Santa Cruz: ]
Leave as open. Howard to provide wording. Howard welcomes any help.
[ 2009-12-12 Jonathan Wakely adds: ]
20.7.15.2 [func.wrap.func] says
2 A function object f of type F is Callable for argument types T1, T2, ..., TN in ArgTypes and a return type R, if, given lvalues t1, t2, ..., tN of types T1, T2, ..., TN, respectively, INVOKE (f, t1, t2, ..., tN) is well formed (20.7.2) and, if R is not void, convertible to R.N.B. lvalues, which means you can't use function<R(T&&)> or function<R(unique_ptr<T>)>
I recently implemented rvalue arguments in GCC's std::function, all that was needed was to use std::forward<ArgTypes> in a few places. The example in issue 815 works.
I think 815 could be resolved by removing the requirement that the target function be callable with lvalues. Saying ArgTypes need to be CopyConstructible is wrong, and IMHO saying MoveConstructible is unnecessary, since the by-value signature implies that already, but if it is needed it should only be on operator(), not the whole class (you could in theory instantiate std::function<R(noncopyable)> as long as you don't invoke the call operator.)
I think defining invocation in terms of INVOKE already implies perfect forwarding, so we don't need to say explicitly that std::forward should be used (N.B. the types that are forwarded are those in ArgTypes, which can differ from the actual parameter types of the target function. The actual parameter types have gone via type erasure, but that's not a problem - IMHO forwarding the arguments as ArgTypes is the right thing to do anyway.)
Is it sufficient to simply replace "lvalues" with "values"? or do we need to say something like "lvalues when Ti is an lvalue-reference and rvalues otherwise"? I prefer the former, so I propose the following resolution for 815:
Edit 20.7.15.2 [func.wrap.func] paragraph 2:
2 A function object f of type F is Callable for argument types T1, T2, ..., TN in ArgTypes and a return type R, if, givenlvalues t1, t2, ..., tN of types T1, T2, ..., TN, respectively, INVOKE (f, t1, t2, ..., tN) is well formed (20.7.2) and, if R is not void, convertible to R.
[ 2009-12-12 Daniel adds: ]
I don't like the reduction to "values" and prefer the alternative solution suggested using "lvalues when Ti is an lvalue-reference and rvalues otherwise". The reason why I dislike the shorter version is based on different usages of "values" as part of defining the semantics of requirement tables via expressions. E.g. 20.2.1 [utility.arg.requirements]/1 says "a, b, and c are values of type const T;" or similar in 23.2.1 [container.requirements.general]/4 or /14 etc. My current reading of all these parts is that both rvalues and lvalues are required to be supported, but this interpretation would violate the intention of the suggested fix of #815, if I correctly understand Jonathan's rationale.
[ 2009-12-12 Howard adds: ]
"lvalues when Ti is an lvalue-reference and rvalues otherwise"doesn't quite work here because the Ti aren't deduced. They are specified by the function type. Ti might be const int& (an lvalue reference) and a valid ti might be 2 (a non-const rvalue). I've taken another stab at the wording using "expressions" and "bindable to".
[ 2010-02-09 Wording updated by Jonathan, Ganesh and Daniel. ]
[ 2010-02-09 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]
[ 2010-02-10 Daniel opens to improve wording. ]
[ 2010-02-11 This issue is now addressed by 870. ]
[ 2010-02-12 Moved to Tentatively NAD Editorial after 5 positive votes on c++std-lib. Rationale added below. ]
Rationale:
Addressed by 870.
Proposed resolution:
Edit 20.7.15.2 [func.wrap.func] paragraph 2:
2 A function object f of type F is Callable for argument types
T1, T2, ..., TN inArgTypes andareturn type R,if, given lvalues t1, t2, ..., tN of types T1, T2, ..., TN, respectively,the expression INVOKE(f, declval<ArgTypes>()..., Rt1, t2, ..., tN), considered as an unevaluated operand (5 [expr]), is well formed (20.7.2)and, if R is not void, convertible to R.
Section: 20.7.11.1.3 [func.bind.bind] Status: Tentatively NAD Editorial Submitter: Stephan T. Lavavej Opened: 2008-02-08 Last modified: 2010-02-11
View other active issues in [func.bind.bind].
View all other issues in [func.bind.bind].
View all issues with Tentatively NAD Editorial status.
Discussion:
Library Issue 527 notes that bind(f, t1, ..., tN) should be nofail when f, t1, ..., tN have nofail copy ctors.
However, no guarantees are provided for the copy ctor of the functor returned by bind(). (It's guaranteed to have a copy ctor, which can throw implementation-defined exceptions: bind() returns a forwarding call wrapper, TR1 3.6.3/2. A forwarding call wrapper is a call wrapper, TR1 3.3/4. Every call wrapper shall be CopyConstructible, TR1 3.3/4. Everything without an exception-specification may throw implementation-defined exceptions unless otherwise specified, C++03 17.4.4.8/3.)
Should the nofail guarantee requested by Library Issue 527 be extended to cover both calling bind() and copying the returned functor?
[ Howard adds: ]
tuple construction should probably have a similar guarantee.
[ San Francisco: ]
Howard to provide wording.
[ Post Summit, Anthony provided wording. ]
[ Batavia (2009-05): ]
Part of all of this issue appears to be rendered moot by the proposed resolution to issue 817 (q.v.). We recommend the issues be considered simultaneously (or possibly even merged) to ensure there is no overlap. Move to Open, and likewise for issue 817.
[ 2009-07 Frankfurt: ]
Related to 817 (see below). Leave Open.
[ 2009-10 Santa Cruz: ]
Move to Ready. Decoupling from issue 817.
[ 2010-02-11 Moved from Ready to Tentatively NAD Editorial, rationale added below. ]
Rationale:
This issue is solved as proposed by 817.
Proposed resolution:
Add a new sentence to the end of paragraphs 2 and 4 of 20.7.11.1.3 [func.bind.bind]:
-2- Returns: A forwarding call wrapper g with a weak result type (20.6.2). The effect of g(u1, u2, ..., uM) shall be INVOKE(f, v1, v2, ..., vN, Callable<F cv,V1, V2, ..., VN>::result_type), 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 F or any of the types in BoundArgs... throw an exception.
...
-5- Returns: A forwarding call wrapper g with a nested type result_type defined as a synonym for R. The effect of g(u1, u2, ..., uM) shall be INVOKE(f, v1, v2, ..., vN, R), where 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 F or any of the types in BoundArgs... throw an exception.
Section: 20.7.11.1.3 [func.bind.bind] Status: Tentatively Ready Submitter: Howard Hinnant Opened: 2008-03-17 Last modified: 2009-11-16
View other active issues in [func.bind.bind].
View all other issues in [func.bind.bind].
View all issues with Tentatively Ready status.
Discussion:
Addresses US 72, JP 38 and DE 21
The functor returned by bind() should have a move constructor that requires only move construction of its contained functor and bound arguments. That way move-only functors can be passed to objects such as thread.
This issue is related to issue 816.
US 72:
bind should support move-only functors and bound arguments.
JP 38:
add the move requirement for bind's return type.
For example, assume following th1 and th2,
void f(vector<int> v) { } vector<int> v{ ... }; thread th1([v]{ f(v); }); thread th2(bind(f, v));When function object are set to thread, v is moved to th1's lambda expression in a Move Constructor of lambda expression because th1's lambda expression has a Move Constructor. But bind of th2's return type doesn't have the requirement of Move, so it may not moved but copied.
Add the requirement of move to get rid of this useless copy.
And also, add the MoveConstructible as well as CopyConstructible.
DE 21
The specification for bind claims twice that "the values and types for the bound arguments v1, v2, ..., vN are determined as specified below". No such specification appears to exist.
[ San Francisco: ]
Howard to provide wording.
[ Post Summit Alisdair and Howard provided wording. ]
Several issues are being combined in this resolution. They are all touching the same words so this is an attempt to keep one issue from stepping on another, and a place to see the complete solution in one place.
- bind needs to be "moved".
- 20.7.11.1.3 [func.bind.bind]/p3, p6 and p7 were accidently removed from N2798.
- Issue 929 argues for a way to pass by && for efficiency but retain the decaying behavior of pass by value for the thread constructor. That same solution is applicable here.
[ Batavia (2009-05): ]
We were going to recommend moving this issue to Tentatively Ready until we noticed potential overlap with issue 816 (q.v.).
Move to Open, and recommend both issues be considered together (and possibly merged).
[ 2009-07 Frankfurt: ]
The proposed resolution uses concepts. Leave Open.
[ 2009-10 Santa Cruz: ]
Leave as Open. Howard to provide deconceptified wording.
[ 2009-11-07 Howard updates wording. ]
[ 2009-11-15 Further updates by Peter, Chris and Daniel. ]
[ Moved to Tentatively Ready after 6 positive votes on c++std-lib. ]
Proposed resolution:
Change 20.7 [function.objects] p2:
template<class Fn, class...TypesBoundArgs> unspecified bind(Fn&&,TypesBoundArgs&&...); template<class R, class Fn, class...TypesBoundArgs> unspecified bind(Fn&&,TypesBoundArgs&&...);
Change 20.7.2 [func.require]:
4 Every call wrapper (20.7.1 [func.def]) shall be
CopyMoveConstructible. A simple call wrapper is a call wrapper that is CopyConstructible and CopyAssignable and whose copy constructor, move constructor and assignment operator do not throw exceptions. A forwarding call wrapper is a call wrapper that can be called with an argument list. [Note: in a typical implementation forwarding call wrappers have an overloaded function call operator of the formtemplate<class...ArgTypesUnBoundsArgs> R operator()(ArgTypesUnBoundsArgs&&... unbound_args) cv-qual;— end note]
Change 20.7.11.1.3 [func.bind.bind]:
Within this clause:
- Let FD be a synonym for the type decay<F>::type.
- Let fd be an lvalue of type FD constructed from std::forward<F>(f).
- Let Ti be a synonym for the ith type in the template parameter pack BoundArgs.
- Let TiD be a synonym for the type decay<Ti>::type.
- Let ti be the ith argument in the function parameter pack bound_args.
- Let tid be an lvalue of type TiD constructed from std::forward<Ti>(ti).
- Let Uj be the jth deduced type of the UnBoundArgs&&... parameter of the operator() of the forwarding call wrapper.
- Let uj be the jth argument associated with Uj.
template<class F, class... BoundArgs> unspecified bind(F&& f, BoundArgs&&... bound_args);-1- Requires: is_constructible<FD, F>::value shall be true. For each Ti in BoundArgs, is_constructible<TiD, Ti>::value shall be true.
F and each Ti in BoundArgs shall be CopyConstructible.INVOKE(fd, w1, w2, ..., wN) (20.7.2 [func.require]) shall be a valid expression for some values w1, w2, ..., wN, where N == sizeof...(bound_args).-2- Returns: A forwarding call wrapper g with a weak result type (20.7.2 [func.require]). The effect of g(u1, u2, ..., uM) shall be INVOKE(fd, v1, v2, ..., vN, result_of<FD cv (V1, V2, ..., VN)>::type), 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 TiD throws an exception.
-3- Throws: Nothing unless the
copyconstructionorofFfd or of one of the values tidtypes in the BoundArgs... pack expansionthrows an exception.Remarks: The unspecified return type shall satisfy the requirements of MoveConstructible. If all of FD and TiD satisfy the requirements of CopyConstructible then the unspecified return type shall satisfy the requirements of CopyConstructible. [Note: This implies that all of FD and TiD shall be MoveConstructible — end note]
template<class R, class F, class... BoundArgs> unspecified bind(F&& f, BoundArgs&&... bound_args);-4- Requires: is_constructible<FD, F>::value shall be true. For each Ti in BoundArgs, is_constructible<TiD, Ti>::value shall be true.
F and each Ti in BoundArgs shall be CopyConstructible.INVOKE(fd, w1, w2, ..., wN) shall be a valid expression for some values w1, w2, ..., wN, where N == sizeof...(bound_args).-5- Returns: A forwarding call wrapper g with a nested type result_type defined as a synonym for R. The effect of g(u1, u2, ..., uM) shall be INVOKE(fd, v1, v2, ..., vN, R), where 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 TiD throws an exception.
-6- Throws: Nothing unless the
copyconstructionorofFfd or of one of the values tidtypes in the BoundArgs... pack expansionthrows an exception.Remarks: The unspecified return type shall satisfy the requirements of MoveConstructible. If all of FD and TiD satisfy the requirements of CopyConstructible then the unspecified return type shall satisfy the requirements of CopyConstructible. [Note: This implies that all of FD and TiD shall be MoveConstructible — end note]
-7- The values of the bound arguments v1, v2, ..., vN and their corresponding types V1, V2, ..., VN depend on the types TiD derived from
of the corresponding argument ti in bound_args of type Ti in BoundArgs inthe call to bind and the cv-qualifiers cv of the call wrapper g as follows:
- if
tiTiD isof typereference_wrapper<T> the argument is tid.get() and its type Vi is T&;- if the value of
std::is_bind_expression<TiD>::value is true the argument is tid(std::forward<Uj>(uj)...u1, u2, ..., uM) and its type Vi is result_of<TiD cv (Uj...U1&, U2&, ..., UM&)>::type;- if the value j of
std::is_placeholder<TiD>::value is not zero the argument is std::forward<Uj>(uj) and its type Vi is Uj&&;- otherwise the value is tid and its type Vi is TiD cv &.
Section: 18.8.6 [except.nested] Status: Open Submitter: Alisdair Meredith Opened: 2008-03-25 Last modified: 2009-11-09
View other active issues in [except.nested].
View all other issues in [except.nested].
View all issues with Open status.
Discussion:
Looking at the wording I submitted for rethrow_if_nested, I don't think I got it quite right.
The current wording says:
template <class E> void rethrow_if_nested(const E& e);Effects: Calls e.rethrow_nested() only if e is publicly derived from nested_exception.
This is trying to be a bit subtle, by requiring e (not E) to be publicly derived from nested_exception the idea is that a dynamic_cast would be required to be sure. Unfortunately, if e is dynamically but not statically derived from nested_exception, e.rethrow_nested() is ill-formed.
[ San Francisco: ]
Alisdair was volunteered to provide wording.
[ 2009-10 Santa Cruz: ]
Leave as Open. Alisdair to provide wording.
[ 2009-11-09 Alisdair provided wording. ]
Proposed resolution:
Change 18.8.6 [except.nested], p8:
template <class E> void rethrow_if_nested(const E& e);-8- Effects: Calls e.rethrow_nested() only if the dynamic type of e is publicly and unambiguously derived from nested_exception.
Section: 20.8.14.2 [unique.ptr.single] Status: Open Submitter: Daniel Krügler Opened: 2008-05-14 Last modified: 2010-01-16
View other active issues in [unique.ptr.single].
View all other issues in [unique.ptr.single].
View all issues with Open status.
Discussion:
Issue 673 (including recent updates by 821) proposes a useful extension point for unique_ptr by granting support for an optional deleter_type::pointer to act as pointer-like replacement for element_type* (In the following: pointer).
Unfortunately no requirements are specified for the type pointer which has impact on at least two key features of unique_ptr:
Unique_ptr specification makes great efforts to require that essentially *all* operations cannot throw and therefore adds proper wording to the affected operations of the deleter as well. If user-provided pointer-emulating types ("smart pointers") will be allowed, either *all* throw-nothing clauses have to be replaced by weaker "An exception is thrown only if pointer's {op} throws an exception"-clauses or it has to be said explicitly that all used operations of pointer are required *not* to throw. I understand the main focus of unique_ptr to be as near as possible to the advantages of native pointers which cannot fail and thus strongly favor the second choice. Also, the alternative position would make it much harder to write safe and simple template code for unique_ptr. Additionally, I assume that a general statement need to be given that all of the expressions of pointer used to define semantics are required to be well-formed and well-defined (also as back-end for 762).
[ Sophia Antipolis: ]
Howard: We maybe need a core concept PointerLike, but we don't need the arithmetic (see shared_ptr vs. vector<T>::iterator.
Howard will go through and enumerate the individual requirements wrt. pointer for each member function.
[ 2009-07 Frankfurt: ]
Move to Ready.
[ 2009-10-15 Alisdair pulls from Ready: ]
I hate to pull an issue out of Ready status, but I don't think 834 is fully baked yet.
For reference the proposed resolution is to add the following words:
unique_ptr<T, D>::pointer's operations shall be well-formed, shall have well defined behavior, and shall not throw exceptions.This leaves me with a big question : which operations?
Are all pointer operations required to be nothrow, including operations that have nothing to do with interactions with unique_ptr? This was much simpler with concepts where we could point to operations within a certain concept, and so nail down the interactions.
[ 2009-10-15 Daniel adds: ]
I volunteer to prepare a more fine-grained solution, but I would like to ask for feedback that helps me doing so. If this question is asked early in the meeting I might be able to fix it within the week, but I cannot promise that now.
[ 2009-10 Santa Cruz: ]
Leave in open. Daniel to provide wording as already suggested.
[ 2009-12-22 Daniel provided wording and rationale. ]
Rationale:
The here proposed resolution has considerable overlap with the requirements that are used in the allocator requirements.
This might be a convincing argument to isolate the common subset into one requirement. The reason I did not do that is basically because we might find out that they are either over-constraining or under-constraining at this late point of specification. Note also that as a result of the idea of a general requirement set I added the requirement
A default-initialized object may have a singular value
even though this does not play a relevant role for unique_ptr.
One further characteristics of the resolution is that availability of relational operators of unique_ptr<T, D>::pointer is not part of the basic requirements, which is in sync with the allocator requirements on pointer-like (this means that unique_ptr can hold a void_pointer or const_void_pointer).
Proposed resolution:
Change 20.8.14.2 [unique.ptr.single]/1 as indicated: [The intent is to replace the coupling between T* and the deleter's operator() by a coupling between unique_ptr<T, D>::pointer and this operator()]
1 - The default type for the template parameter D is default_delete. A client-supplied template argument D shall be a function pointer or functor for which, given a value d of type D and apointervalue ptr of typeT*unique_ptr<T, D>::pointer, the expression d(ptr) is valid and has the effect of deallocating the pointer as appropriate for that deleter. D may also be an lvalue-reference to a deleter.
Change 20.8.14.2 [unique.ptr.single]/3 as indicated:
3 - If the type remove_reference<D>::type::pointer exists, then unique_ptr<T, D>::pointer shall be a synonym for remove_reference<D>::type::pointer. Otherwise unique_ptr<T, D>::pointer shall be a synonym for T*. The type unique_ptr<T, D>::pointer shall
besatisfy the requirements of EqualityComparable, DefaultConstructible, CopyConstructible(Table 34) and, CopyAssignable(Table 36), Swappable, and Destructible (20.2.1 [utility.arg.requirements]). A default-initialized object may have a singular value. A value-initialized object produces the null value of the type. The null value shall be equivalent only to itself. An object of this type can be copy-initialized with a value of type nullptr_t, compared for equality with a value of type nullptr_t, and assigned a value of type nullptr_t. The effect shall be as if a value-initialized object had been used in place of the null pointer constant. An object p of this type can be contextually converted to bool. The effect shall be as if p != nullptr had been evaluated in place of p. No operation on this type which is part of the above mentioned requirements shall exit via an exception.[Note: Given an allocator type X (20.2.2 [allocator.requirements]), the types X::pointer, X::const_pointer, X::void_pointer, and X::const_void_pointer may be used as unique_ptr<T, D>::pointer — end note]
In addition to being available via inclusion of the <utility> header, the swap function template in 20.3.2 [utility.swap] is also available within the definition of unique_ptr's swap function.
Change 20.8.14.2.1 [unique.ptr.single.ctor]/2+3 as indicated: [The first change ensures that we explicitly say, how the stored pointer is initialized. This is important for a constexpr function, because this may make a difference for user-defined pointer-like types]
constexpr unique_ptr();...
2 - Effects: Constructs a unique_ptr which owns nothing, value-initializing the stored pointer.
3 - Postconditions: get() ==
0nullptr.
Change 20.8.14.2.1 [unique.ptr.single.ctor]/6+7 as indicated: [This is a step-by-fix to ensure consistency to the changes of N2976]
unique_ptr(pointer p);...
6 - Effects: Constructs a unique_ptr which owns p, initializing the stored pointer with p.
7 - Postconditions: get() == p. get_deleter() returns a reference to a
default constructedvalue-initialized deleter D.
Insert a new effects clause in 20.8.14.2.1 [unique.ptr.single.ctor] just before p. 14: [The intent is to fix the current lack of specification in which way the stored pointer is initialized]
unique_ptr(pointer p,implementation-definedsee below d1); unique_ptr(pointer p,implementation-definedsee below d2);...
Effects: Constructs a unique_ptr which owns p, initializing the stored pointer with p and the initializing the deleter as described above.
14 - Postconditions: get() == p. get_deleter() returns a reference to the internally stored deleter. If D is a reference type then get_deleter() returns a reference to the lvalue d.
Change 20.8.14.2.1 [unique.ptr.single.ctor]/18+22 as indicated: [The intent is to clarify that the moved-from source must contain a null pointer, there is no other choice left]
unique_ptr(unique_ptr&& u);[..]
18 - Postconditions: get() == value u.get() had before the construction and u.get() == nullptr. get_deleter() returns a reference to the internally stored deleter which was constructed from u.get_deleter(). If D is a reference type then get_deleter() and u.get_deleter() both reference the same lvalue deleter.
template <class U, class E> unique_ptr(unique_ptr<U, E>&& u);[..]
22 - Postconditions: get() == value u.get() had before the construction, modulo any required offset adjustments resulting from the cast from unique_ptr<U, E>::pointer to pointer and u.get() == nullptr. get_deleter() returns a reference to the internally stored deleter which was constructed from u.get_deleter().
Change 20.8.14.2.1 [unique.ptr.single.ctor]/20 as indicated: [With the possibility of user-defined pointer-like types the implication does only exist, if those are built-in pointers. Note that this change should also be applied with the acceptance of 950]
template <class U, class E> unique_ptr(unique_ptr<U, E>&& u);20 - Requires: If D is not a reference type, construction of the deleter D from an rvalue of type E shall be well formed and shall not throw an exception. If D is a reference type, then E shall be the same type as D (diagnostic required). unique_ptr<U, E>::pointer shall be implicitly convertible to pointer.[Note: These requirements imply that T and U are complete types. — end note]
Change 20.8.14.2.2 [unique.ptr.single.dtor]/2 as indicated:
~unique_ptr();...
2 - Effects: If get() ==
0nullptr there are no effects. Otherwise get_deleter()(get()).
Change 20.8.14.2.3 [unique.ptr.single.asgn]/3+8 as indicated: [The intent is to clarify that the moved-from source must contain a null pointer, there is no other choice left]
unique_ptr& operator=(unique_ptr&& u);[..]
3 - Postconditions: This unique_ptr now owns the pointer which u owned, and u no longer owns it, u.get() == nullptr. [Note: If D is a reference type, then the referenced lvalue deleters are move assigned. — end note]
template <class U, class E> unique_ptr& operator=(unique_ptr<U, E>&& u);[..]
8 - Postconditions: This unique_ptr now owns the pointer which u owned, and u no longer owns it, u.get() == nullptr.
Change 20.8.14.2.3 [unique.ptr.single.asgn]/6 as indicated: [With the possibility of user-defined pointer-like types the implication does only exist, if those are built-in pointers. Note that this change should also be applied with the acceptance of 950]
template <class U, class E> unique_ptr& operator=(unique_ptr<U, E>&& u);[..]
6 - Requires: Assignment of the deleter D from an rvalue D shall not throw an exception. unique_ptr<U, E>::pointer shall be implicitly convertible to pointer.
[Note: These requirements imply that T and U are complete types. — end note]
Change 20.8.14.2.3 [unique.ptr.single.asgn] before p. 11 and p. 12 as indicated: [The first change is a simple typo fix]
unique_ptr& operator=(nullptr_t});11 - Effects: reset().
12 - Postcondition: get() ==
0nullptr
Change 20.8.14.2.4 [unique.ptr.single.observers]/1+4+12 as indicated:
typename add_lvalue_reference<T>::type operator*() const;1 - Requires: get() !=
0nullptr. The variable definition add_lvalue_reference<T>::type t = *get() shall be well formed, shall have well-defined behavior, and shall not exit via an exception.[..]
pointer operator->() const;4 - Requires: get() !=
0nullptr.[..]
explicit operator bool() const;12 - Returns: get() !=0nullptr.
Change 20.8.14.2.5 [unique.ptr.single.modifiers]/1 as indicated:
pointer release();1 - Postcondition: get() ==0nullptr.
Change 20.8.14.2.5 [unique.ptr.single.modifiers]/9 as indicated: [The intent is to ensure that potentially user-defined swaps are used. A side-step fix and harmonization with the specification of the the deleter is realized. Please note the additional requirement in bullet 2 of this proposed resolution regarding the availability of the generic swap templates within the member swap function.]
void swap(unique_ptr& u);8 - Requires: The deleter D shall be Swappable and shall not throw an exception under swap.
9 - Effects: The stored pointers of *this and u are exchanged by an unqualified call to non-member swap. The stored deleters are
swap'd (unqualified)exchanged by an unqualified call to non-member swap.
Change 20.8.14.3.2 [unique.ptr.runtime.observers]/1 as indicated:
T& operator[](size_t i) const;Requires: i < the size of the array to which the stored pointer points. The variable definition T& t = get()[i] shall be well formed, shall have well-defined behavior, and shall not exit via an exception.
Change 20.8.14.3.3 [unique.ptr.runtime.modifiers]/1 as indicated:
void reset(pointer p = pointer()); void reset(nullptr_t p);1 - Effects: If get() ==0nullptr there are no effects. Otherwise get_deleter()(get()).
Change 20.8.14.4 [unique.ptr.special] as indicated: [We don't add the relational operators to the basic requirement set, therefore we need special handling here]
template <class T1, class D1, class T2, class D2> bool operator==(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition bool b = x.get() == y.get(); shall be well formed, shall have well-defined behavior, and shall not exit via an exception.
2 - Returns: x.get() == y.get().
Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition bool b = x.get() != y.get(); shall be well formed, shall have well-defined behavior, and shall not exit via an exception.
3 - Returns: x.get() != y.get().
Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator<(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition bool b = x.get() < y.get(); shall be well formed, shall have well-defined behavior, and shall not exit via an exception.
4 - Returns: x.get() < y.get().
Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator<=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition bool b = x.get() <= y.get(); shall be well formed, shall have well-defined behavior, and shall not exit via an exception.
5 - Returns: x.get() <= y.get().
Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator>(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition bool b = x.get() > y.get(); shall be well formed, shall have well-defined behavior, and shall not exit via an exception.
6 - Returns: x.get() > y.get().
Throws: nothing.
template <class T1, class D1, class T2, class D2> bool operator>=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);Requires: The variable definition bool b = x.get() >= y.get(); shall be well formed, shall have well-defined behavior, and shall not exit via an exception.
7 - Returns: x.get() >= y.get().
Throws: nothing.
Section: 27.5.4.2 [basic.ios.members] Status: Open Submitter: Martin Sebor Opened: 2008-05-17 Last modified: 2009-10-20
View other active issues in [basic.ios.members].
View all other issues in [basic.ios.members].
View all issues with Open status.
Discussion:
The fix for issue 581, now integrated into the working paper, overlooks a couple of minor problems.
First, being an unformatted function once again, flush()
is required to create a sentry object whose constructor must, among
other things, flush the tied stream. When two streams are tied
together, either directly or through another intermediate stream
object, flushing one will also cause a call to flush() on
the other tied stream(s) and vice versa, ad infinitum. The program
below demonstrates the problem.
Second, as Bo Persson notes in his
comp.lang.c++.moderated post,
for streams with the unitbuf flag set such
as std::stderr, the destructor of the sentry object will
again call flush(). This seems to create an infinite
recursion for std::cerr << std::flush;
#include <iostream>
int main ()
{
std::cout.tie (&std::cerr);
std::cerr.tie (&std::cout);
std::cout << "cout\n";
std::cerr << "cerr\n";
}
[ Batavia (2009-05): ]
We agree with the proposed resolution. Move to Review.
[ 2009-05-26 Daniel adds: ]
I think that the most recently suggested change in 27.7.2.4 [ostream::sentry] need some further word-smithing. As written, it would make the behavior undefined, if under conditions when pubsync() should be called, but when in this scenario os.rdbuf() returns 0.
This case is explicitly handled in flush() and needs to be taken care of. My suggested fix is:
If ((os.flags() & ios_base::unitbuf) && !uncaught_exception() && os.rdbuf() != 0) is true, callsos.flush()os.rdbuf()->pubsync().Two secondary questions are:
- Should pubsync() be invoked in any case or shouldn't a base requirement for this trial be that os.good() == true as required in the original flush() case?
- Since uncaught_exception() is explicitly tested, shouldn't a return value of -1 of pubsync() produce setstate(badbit) (which may throw ios_base::failure)?
[ 2009-07 Frankfurt: ]
Daniel volunteered to modify the proposed resolution to address his two questions.
Move back to Open.
[ 2009-07-26 Daniel provided wording. Moved to Review. ]
[ 2009-10-13 Daniel adds: ]
This proposed wording is written to match the outcome of 397.
[ 2009 Santa Cruz: ]
Move to Open. Martin to propose updated wording that will also resolve issue 397 consistently.
Proposed resolution:
[ based on N2960 numbering ]
Just before 27.5.4.2 [basic.ios.members]/2 insert a new paragraph:
Requires: If (tiestr != 0) is true, tiestr must not be reachable by traversing the linked list of tied stream objects starting from tiestr->tie().
Change 27.7.2.4 [ostream::sentry]/4 as indicated:
If ((os.flags() & ios_base::unitbuf) && !uncaught_exception()&& os.good()) is true, callsos.flush()os.rdbuf()->pubsync(). If that function returns -1 sets badbit in os.rdstate() without propagating an exception.
money_base::space and
money_base::none on money_get
Section: 22.4.6.1.2 [locale.money.get.virtuals] Status: Ready Submitter: Martin Sebor Opened: 2008-05-17 Last modified: 2009-10-21
View all other issues in [locale.money.get.virtuals].
View all issues with Ready status.
Duplicate of: 670
Discussion:
In paragraph 2, 22.4.6.1.2 [locale.money.get.virtuals] specifies the following:
Wherespaceornoneappears in the format pattern, except at the end, optional white space (as recognized byct.is) is consumed after any required space.
This requirement can be (and has been) interpreted two mutually exclusive ways by different readers. One possible interpretation is that:
- where
money_base::spaceappears in the format, at least one space is required, and- where
money_base::noneappears in the format, space is allowed but not required.
The other is that:
where eithermoney_base::spaceormoney_base::noneappears in the format, white space is optional.
[ San Francisco: ]
Martin will revise the proposed resolution.
[ 2009-07 Frankfurt: ]
There is a noun missing from the proposed resolution. It's not clear that the last sentence would be helpful, even if the word were not missing:
In either case, any required MISSINGWORD followed by all optional whitespace (as recognized by ct.is()) is consumed.Strike this sentence and move to Review.
[ Howard: done. ]
[ 2009-10 Santa Cruz: ]
Move to Ready.
Proposed resolution:
I propose to change the text to make it clear that the first interpretation is intended, that is, to make following change to 22.4.6.1.2 [locale.money.get.virtuals], p2:
Whenmoney_base::spaceormoney_base::noneappears as the last element in the format pattern,except at the end, optional white space (as recognized byno white space is consumed. Otherwise, wherect.is) is consumed after any required space.money_base::spaceappears in any of the initial elements of the format pattern, at least one white space character is required. Wheremoney_base::noneappears in any of the initial elements of the format pattern, white space is allowed but not required. If(str.flags() & str.showbase)isfalse, ...
Section: 20.8.14.1.1 [unique.ptr.dltr.dflt] Status: Ready Submitter: Howard Hinnant Opened: 2008-06-18 Last modified: 2009-10-21
View all issues with Ready status.
Discussion:
No relationship between U and T in the converting constructor for default_delete template.
Requirements: U* is convertible to T* and has_virtual_destructor<T>; the latter should also become a concept.
Rules out cross-casting.
The requirements for unique_ptr conversions should be the same as those on the deleter.
[ Howard adds 2008-11-26: ]
I believe we need to be careful to not outlaw the following use case, and I believe the current proposed wording (requires Convertible<U*, T*> && HasVirtualDestructor<T>) does so:
#include <memory> int main() { std::unique_ptr<int> p1(new int(1)); std::unique_ptr<const int> p2(move(p1)); int i = *p2; // *p2 = i; // should not compile }I've removed "&& HasVirtualDestructor<T>" from the requires clause in the proposed wording.
[ Post Summit: ]
Alisdair: This issue has to stay in review pending a paper constraining unique_ptr.
Consensus: We agree with the resolution, but unique_ptr needs to be constrained, too.
Recommend Keep in Review.
[ Batavia (2009-05): ]
Keep in Review status for the reasons cited.
[ 2009-07 Frankfurt: ]
The proposed resolution uses concepts. Howard needs to rewrite the proposed resolution.
Move back to Open.
[ 2009-07-26 Howard provided rewritten proposed wording and moved to Review. ]
[ 2009-10 Santa Cruz: ]
Move to Ready.
Proposed resolution:
Add after 20.8.14.1.1 [unique.ptr.dltr.dflt], p1:
template <class U> default_delete(const default_delete<U>& other);-1- Effects: ...
Remarks: This constructor shall participate in overload resolution if and only if U* is implicitly convertible to T*.
Section: 26 [numerics] Status: Ready Submitter: Lawrence Crowl Opened: 2008-06-23 Last modified: 2009-10-23
View all other issues in [numerics].
View all issues with Ready status.
Discussion:
There are a number of functions that affect the floating point state. These function need to be thread-safe, but I'm unsure of the right approach in the standard, as we inherit them from C.
[ San Francisco: ]
Nick: I think we already say that these functions do not introduce data races; see 17.6.5.6/20
Pete: there's more to it than not introducing data races; are these states maintained per thread?
Howard: 21.5/14 says that strtok and strerror are not required to avoid data races, and 20.9/2 says the same about asctime, gmtime, ctime, and gmtime.
Nick: POSIX has a list of not-safe functions. All other functions are implicitly thread safe.
Lawrence is to form a group between meetings to attack this issue. Nick and Tom volunteered to work with Lawrence.
Move to Open.
[ Post Summit: ]
Hans: Sane oses seem ok. Sensible thing is implementable and makes sense.
Nick: Default wording seems to cover this? Hole in POSIX, these functions need to be added to list of thread-unsafe functions.
Lawrence: Not sufficient, not "thread-safe" per our definition, but think of state as a thread-local variable. Need something like "these functions only affect state in the current thread."
Hans: Suggest the following wording: "The floating point environment is maintained per-thread."
Walter: Any other examples of state being thread safe that are not already covered elsewhere?
Have thread unsafe functions paper which needs to be updated. Should just fold in 26.3 [cfenv] functions.
Recommend Open. Lawrence instead suggests leaving it open until we have suitable wording that may or may not include the thread local commentary.
[ 2009-09-23 Hans provided wording. ]
If I understand the history correctly, Nick, as the Posix liaison, should probably get a veto on this, since I think it came from Posix (?) via WG14 and should probably really be addressed there (?). But I think we are basically in agreement that there is no other sane way to do this, and hence we don't have to worry too much about stepping on toes. As far as I can tell, this same issue also exists in the latest Posix standard (?).
[ 2009-10 Santa Cruz: ]
Moved to Ready.
Proposed resolution:
Add at the end of 26.3.1 [cfenv.syn]:
2 The header defines all functions, types, and macros the same as C99 7.6.
A separate floating point environment shall be maintained for each thread. Each function accesses the environment corresponding to its calling thread.
Section: 23.2 [container.requirements] Status: Open Submitter: Daniel Krügler Opened: 2008-06-24 Last modified: 2010-01-30
View other active issues in [container.requirements].
View all other issues in [container.requirements].
View all issues with Open status.
Discussion:
Table 89, Container requirements, defines operator== in terms of the container member function size() and the algorithm std::equal:
== is an equivalence relation. a.size() == b.size() && equal(a.begin(), a.end(), b.begin()
The new container forward_list does not provide a size member function by design but does provide operator== and operator!= without specifying it's semantic.
Other parts of the (sequence) container requirements do also depend on size(), e.g. empty() or clear(), but this issue explicitly attempts to solve the missing EqualityComparable specification, because of the special design choices of forward_list.
I propose to apply one of the following resolutions, which are described as:
Both proposal choices are discussed, the preferred choice of the author is to apply (A).
[ San Francisco: ]
There's an Option C: change the requirements table to use distance().
LWG found Option C acceptable.
Martin will draft the wording for Option C.
[ post San Francisco: ]
Martin provided wording for Option C.
[ 2009-07 Frankfurt ]
Other operational semantics (see, for example, Tables 82 and 83) are written in terms of a container's size() member. Daniel to update proposed resolution C.
[ Howard: Commented out options A and B. ]
[ 2009-07-26 Daniel updated proposed resolution C. ]
[ 2009-10 Santa Cruz: ]
Mark NAD Editorial. Addressed by N2986.
[ 2009-10 Santa Cruz: ]
Reopened. N2986 was rejected in full committee on procedural grounds.
[ 2010-01-30 Howard updated Table numbers. ]
Proposed resolution:
Option (C):
In 23.2.1 [container.requirements.general] change Table 90 -- Container requirements as indicated:
Change the text in the Assertion/note column in the row for "X u;" as follows:
post: u.size() == 0empty() == trueChange the text in the Assertion/note column in the row for "X();" as follows:
X().size() == 0empty() == trueChange the text in the Operational Semantics column in the row for "a == b" as follows:
== is an equivalence relation.a.size()distance(a.begin(), a.end()) ==b.size()distance(b.begin(), b.end()) && equal(a.begin(), a.end(), b.begin())Add text in the Ass./Note/pre-/post-condition column in the row for "a == b" as follows:
Requires: T is EqualityComparableChange the text in the Operational Semantics column in the row for "a.size()" as follows:
a.end() - a.begin()distance(a.begin(), a.end())Change the text in the Operational Semantics column in the row for "a.max_size()" as follows:
size()distance(begin(), end()) of the largest possible containerChange the text in the Operational Semantics column in the row for "a.empty()" as follows:
a.size() == 0a.begin() == a.end()In 23.2.1 [container.requirements.general] change Table 93 -- Allocator-aware container requirements as indicated:
Change the text in the Assertion/note column in the row for "X() / X u;" as follows:
Requires: A is DefaultConstructible post:u.size() == 0u.empty() == true, get_allocator() == A()Change the text in the Assertion/note column in the row for "X(m) / X u(m);" as follows:
post:u.size() == 0u.empty() == true, get_allocator() == mIn 23.2.3 [sequence.reqmts] change Table 94 -- Sequence container requirements as indicated:
Change the text in the Assertion/note column in the row for "X(n, t) / X a(n, t)" as follows:
post:size()distance(begin(), end()) == n [..]Change the text in the Assertion/note column in the row for "X(i, j) / X a(i, j)" as follows:
[..] post:size() == distance between i and jdistance(begin(), end()) == distance(i, j) [..]Change the text in the Assertion/note column in the row for "a.clear()" as follows:
a.erase(a.begin(), a.end()) post:size() == 0a.empty() == trueIn 23.2.4 [associative.reqmts] change Table 96 -- Associative container requirements as indicated:
[ Not every occurrence of size() was replaced, because all current associative containers have a size. The following changes ensure consistency regarding the semantics of "erase" for all tables and adds some missing objects ]
Change the text in the Complexity column in the row for X(i,j,c)/X a(i,j,c); as follows:
N log N in general (N == distance(i, j)is the distance from i to j); ...Change the text in the Complexity column in the row for "a.insert(i, j)" as follows:
N log(a.size() + N)(N is the distance from i to j)where N == distance(i, j)Change the text in the Complexity column in the row for "a.erase(k)" as follows:
log(a.size()) + a.count(k)Change the text in the Complexity column in the row for "a.erase(q1, q2)" as follows:
log(a.size()) + N where Nis the distance from q1 to q2== distance(q1, q2).Change the text in the Assertion/note column in the row for "a.clear()" as follows:
a.erase(a.begin(),a.end()) post:size() == 0a.empty() == trueChange the text in the Complexity column in the row for "a.clear()" as follows:
linear in a.size()Change the text in the Complexity column in the row for "a.count(k)" as follows:
log(a.size()) + a.count(k)In 23.2.5 [unord.req] change Table 98 -- Unordered associative container requirements as indicated:
[ The same rational as for Table 96 applies here ]
Change the text in the Assertion/note column in the row for "a.clear()" as follows:
[..] Post: a.size() == 0empty() == true
Section: 25.3.6 [alg.fill], 25.3.7 [alg.generate] Status: Ready Submitter: Daniel Krügler Opened: 2008-07-13 Last modified: 2009-10-23
View all issues with Ready status.
Discussion:
In regard to library defect 488 I found some more algorithms which unnecessarily throw away information. These are typically algorithms, which sequentially write into an OutputIterator, but do not return the final value of this output iterator. These cases are:
template<class OutputIterator, class Size, class T> void fill_n(OutputIterator first, Size n, const T& value);
template<class OutputIterator, class Size, class Generator> void generate_n(OutputIterator first, Size n, Generator gen);
In both cases the minimum requirements on the iterator are OutputIterator, which means according to the requirements of 24.2.2 [output.iterators]/2 that only single-pass iterations are guaranteed. So, if users of fill_n and generate_n have *only* an OutputIterator available, they have no chance to continue pushing further values into it, which seems to be a severe limitation to me.
[ Post Summit Daniel "conceptualized" the wording. ]
[ Batavia (2009-05): ]
Alisdair likes the idea, but has concerns about the specific wording about the returns clauses.
Alan notes this is a feature request.
Bill notes we have made similar changes to other algorithms.
Move to Open.
[ 2009-07 Frankfurt ]
We have a consensus for moving forward on this issue, but Daniel needs to deconceptify it.
[ 2009-07-25 Daniel provided non-concepts wording. ]
[ 2009-10 Santa Cruz: ]
Moved to Ready.
Proposed resolution:
Replace the current declaration of fill_n in 25 [algorithms]/2, header <algorithm> synopsis and in 25.3.6 [alg.fill] by
template<class OutputIterator, class Size, class T>voidOutputIterator fill_n(OutputIterator first, Size n, const T& value);
Just after the effects clause add a new returns clause saying:
Returns: For fill_n and positive n, returns first + n. Otherwise returns first for fill_n.
Replace the current declaration of generate_n in 25 [algorithms]/2, header <algorithm> synopsis and in 25.3.7 [alg.generate] by
template<class OutputIterator, class Size, class Generator>voidOutputIterator generate_n(OutputIterator first, Size n, Generator gen);
Just after the effects clause add a new returns clause saying:
For generate_n and positive n, returns first + n. Otherwise returns first for generate_n.
Section: 23 [containers] Status: Ready Submitter: Alberto Ganesh Barbati Opened: 2008-07-22 Last modified: 2009-10-20
View other active issues in [containers].
View all other issues in [containers].
View all issues with Ready status.
Discussion:
The term "default constructed" is often used in wording that predates the introduction of the concept of value-initialization. In a few such places the concept of value-initialization is more correct than the current wording (for example when the type involved can be a built-in) so a replacement is in order. Two of such places are already covered by issue 867. This issue deliberately addresses the hopefully non-controversial changes in the attempt of being approved more quickly. A few other occurrences (for example in std::tuple, std::reverse_iterator and std::move_iterator) are left to separate issues. For std::reverse_iterator, see also issue 408. This issue is related with issue 724.
[ San Francisco: ]
The list provided in the proposed resolution is not complete. James Dennett will review the library and provide a complete list and will double-check the vocabulary.
This issue relates to Issue 886 tuple construction
[ 2009-07 Frankfurt ]
The proposed resolution is incomplete.
Move to Tentatively NAD Future. Howard will contact Ganesh for wording. If wording is forthcoming, Howard will move it back to Review.
[ 2009-07-18 Ganesh updated the proposed wording. ]
Howard: Moved back to Review. Note that 20.2.1 [utility.arg.requirements] refers to a section that is not in the current working paper, but does refer to a section that we expect to reappear after the de-concepts merge. This was a point of confusion we did not recognize when we reviewed this issue in Frankfurt.
Howard: Ganesh also includes a survey of places in the WP surveyed for changes of this nature and purposefully not treated:
Places where changes are not being proposed
In the following paragraphs, we are not proposing changes because it's not clear whether we actually prefer value-initialization over default-initialization (now partially covered by 1012):
20.8.14.2.1 [unique.ptr.single.ctor] para 3 e 7
24.5.1.3.1 [reverse.iter.cons] para 1
24.5.3.3.1 [move.iter.op.const] para 1
In the following paragraphs, the expression "default constructed" need not be changed, because the relevant type does not depend on a template parameter and has a user-provided constructor:
[func.referenceclosure.invoke] para 12, type: reference_closure
30.3.1.2 [thread.thread.constr] para 30, type: thread
30.3.1.5 [thread.thread.member] para 52, type: thread_id
30.3.2 [thread.thread.this], para 1, type: thread_id
[ 2009-08-18 Daniel adds: ]
I have no objections against the currently suggested changes, but I also cross-checked with the list regarding intentionally excluded changes, and from this I miss the discussion of
21.4.1 [string.require]/2:
"[..] The Allocator object used shall be a copy of the Allocator> object passed to the basic_string object's constructor or, if the constructor does not take an Allocator argument, a copy of a default-constructed Allocator object."N2723, X [rand.req.eng], Table 109, expression "T()":
Pre-/post-condition: "Creates an engine with the same initial state as all other default-constructed engines of type X."as well as in 26.5.5 [rand.predef]/1-9 (N2914), 26.5.7.1 [rand.util.seedseq]/3, 27.7.1.1.1 [istream.cons]/3, 27.7.2.2 [ostream.cons]/9 (N2914), 28.13 [re.grammar]/2, 30.3.1.4 [thread.thread.assign]/1 (N2914),
[ Candidates for the "the expression "default constructed" need not be changed" list ]
I'm fine, if these would be added to the intentionally exclusion list, but mentioning them makes it easier for other potential reviewers to decide on the relevance or not-relevance of them for this issue.
I suggest to remove the reference of [func.referenceclosure.invoke] in the "it's not clear" list, because this component does no longer exist.
I also suggest to add a short comment that all paragraphs in the resolution whether they refer to N2723 or to N2914 numbering, because e.g. "Change 23.3.2.1 [deque.cons] para 5" is an N2723 coordinate, while "Change 23.3.2.2 [deque.capacity] para 1" is an N2914 coordinate. Even better would be to use one default document for the numbering (probably N2914) and mention special cases (e.g. "Change 20.2.1 [utility.arg.requirements] para 2" as referring to N2723 numbering).
[ 2009-08-18 Alisdair adds: ]
I strongly believe the term "default constructed" should not appear in the library clauses unless we very clearly define a meaning for it, and I am not sure what that would be.
In those cases where we do not want to replace "default constructed" with "vale initialized" we should be using "default initialized". If we have a term that could mean either, we reduce portability of programs.
I have not done an exhaustive review to clarify if that is a vendor freedom we have reason to support (e.g. value-init in debug, default-init in release) so I may yet be convinced that LWG has reason to define this new term of art, but generally C++ initialization is confusing enough without supporting further ill-defined terms.
[ 2009-10 Santa Cruz: ]
Move to Ready.
Proposed resolution:
Change 20.2.1 [utility.arg.requirements] para 2:
In general, a default constructor is not required. Certain container class member function signatures specifythe default constructorT() as a default argument. T() shall be a well-defined expression (8.5) if one of those signatures is called using the default argument (8.3.6).
Change 23.3.2.1 [deque.cons] para 5:
Effects: Constructs a deque with ndefault constructedvalue-initialized elements.
Change 23.3.2.2 [deque.capacity] para 1:
Effects: If sz < size(), equivalent to erase(begin() + sz, end());. If size() < sz, appends sz - size()default constructedvalue-initialized elements to the sequence.
Change 23.3.3.1 [forwardlist.cons] para 5:
Effects: Constructs a forward_list object with ndefault constructedvalue-initialized elements.
Change 23.3.3.4 [forwardlist.modifiers] para 21:
Effects: [...] For the first signature the inserted elements aredefault constructedvalue-initialized, and for the second signature they are copies of c.
Change 23.3.4.1 [list.cons] para 5:
Effects: Constructs a list with ndefault constructedvalue-initialized elements.
Change 23.3.4.2 [list.capacity] para 15:
Effects: If sz < size(), equivalent to list<T>::iterator it = begin(); advance(it, sz); erase(it, end());. If size() < sz, appends sz - size()default constructedvalue-initialized elements to the sequence.
Change 23.3.6.1 [vector.cons] para 3:
Effects: Constructs a vector with ndefault constructedvalue-initialized elements.
Change 23.3.6.2 [vector.capacity] para 24:
Effects: If sz < size(), equivalent to erase(begin() + sz, end());. If size() < sz, appends sz - size()default constructedvalue-initialized elements to the sequence.
Section: 23.2.5 [unord.req] Status: Tentatively Ready Submitter: Daniel Krügler Opened: 2008-08-17 Last modified: 2010-02-11
View other active issues in [unord.req].
View all other issues in [unord.req].
View all issues with Tentatively Ready status.
Discussion:
Good ol' associative containers allow both function pointers and function objects as feasible comparators, as described in 23.2.4 [associative.reqmts]/2:
Each associative container is parameterized on Key and an ordering relation Compare that induces a strict weak ordering (25.3) on elements of Key. [..]. The object of type Compare is called the comparison object of a container. This comparison object may be a pointer to function or an object of a type with an appropriate function call operator.[..]
The corresponding wording for unordered containers is not so clear, but I read it to disallow function pointers for the hasher and I miss a clear statement for the equality predicate, see 23.2.5 [unord.req]/3+4+5:
Each unordered associative container is parameterized by Key, by a function object Hash that acts as a hash function for values of type Key, and by a binary predicate Pred that induces an equivalence relation on values of type Key.[..]
A hash function is a function object that takes a single argument of type Key and returns a value of type std::size_t.
Two values k1 and k2 of type Key are considered equal if the container's equality function object returns true when passed those values.[..]
and table 97 says in the column "assertion...post-condition" for the expression X::hasher:
Hash shall be a unary function object type such that the expression hf(k) has type std::size_t.
Note that 20.7 [function.objects]/1 defines as "Function objects are objects with an operator() defined.[..]"
Does this restriction exist by design or is it an oversight? If an oversight, I suggest that to apply the following
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-10 Santa Cruz: ]
Ask Daniel to provide proposed wording that: makes it explicit that function pointers are function objects at the beginning of 20.7 [function.objects]; fixes the "requirements" for typedefs in 20.7.5 [refwrap] to instead state that the function objects defined in that clause have these typedefs, but not that these typedefs are requirements on function objects; remove the wording that explicitly calls out that associative container comparators may be function pointers.
[ 2009-12-19 Daniel updates wording and rationale. ]
[ 2010-02-11 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]
Rationale:
The below provided wording also affects some part of the library which is involved with callable types (20.7.1 [func.def]/3). Reason for this is that callable objects do have a lot in common with function objects. A simple formula seems to be:
callable objects = function objects + pointers to member
The latter group is excluded from function objects because of the expression-based usage of function objects in the algorithm clause, which is incompatible with the notation to dereference pointers to member without a concept map available in the language.
This analysis showed some currently existing normative definition differences between the above subset of callable objects and function objects which seem to be unintended: Backed by the Santa Cruz outcome function objects should include both function pointers and "object[s] with an operator() defined". This clearly excludes class types with a conversion function to a function pointer or all similar conversion function situations described in 13.3 [over.match]/2 b. 2. In contrast to this, the wording for callable types seems to be less constrained (20.7.1 [func.def]/3):
A callable type is a [..] class type whose objects can appear immediately to the left of a function call operator.
The rationale given in N1673 and a recent private communication with Peter Dimov revealed that the intention of this wording was to cover the above mentioned class types with conversion functions as well. To me the current wording of callable types can be read either way and I suggest to make the intention more explicit by replacing
[..] class type whose objects can appear immediately to the left of a function call operatorby
[..] class type whose objects can appear as the leftmost subexpression of a function call expression 5.2.2 [expr.call].
and to use the same definition for the class type part of function objects, because there is no reason to exclude class types with a conversion function to e.g. pointer to function from being used in algorithms.
Now this last term "function objects" itself brings us to a third unsatisfactory state: The term is used both for objects (e.g. "Function objects are objects[..]" in 20.7 [function.objects]/1) and for types (e.g. "Each unordered associative container is parameterized [..] by a function object Hash that acts as a hash function [..]" in 23.2.5 [unord.req]/3). This impreciseness should be fixed and I suggest to introduce the term function object type as the counter part to callable type. This word seems to be a quite natural choice, because the library already uses it here and there (e.g. "Hash shall be a unary function object type such that the expression hf(k) has type std::size_t." in Table 98, "X::hasher" or "Requires: T shall be a function object type [..]" in 20.7.15.2.5 [func.wrap.func.targ]/3).
Finally I would like to add that part of the issue 870 discussion related to the requirements for typedefs in 20.7.5 [refwrap] during the Santa Cruz meeting is now handled by the new issue 1290.
Obsolete rationale:
[ San Francisco: ]
This is fixed by N2776.
Proposed resolution:
Change 20.7 [function.objects]/1 as indicated:
1
Function objects are objects with an operator() defined.An object type (3.9 [basic.types]) that can be the type of the postfix-expression in a function call (5.2.2 [expr.call], 13.3.1.1 [over.match.call]) is called a function object type*. A function object is an object of a function object type. In the places where one would expect to pass a pointer to a function to an algorithmic template (Clause 25 [algorithms]), the interface is specified to acceptan object with an operator() defineda function object. This not only makes algorithmic templates work with pointers to functions, but also enables them to work with arbitrary function objects.* Such a type is either a function pointer or a class type which often has a member operator(), but in some cases it can omit that member and provide a conversion to a pointer to function.
Change 20.7.1 [func.def]/3 as indicated: [The intent is to make the commonality of callable types and function object types more explicit and to get rid of wording redundancies]
3 A callable type isa pointer to function,a pointer to memberfunction, a pointer to member data,or aclass type whose objects can appear immediately to the left of a function call operatorfunction object type (20.7 [function.objects]).
Change 20.7.11 [bind]/1 as indicated:
1 The function template bind returns an object that binds afunctioncallable object passed as an argument to additional arguments.
Change 20.7.11.1 [func.bind]/1 as indicated:
1 This subclause describes a uniform mechanism for binding arguments offunctioncallable objects.
Change 20.7.15 [func.wrap]/1 as indicated:
1 This subclause describes a polymorphic wrapper class that encapsulates arbitraryfunctioncallable objects.
Change 20.7.15.2 [func.wrap.func]/2 as indicated [The reason for this change is that 20.7.15.2 [func.wrap.func]/1 clearly says that all callable types may be wrapped by std::function and current implementations indeed do provide support for pointer to members as well. One further suggested improvement is to set the below definition of Callable in italics]:
2 Afunctioncallable object f of type F isCallableCallable for argument typesT1, T2, ..., TN inArgTypes andareturn type R,if, given lvalues t1, t2, ..., tN of types T1, T2, ..., TN, respectively,the expression INVOKE(f, declval<ArgTypes>()..., Rt1, t2, ..., tN), considered as an unevaluated operand (5 [expr]), is well formed (20.7.2)and, if R is not void, convertible to R.
Change 20.7.15.2.1 [func.wrap.func.con]/7 as indicated:
function(const function& f); template <class A> function(allocator_arg_t, const A& a, const function& f);...
7 Throws: shall not throw exceptions if f's target is a function pointer or a
functioncallable object passed via reference_wrapper. Otherwise, may throw bad_alloc or any exception thrown by the copy constructor of the storedfunctioncallable object. [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for smallfunctioncallable objects, e.g., where f's target is an object holding only a pointer or reference to an object and a member function pointer. — end note]
Change 20.7.15.2.1 [func.wrap.func.con]/11 as indicated:
template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);...
11 [..] [Note: implementations are encouraged to avoid the use of dynamically allocated memory for small
functioncallable 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]
Change 20.7.15.2.4 [func.wrap.func.inv]/3 as indicated:
R operator()(ArgTypes... args) const...
3 Throws: bad_function_call if !*this; otherwise, any exception thrown by the wrapped
functioncallable object.
Change 20.7.15.2.5 [func.wrap.func.targ]/3 as indicated:
template<typename T> T* target(); template<typename T> const T* target() const;...
3 Requires: T shall be a
function objecttype that is Callable (20.7.15.2 [func.wrap.func]) for parameter types ArgTypes and return type R.
Change 23.2.4 [associative.reqmts]/2 as indicated: [The suggested removal seems harmless, because 25.4 [alg.sorting]1 already clarifies that Compare is a function object type. Nevertheless it is recommended, because the explicit naming of "pointer to function" is misleading]
2 Each associative container is parameterized on Key and an ordering relation Compare that induces a strict weak ordering (25.4 [alg.sorting]) on elements of Key. In addition, map and multimap associate an arbitrary type T with the Key. The object of type Compare is called the comparison object of a container.This comparison object may be a pointer to function or an object of a type with an appropriate function call operator.
Change 23.2.5 [unord.req]/3 as indicated:
3 Each unordered associative container is parameterized by Key, by a function object type Hash that acts as a hash function for values of type Key, and by a binary predicate Pred that induces an equivalence relation on values of type Key. [..]
Change 25.1 [algorithms.general]/7 as indicated: [The intent is to bring this part in sync with 20.7 [function.objects]]
7 The Predicate parameter is used whenever an algorithm expects a function object (20.7 [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 if (pred(*first)){...}. The function object pred shall not apply any nonconstant function through the dereferenced iterator.This function object may be a pointer to function, or an object of a type with an appropriate function call operator.
Change 20.8.14.2 [unique.ptr.single]/1 as indicated:
1 The default type for the template parameter D is default_delete. A client-supplied template argument D shall be a functionpointer or functorobject type for which, given a value d of type D and a pointer ptr of type T*, the expression d(ptr) is valid and has the effect of deallocating the pointer as appropriate for that deleter. D may also be an lvalue-reference to a deleter.
Section: 26.7.5 [numeric.iota] Status: Ready Submitter: Daniel Krügler Opened: 2008-08-20 Last modified: 2009-10-22
View all issues with Ready status.
Discussion:
According to the recent WP N2691, 26.7.5 [numeric.iota]/1, the requires clause of std::iota says:
T shall meet the requirements of CopyConstructible and Assignable types, and shall be convertible to ForwardIterator's value type.[..]
Neither CopyConstructible nor Assignable is needed, instead MoveConstructible seems to be the correct choice. I guess the current wording resulted as an artifact from comparing it with similar numerical algorithms like accumulate.
Note: If this function will be conceptualized, the here proposed MoveConstructible requirement can be removed, because this is an implied requirement of function arguments, see N2710/[temp.req.impl]/3, last bullet.
[ post San Francisco: ]
Issue pulled by author prior to review.
[ 2009-07-30 Daniel reopened: ]
with the absence of concepts, this issue (closed) is valid again and I suggest to reopen it. I also revised by proposed resolution based on N2723 wording:
[ 2009-10 Santa Cruz: ]
Change 'convertible' to 'assignable', Move To Ready.
Proposed resolution:
Change the first sentence of 26.7.5 [numeric.iota]/1:
Requires: T shallmeet the requirements of CopyConstructible and Assignable types, and shallbe assignable to ForwardIterator's value type. [..]
Section: 24.5.3.3.12 [move.iter.op.index] Status: Ready Submitter: Doug Gregor Opened: 2008-08-21 Last modified: 2009-10-23
View all issues with Ready status.
Discussion:
move_iterator's operator[] is declared as:
reference operator[](difference_type n) const;
This has the same problem that reverse_iterator's operator[] used to have: if the underlying iterator's operator[] returns a proxy, the implicit conversion to value_type&& could end up referencing a temporary that has already been destroyed. This is essentially the same issue that we dealt with for reverse_iterator in DR 386.
[ 2009-07-28 Reopened by Alisdair. No longer solved by concepts. ]
[ 2009-08-15 Howard adds: ]
I recommend closing this as a duplicate of 1051 which addresses this issue for both move_iterator and reverse_iterator.
[ 2009-10 Santa Cruz: ]
Move to Ready. Note that if 1051 is reopened, it may yield a better resolution, but 1051 is currently marked NAD.
Proposed resolution:
In 24.5.3.1 [move.iterator] and 24.5.3.3.12 [move.iter.op.index], change the declaration of move_iterator's operator[] to:
referenceunspecified operator[](difference_type n) const;
Rationale:
[ San Francisco: ]
NAD Editorial, see N2777.
Section: 20.3.4 [pairs] Status: Open Submitter: Alisdair Meredith Opened: 2008-09-15 Last modified: 2009-07-17
View other active issues in [pairs].
View all other issues in [pairs].
View all issues with Open status.
Discussion:
20.2.3 pairs
Missing assignemnt operator:
template<class U , class V>
requires CopyAssignable<T1, U> && CopyAssignable<T2, V>
pair& operator=(pair<U , V> const & p );
Well, that's interesting. This assignment operator isn't in the current working paper, either. Perhaps we deemed it acceptable to build a temporary of type pair from pair<U, V>, then move-assign from that temporary?
It sounds more like an issue waiting to be opened, unless you want to plug it now. As written we risk moving from lvalues.
[ San Francisco: ]
Would be NAD if better ctors fixed it.
Related to 811.
[ post San Francisco: ]
Possibly NAD Editorial, solved by N2770.
[ 2009-05-25 Alisdair adds: ]
Issue 885 was something I reported while reviewing the library concepts documents ahead of San Francisco. The missing operator was added as part of the paper adopted at that meeting (N2770) and I can confirm this operator is present in the current working paper. I recommend NAD.
[ 2009-07 Frankfurt ]
We agree with the intent, but we need to wait for the dust to settle on concepts.
Proposed resolution:
Section: 30.5.1 [thread.condition.condvar] Status: Open Submitter: Lawrence Crowl Opened: 2008-09-15 Last modified: 2010-02-11
View other active issues in [thread.condition.condvar].
View all other issues in [thread.condition.condvar].
View all issues with Open status.
Discussion:
The Posix/C++ working group has identified an inconsistency between Posix and the C++ working draft in that Posix requires the clock to be identified at creation, whereas C++ permits identifying the clock at the call to wait. The latter cannot be implemented with the former.
[ San Francisco: ]
Howard recommends NAD with the following explanation:
The intent of the current wording is for the condtion_variable::wait_until be able to handle user-defined clocks as well as clocks the system knows about. This can be done by providing overloads for the known clocks, and another overload for unknown clocks which synchs to a known clock before waiting. For example:
template <class Duration> bool condition_variable::wait_until(unique_lock<mutex>& lock, const chrono::time_point<chrono::system_clock, Duration>& abs_time) { using namespace chrono; nanoseconds d = __round_up<nanoseconds>(abs_time.time_since_epoch()); __do_timed_wait(lock.mutex()->native_handle(), time_point<system_clock, nanoseconds>(d)); return system_clock::now() < abs_time; } template <class Clock, class Duration> bool condition_variable::wait_until(unique_lock<mutex>& lock, const chrono::time_point<Clock, Duration>& abs_time) { using namespace chrono; system_clock::time_point s_entry = system_clock::now(); typename Clock::time_point c_entry = Clock::now(); nanoseconds dn = __round_up<nanoseconds>(abs_time.time_since_epoch() - c_entry.time_since_epoch()); __do_timed_wait(lock.mutex()->native_handle(), s_entry + dn); return Clock::now() < abs_time; }In the above example, system_clock is the only clock which the underlying condition variable knows how to deal with. One overload just passes that clock through. The second overload (approximately) converts the unknown clock into a system_clock time_point prior to passing it down to the native condition variable.
On Posix systems vendors are free to add implementation defined constructors which take a clock. That clock can be stored in the condition_variable, and converted to (or not as necessary) as shown above.
If an implementation defined constructor takes a clock (for example), then part of the semantics for that implementation defined ctor might include that a wait_until using a clock other than the one constructed with results in an error (exceptional condition) instead of a conversion to the stored clock. Such a design is up to the vendor as once an implementation defined ctor is used, the vendor is free to specifiy the behavior of waits and/or notifies however he pleases (when the cv is constructed in an implementation defined manner).
[ Post Summit: ]
"POSIX people will review the proposed NAD resolution at their upcoming NY meeting.
See the minutes at: http://wiki.dinkumware.com/twiki/bin/view/Posix/POSIX-CppBindingWorkingGroupNewYork2009.
[ 2009-07 Frankfurt ]
Move to NAD.
[ 2009-07-18 Detlef reopens the issue: ]
On Friday afternoon in Frankfurt is was decided that 887 is NAD. This decision was mainly based on a sample implementation presented by Howard that implemented one clock on top of another. Unfortunately this implementation doesn't work for the probably most important case where a system has a monotonic clock and a real-time clock (or "wall time" clock):
If the underlying "system_clock" is a monotonic clock, and the program waits on the real-time clock, and the real-time clock is set forward, the wait will unblock too late.
If the underlying "system_clock" is a real-time clock, and the program waits on the monotonic clock, and the real-time clock is set back, the wait again will unblock too late.
Sorry that I didn't remember this on Friday, but it was Friday afternoon after a busy week...
So as the decision was made on a wrong asumption, I propose to re-open the issue.
[ 2009-07-26 Howard adds: ]
Detlef correctly argues that condition_variable::wait_until could return "too late" in the context of clocks being adjusted during the wait. I agree with his logic. But I disagree that this makes this interface unimplementable on POSIX.
The POSIX spec also does not guarantee that pthread_cond_timedwait does not return "too late" when clocks are readjusted during the wait. Indeed, the POSIX specification lacks any requirements at all concerning how soon pthread_cond_timedwait returns after a time out. This is evidently a QOI issue by the POSIX standard. Here is a quote of the most relevant normative text concerning pthread_cond_timedwait found here.
The pthread_cond_timedwait() function shall be equivalent to pthread_cond_wait(), except that an error is returned if the absolute time specified by abstime passes (that is, system time equals or exceeds abstime) before the condition cond is signaled or broadcasted, or if the absolute time specified by abstime has already been passed at the time of the call.I.e. the POSIX specification speaks of the error code returned in case of a time out, but not on the timeliness of that return.
Might this simply be an oversight, or minor defect in the POSIX specification?
I do not believe so. This same section goes on to say in non-normative text:
For cases when the system clock is advanced discontinuously by an operator, it is expected that implementations process any timed wait expiring at an intervening time as if that time had actually occurred.Here is non-normative wording encouraging the implementation to ignore an advancing underlying clock and subsequently causing an early (spurious) return. There is no wording at all which addresses Detlef's example of a "late return". With pthread_cond_timedwait this would be caused by setting the system clock backwards. It seems reasonable to assume, based on the wording that is already in the POSIX spec, that again, the discontinuously changed clock would be ignored by pthread_cond_timedwait.
A noteworthy difference between pthread_cond_timedwait and condition_variable::wait_until is that the POSIX spec appears to say that ETIMEDOUT should be returned if pthread_cond_timedwait returns because of timeout signal, whether or not the system clock was discontinuously advanced during the wait. In contrast condition_variable::wait_until always returns:
Clock::now() < abs_timeThat is, the C++ spec requires that the clock be rechecked (detecting discontinuous adjustments during the wait) at the time of return. condition_variable::wait_until may indeed return early or late. But regardless it will return a value reflecting timeout status at the time of return (even if clocks have been adjusted). Of course the clock may be adjusted after the return value is computed but before the client has a chance to read the result of the return. Thus there are no iron-clad guarantees here.
condition_variable::wait_until (and pthread_cond_timedwait) is little more than a convenience function for making sure condition_variable::wait doesn't hang for an unreasonable amount of time (where the client gets to define "unreasonable"). I do not think it is in anyone's interest to try to make it into anything more than that.
I maintain that this is a useful and flexible specification in the spirit of C++, and is implementable on POSIX. Th