Doc. no. | N1830=05-0090 |
Date: | 2005-06-24 |
Project: | Programming Language C++ |
Reply to: | Howard Hinnant <howard.hinnant@gmail.com> |
Reference ISO/IEC IS 14882:1998(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:1998(E) document. Issues are not to be used to request new features.
This document contains only library issues which are actively being considered by the Library Working Group. That is, issues which have a status of New, Open, Ready, and 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.
This document is in an experimental format designed for both viewing via a world-wide web browser and hard-copy printing. It is available as an HTML file for browsing or PDF file for printing.
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:1998(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. Public discussion of C++ Standard related issues occurs on news:comp.std.c++.
For committee members, files available on the committee's private web site include the HTML version of the Standard itself. HTML hyperlinks from this issues list to those files will only work for committee members who have downloaded them into the same disk directory as the issues list files.
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, and the issue is ready to forward to the full committee as a proposed record of response. A Rationale discusses the LWG's reasoning.
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.
TC - (Technical Corrigenda) - 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.
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.
RR - (Record of Response) - The full WG21 committee has determined that this issue is not a defect in the Standard. Action on this issue is thus complete and no further action is possible under ISO rules.
Future - In addition to the regular status, the LWG believes that this issue should be revisited at the next revision of the standard. It is usually paired with NAD.
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: 22.2.2.1.2 [lib.facet.num.get.virtuals] Status: Open Submitter: Nathan Myers Date: 6 Aug 1998
The current description of numeric input does not account for the possibility of overflow. This is an implicit result of changing the description to rely on the definition of scanf() (which fails to report overflow), and conflicts with the documented behavior of traditional and current implementations.
Users expect, when reading a character sequence that results in a value unrepresentable in the specified type, to have an error reported. The standard as written does not permit this.
Further comments from Dietmar:
I don't feel comfortable with the proposed resolution to issue 23: It kind of simplifies the issue to much. Here is what is going on:
Currently, the behavior of numeric overflow is rather counter intuitive and hard to trace, so I will describe it briefly:
Further discussion from Redmond:
The basic problem is that we've defined our behavior, including our error-reporting behavior, in terms of C90. However, C90's method of reporting overflow in scanf is not technically an "input error". The strto_* functions are more precise.
There was general consensus that failbit should be set upon overflow. We considered three options based on this:
Straw poll: (1) 5; (2) 0; (3) 8.
Proposed resolution:
Discussed at Lillehammer. General outline of what we want the solution to look like: we want to say that overflow is an error, and provide a way to distinguish overflow from other kinds of errors. Choose candidate field the same way scanf does, but don't describe the rest of the process in terms of format. If a finite input field is too large (positive or negative) to be represented as a finite value, then set failbit and assign the nearest representable value. Bill will provide wording.
Section: 23.2.5 [lib.vector.bool] Status: Open Submitter: AFNOR Date: 7 Oct 1998
vector<bool> is not a container as its reference and pointer types are not references and pointers.
Also it forces everyone to have a space optimization instead of a speed one.
See also: 99-0008 == N1185 Vector<bool> is Nonconforming, Forces Optimization Choice.
Proposed resolution:
[In Santa Cruz the LWG felt that this was Not A Defect.]
[In Dublin many present felt that failure to meet Container requirements was a defect. There was disagreement as to whether or not the optimization requirements constituted a defect.]
[The LWG looked at the following resolutions in some detail:
* Not A Defect.
* Add a note explaining that vector<bool> does not meet
Container requirements.
* Remove vector<bool>.
* Add a new category of container requirements which
vector<bool> would meet.
* Rename vector<bool>.
No alternative had strong, wide-spread, support and every alternative
had at least one "over my dead body" response.
There was also mention of a transition scheme something like (1) add
vector_bool and deprecate vector<bool> in the next standard. (2)
Remove vector<bool> in the following standard.]
[Modifying container requirements to permit returning proxies (thus allowing container requirements conforming vector<bool>) was also discussed.]
[It was also noted that there is a partial but ugly workaround in that vector<bool> may be further specialized with a customer allocator.]
[Kona: Herb Sutter presented his paper J16/99-0035==WG21/N1211, vector<bool>: More Problems, Better Solutions. Much discussion of a two step approach: a) deprecate, b) provide replacement under a new name. LWG straw vote on that: 1-favor, 11-could live with, 2-over my dead body. This resolution was mentioned in the LWG report to the full committee, where several additional committee members indicated over-my-dead-body positions.]
Discussed at Lillehammer. General agreement that we should deprecate vector<bool> and introduce this functionality under a different name, e.g. bit_vector. This might make it possible to remove the vector<bool> specialization in the standard that comes after C++0x. There was also a suggestion that in C++0x we could additional say that it's implementation defined whether vector<bool> refers to the specialization or to the primary template, but there wasn't general agreement that this was a good idea.
We need a paper for the new bit_vector class.
Section: 18.2.1 [lib.limits] Status: Open Submitter: Stephen Cleary Date: 21 Dec 1999
In some places in this section, the terms "fundamental types" and "scalar types" are used when the term "arithmetic types" is intended. The current usage is incorrect because void is a fundamental type and pointers are scalar types, neither of which should have specializations of numeric_limits.
Proposed resolution:
[Lillehammer: it remains true that numeric_limits is using imprecise language. However, none of the proposals for changed wording are clearer. A redesign of numeric_limits is needed, but this is more a task than an open issue.]
Section: 23.1.2 [lib.associative.reqmts] Status: Open Submitter: Andrew Koenig Date: 30 Apr 2000
If mm is a multimap and p is an iterator into the multimap, then mm.insert(p, x) inserts x into mm with p as a hint as to where it should go. Table 69 claims that the execution time is amortized constant if the insert winds up taking place adjacent to p, but does not say when, if ever, this is guaranteed to happen. All it says it that p is a hint as to where to insert.
The question is whether there is any guarantee about the relationship between p and the insertion point, and, if so, what it is.
I believe the present state is that there is no guarantee: The user can supply p, and the implementation is allowed to disregard it entirely.
Additional comments from Nathan:
The vote [in Redmond] was on whether to elaborately specify the use of
the hint, or to require behavior only if the value could be inserted
adjacent to the hint. I would like to ensure that we have a chance to
vote for a deterministic treatment: "before, if possible, otherwise
after, otherwise anywhere appropriate", as an alternative to the
proposed "before or after, if possible, otherwise [...]".
Proposed resolution:
In table 69 "Associative Container Requirements" in 23.1.2 [lib.associative.reqmts], in the row for a.insert(p, t), change
iterator p is a hint pointing to where the insert should start to search.
to
insertion adjacent to iterator p is preferred if more than one insertion point is valid.
and change
logarithmic in general, but amortized constant if t is inserted right after p.
to
logarithmic in general, but amortized constant if t is inserted adjacent to iterator p.
[Toronto: there was general agreement that this is a real defect: when inserting an element x into a multiset that already contains several copies of x, there is no way to know whether the hint will be used. The proposed resolution was that the new element should always be inserted as close to the hint as possible. So, for example, if there is a subsequence of equivalent values, then providing a.begin() as the hint means that the new element should be inserted before the subsequence even if a.begin() is far away. JC van Winkel supplied precise wording for this proposed resolution, and also for an alternative resolution in which hints are only used when they are adjacent to the insertion point.]
[Copenhagen: the LWG agreed to the original proposed resolution, in which an insertion hint would be used even when it is far from the insertion point. This was contingent on seeing a reference implementation showing that it is possible to implement this requirement without loss of efficiency. John Potter provided such a reference implementation.]
[Redmond: The LWG was reluctant to adopt the proposal that emerged from Copenhagen: it seemed excessively complicated, and went beyond fixing the defect that we identified in Toronto. PJP provided the new wording described in this issue. Nathan agrees that we shouldn't adopt the more detailed semantics, and notes: "we know that you can do it efficiently enough with a red-black tree, but there are other (perhaps better) balanced tree techniques that might differ enough to make the detailed semantics hard to satisfy."]
[Curaçao: Nathan should give us the alternative wording he suggests so the LWG can decide between the two options.]
[Lillehammer: The LWG previously rejected the more detailed semantics, because it seemed more loike a new feature than like defect fixing. We're now more sympathetic to it, but we (especially Bill) are still worried about performance. N1780 describes a naive algorithm, but it's not clear whether there is a non-naive implementation. Is it possible to implement this as efficently as the current version of insert?]
[Post Lillehammer: N1780 updated in post meeting mailing with feedback from Lillehammer with more information regarding performance. ]
Section: 23.2.4.3 [lib.vector.modifiers] Status: Review Submitter: Lisa Lippincott Date: 06 June 2000
Paragraph 2 of 23.2.4.3 [lib.vector.modifiers] describes the complexity of vector::insert:
Complexity: If first and last are forward iterators, bidirectional iterators, or random access iterators, the complexity is linear in the number of elements in the range [first, last) plus the distance to the end of the vector. If they are input iterators, the complexity is proportional to the number of elements in the range [first, last) times the distance to the end of the vector.
First, this fails to address the non-iterator forms of insert.
Second, the complexity for input iterators misses an edge case -- it requires that an arbitrary number of elements can be added at the end of a vector in constant time.
I looked to see if deque had a similar problem, and was surprised to find that deque places no requirement on the complexity of inserting multiple elements (23.2.1.3 [lib.deque.modifiers], paragraph 3):
Complexity: In the worst case, inserting a single element into a deque takes time linear in the minimum of the distance from the insertion point to the beginning of the deque and the distance from the insertion point to the end of the deque. Inserting a single element either at the beginning or end of a deque always takes constant time and causes a single call to the copy constructor of T.
Proposed resolution:
Change Paragraph 2 of 23.2.4.3 [lib.vector.modifiers] to
Complexity: The complexity is linear in the number of elements inserted plus the distance to the end of the vector.
[For input iterators, one may achieve this complexity by first inserting at the end of the vector, and then using rotate.]
Change 23.2.1.3 [lib.deque.modifiers], paragraph 3, to:
Complexity: The complexity is linear in the number of elements inserted plus the shorter of the distances to the beginning and end of the deque. Inserting a single element at either the beginning or the end of a deque causes a single call to the copy constructor of T.
Rationale:
This is a real defect, and proposed resolution fixes it: some complexities aren't specified that should be. This proposed resolution does constrain deque implementations (it rules out the most naive possible implementations), but the LWG doesn't see a reason to permit that implementation.
Section: 19.1 [lib.std.exceptions] Status: Open Submitter: Dave Abrahams Date: 01 Aug 2000
Many of the standard exception types which implementations are required to throw are constructed with a const std::string& parameter. For example:
19.1.5 Class out_of_range [lib.out.of.range] namespace std { class out_of_range : public logic_error { public: explicit out_of_range(const string& what_arg); }; } 1 The class out_of_range defines the type of objects thrown as excep- tions to report an argument value not in its expected range. out_of_range(const string& what_arg); Effects: Constructs an object of class out_of_range. Postcondition: strcmp(what(), what_arg.c_str()) == 0.
There are at least two problems with this:
There may be no cure for (1) other than changing the interface to out_of_range, though one could reasonably argue that (1) is not a defect. Personally I don't care that much if out-of-memory is reported when I only have 20 bytes left, in the case when out_of_range would have been reported. People who use exception-specifications might care a lot, though.
There is a cure for (2), but it isn't completely obvious. I think a note for implementors should be made in the standard. Avoiding possible termination in this case shouldn't be left up to chance. The cure is to use a reference-counted "string" implementation in the exception object. I am not necessarily referring to a std::string here; any simple reference-counting scheme for a NTBS would do.
Further discussion, in email:
...I'm not so concerned about (1). After all, a library implementation can add const char* constructors as an extension, and users don't need to avail themselves of the standard exceptions, though this is a lame position to be forced into. FWIW, std::exception and std::bad_alloc don't require a temporary basic_string.
...I don't think the fixed-size buffer is a solution to the problem,
strictly speaking, because you can't satisfy the postcondition
strcmp(what(), what_arg.c_str()) == 0
For all values of what_arg (i.e. very long values). That means that
the only truly conforming solution requires a dynamic allocation.
Further discussion, from Redmond:
The most important progress we made at the Redmond meeting was realizing that there are two separable issues here: the const string& constructor, and the copy constructor. If a user writes something like throw std::out_of_range("foo"), the const string& constructor is invoked before anything gets thrown. The copy constructor is potentially invoked during stack unwinding.
The copy constructor is a more serious problem, becuase failure during stack unwinding invokes terminate. The copy constructor must be nothrow. Curaçao: Howard thinks this requirement may already be present.
The fundamental problem is that it's difficult to get the nothrow requirement to work well with the requirement that the exception objects store a string of unbounded size, particularly if you also try to make the const string& constructor nothrow. Options discussed include:
(Not all of these options are mutually exclusive.)
Proposed resolution:
Rationale:
Throwing a bad_alloc while trying to construct a message for another exception-derived class is not necessarily a bad thing. And the bad_alloc constructor already has a no throw spec on it (18.4.2.1).
Future:
All involved would like to see const char* constructors added, but this should probably be done for C++0X as opposed to a DR.
I believe the no throw specs currently decorating these functions could be improved by some kind of static no throw spec checking mechanism (in a future C++ language). As they stand, the copy constructors might fail via a call to unexpected. I think what is intended here is that the copy constructors can't fail.
[Pre-Sydney: reopened at the request of Howard Hinnant. Post-Redmond: James Kanze noticed that the copy constructors of exception-derived classes do not have nothrow clauses. Those classes have no copy constructors declared, meaning the compiler-generated implicit copy constructors are used, and those compiler-generated constructors might in principle throw anything.]
Section: 20.1.5 [lib.allocator.requirements] Status: Open Submitter: Matt Austern Date: 22 Aug 2000
From lib-7752:
I've been assuming (and probably everyone else has been assuming) that allocator instances have a particular property, and I don't think that property can be deduced from anything in Table 32.
I think we have to assume that allocator type conversion is a homomorphism. That is, if x1 and x2 are of type X, where X::value_type is T, and if type Y is X::template rebind<U>::other, then Y(x1) == Y(x2) if and only if x1 == x2.
Further discussion: Howard Hinnant writes, in lib-7757:
I think I can prove that this is not provable by Table 32. And I agree it needs to be true except for the "and only if". If x1 != x2, I see no reason why it can't be true that Y(x1) == Y(x2). Admittedly I can't think of a practical instance where this would happen, or be valuable. But I also don't see a need to add that extra restriction. I think we only need:
if (x1 == x2) then Y(x1) == Y(x2)
If we decide that == on allocators is transitive, then I think I can prove the above. But I don't think == is necessarily transitive on allocators. That is:
Given x1 == x2 and x2 == x3, this does not mean x1 == x3.
Example:
x1 can deallocate pointers from: x1, x2, x3
x2 can deallocate pointers from: x1, x2, x4
x3 can deallocate pointers from: x1, x3
x4 can deallocate pointers from: x2, x4x1 == x2, and x2 == x4, but x1 != x4
Proposed resolution:
[Toronto: LWG members offered multiple opinions. One opinion is that it should not be required that x1 == x2 implies Y(x1) == Y(x2), and that it should not even be required that X(x1) == x1. Another opinion is that the second line from the bottom in table 32 already implies the desired property. This issue should be considered in light of other issues related to allocator instances.]
Section: 24.4.1 [lib.reverse.iterators] Status: Ready Submitter: Steve Cleary Date: 27 Nov 2000
This came from an email from Steve Cleary to Fergus in reference to issue 179. The library working group briefly discussed this in Toronto and believed it should be a separate issue. There was also some reservations about whether this was a worthwhile problem to fix.
Steve said: "Fixing reverse_iterator. std::reverse_iterator can (and should) be changed to preserve these additional requirements." He also said in email that it can be done without breaking user's code: "If you take a look at my suggested solution, reverse_iterator doesn't have to take two parameters; there is no danger of breaking existing code, except someone taking the address of one of the reverse_iterator global operator functions, and I have to doubt if anyone has ever done that. . . But, just in case they have, you can leave the old global functions in as well -- they won't interfere with the two-template-argument functions. With that, I don't see how any user code could break."
Proposed resolution:
Section: 24.4.1.1 [lib.reverse.iterator] add/change the following declarations:
A) Add a templated assignment operator, after the same manner as the templated copy constructor, i.e.: template < class U > reverse_iterator < Iterator >& operator=(const reverse_iterator< U >& u); B) Make all global functions (except the operator+) have two template parameters instead of one, that is, for operator ==, !=, <, >, <=, >=, - replace: template < class Iterator > typename reverse_iterator< Iterator >::difference_type operator-( const reverse_iterator< Iterator >& x, const reverse_iterator< Iterator >& y); with: template < class Iterator1, class Iterator2 > typename reverse_iterator < Iterator1 >::difference_type operator-( const reverse_iterator < Iterator1 > & x, const reverse_iterator < Iterator2 > & y);
Also make the addition/changes for these signatures in 24.4.1.3 [lib.reverse.iter.ops].
[ Copenhagen: The LWG is concerned that the proposed resolution introduces new overloads. Experience shows that introducing overloads is always risky, and that it would be inappropriate to make this change without implementation experience. It may be desirable to provide this feature in a different way. ]
[ Lillehammer: We now have implementation experience, and agree that this solution is safe and correct. ]
Section: 25.1.1 [lib.alg.foreach] Status: Open Submitter: Angelika Langer Date: 03 Jan 2001
The specification of the for_each algorithm does not have a "Requires" section, which means that there are no restrictions imposed on the function object whatsoever. In essence it means that I can provide any function object with arbitrary side effects and I can still expect a predictable result. In particular I can expect that the function object is applied exactly last - first times, which is promised in the "Complexity" section.
I don't see how any implementation can give such a guarantee without imposing requirements on the function object.
Just as an example: consider a function object that removes elements from the input sequence. In that case, what does the complexity guarantee (applies f exactly last - first times) mean?
One can argue that this is obviously a nonsensical application and a theoretical case, which unfortunately it isn't. I have seen programmers shooting themselves in the foot this way, and they did not understand that there are restrictions even if the description of the algorithm does not say so.
Proposed resolution:
[Lillehammer: This is more general than for_each. We don't want the function object in transform invalidiating iterators either. There should be a note somewhere in clause 17 (17, not 25) saying that user code operating on a range may not invalidate iterators unless otherwise specified. Bill will provide wording.]
Section: 17.4.3.1.1 [lib.macro.names] Status: Review Submitter: James Kanze Date: 11 Jan 2001
Paragraph 2 of 17.4.3.1.1 [lib.macro.names] reads: "A translation unit that includes a header shall not contain any macros that define names declared in that header." As I read this, it would mean that the following program is legal:
#define npos 3.14 #include <sstream>
since npos is not defined in <sstream>. It is, however, defined in <string>, and it is hard to imagine an implementation in which <sstream> didn't include <string>.
I think that this phrase was probably formulated before it was decided that a standard header may freely include other standard headers. The phrase would be perfectly appropriate for C, for example. In light of 17.4.4.1 [lib.res.on.headers] paragraph 1, however, it isn't stringent enough.
Proposed resolution:
For 17.4.3.1.1 [lib.macro.names], replace the current wording, which reads:
Each name defined as a macro in a header is reserved to the implementation for any use if the translation unit includes the header.168)
A translation unit that includes a header shall not contain any macros that define names declared or defined in that header. Nor shall such a translation unit define macros for names lexically identical to keywords.
168) It is not permissible to remove a library macro definition by using the #undef directive.
with the wording:
A translation unit that includes a standard library header shall not #define or #undef names declared in any standard library header.
A translation unit shall not #define or #undef names lexically identical to keywords.
[Lillehammer: Beman provided new wording]
Section: 24.1.4 [lib.bidirectional.iterators], 24.1.5 [lib.random.access.iterators] Status: Open Submitter: John Potter Date: 22 Jan 2001
In section 24.1.4 [lib.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.1.5 [lib.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.
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]
Section: 27.6.1.3 [lib.istream.unformatted] Status: Review Submitter: Howard Hinnant Date: 09 Oct 2001
I think we have a defect.
According to lwg issue 60 which is now a dr, the description of seekg in 27.6.1.3 [lib.istream.unformatted] paragraph 38 now looks like:
Behaves as an unformatted input function (as described in 27.6.1.3, paragraph 1), except that it does not count the number of characters extracted and does not affect the value returned by subsequent calls to gcount(). After constructing a sentry object, if fail() != true, executes rdbuf()>pubseekpos( pos).
And according to lwg issue 243 which is also now a dr, 27.6.1.3, paragraph 1 looks like:
Each unformatted input function begins execution by constructing an object of class sentry with the default argument noskipws (second) argument true. If the sentry object returns true, when converted to a value of type bool, the function endeavors to obtain the requested input. Otherwise, if the sentry constructor exits by throwing an exception or if the sentry object returns false, when converted to a value of type bool, the function returns without attempting to obtain any input. In either case the number of extracted characters is set to 0; unformatted input functions taking a character array of non-zero size as an argument shall also store a null character (using charT()) in the first location of the array. If an exception is thrown during input then ios::badbit is turned on in *this'ss error state. If (exception()&badbit)!= 0 then the exception is rethrown. It also counts the number of characters extracted. If no exception has been thrown it ends by storing the count in a member object and returning the value specified. In any event the sentry object is destroyed before leaving the unformatted input function.
And finally 27.6.1.1.2/5 says this about sentry:
If, after any preparation is completed, is.good() is true, ok_ != false otherwise, ok_ == false.
So although the seekg paragraph says that the operation proceeds if !fail(), the behavior of unformatted functions says the operation proceeds only if good(). The two statements are contradictory when only eofbit is set. I don't think the current text is clear which condition should be respected.
Further discussion from Redmond:
PJP: It doesn't seem quite right to say that seekg is "unformatted". That makes specific claims about sentry that aren't quite appropriate for seeking, which has less fragile failure modes than actual input. If we do really mean that it's unformatted input, it should behave the same way as other unformatted input. On the other hand, "principle of least surprise" is that seeking from EOF ought to be OK.
Proposed resolution:
Change 27.6.1.3 [lib.istream.unformatted] to:
Behaves as an unformatted input function (as described in 27.6.1.3, paragraph 1), except that it does not count the number of characters extracted, does not affect the value returned by subsequent calls to gcount(), and does not examine the value returned by the sentry object. After constructing a sentry object, if fail() != true, executes rdbuf()->pubseekpos(pos). In case of success, the function calls clear(). In case of failure, the function calls setstate(failbit) (which may throw ios_base::failure).
[Lillehammer: Matt provided wording.]
Rationale:
In C, fseek does clear EOF. This is probably what most users would expect. We agree that having eofbit set should not deter a seek, and that a successful seek should clear eofbit. Note that fail() is true only if failbit or badbit is set, so using !fail(), rather than good(), satisfies this goal.
Section: 20.3.6.2 [lib.bind.1st] Status: Review Submitter: Andrew Demkin Date: 26 Apr 2002
The definition of bind1st() (20.3.6.2 [lib.bind.1st]) can result in the construction of an unsafe binding between incompatible pointer types. For example, given a function whose first parameter type is 'pointer to T', it's possible without error to bind an argument of type 'pointer to U' when U does not derive from T:
foo(T*, int); struct T {}; struct U {}; U u; int* p; int* q; for_each(p, q, bind1st(ptr_fun(foo), &u)); // unsafe binding
The definition of bind1st() includes a functional-style conversion to map its argument to the expected argument type of the bound function (see below):
typename Operation::first_argument_type(x)
A functional-style conversion (5.2.3 [expr.type.conv]) is defined to be semantically equivalent to an explicit cast expression (5.4 [expr.cast]), which may (according to 5.4, paragraph 5) be interpreted as a reinterpret_cast, thus masking the error.
The problem and proposed change also apply to 20.3.6.4 [lib.bind.2nd].
Proposed resolution:
Add this sentence to the end of 20.3.6 [lib.binders]/1: "Binders bind1st and bind2nd are deprecated in favor of std::tr1::bind."
(Notes to editor: (1) when and if tr1::bind is incorporated into the standard, "std::tr1::bind" should be changed to "std::bind". (2) 20.3.6 should probably be moved to Annex D.
Rationale:
There is no point in fixing bind1st and bind2nd. tr1::bind is a superior solution. It solves this problem and others.
Section: 27.3 [lib.iostream.objects] Status: Review Submitter: Ruslan Abdikeev Date: 8 Jul 2002
Is it safe to use standard iostream objects from constructors of static objects? Are standard iostream objects constructed and are their associations established at that time?
Surpisingly enough, Standard does NOT require that.
27.3/2 [lib.iostream.objects] guarantees that standard iostream objects are constructed and their associations are established before the body of main() begins execution. It also refers to ios_base::Init class as the panacea for constructors of static objects.
However, there's nothing in 27.3 [lib.iostream.objects], in 27.4.2 [lib.ios.base], and in 27.4.2.1.6 [lib.ios::Init], that would require implementations to allow access to standard iostream objects from constructors of static objects.
Details:
Core text refers to some magic object ios_base::Init, which will be discussed below:
"The [standard iostream] objects are constructed, and their associations are established at some time prior to or during first time an object of class basic_ios<charT,traits>::Init is constructed, and in any case before the body of main begins execution." (27.3/2 [lib.iostream.objects])
The first non-normative footnote encourages implementations to initialize standard iostream objects earlier than required.
However, the second non-normative footnote makes an explicit and unsupported claim:
"Constructors and destructors for static objects can access these [standard iostream] objects to read input from stdin or write output to stdout or stderr." (27.3/2 footnote 265 [lib.iostream.objects])
The only bit of magic is related to that ios_base::Init class. AFAIK, the rationale behind ios_base::Init was to bring an instance of this class to each translation unit which #included <iostream> or related header. Such an inclusion would support the claim of footnote quoted above, because in order to use some standard iostream object it is necessary to #include <iostream>.
However, while Standard explicitly describes ios_base::Init as an appropriate class for doing the trick, I failed to found a mention of an _instance_ of ios_base::Init in Standard.
Proposed resolution:
Add to 27.3 [lib.iostream.objects], p2, immediately before the last sentence of the paragraph, the following two sentences:
If a translation unit includes <iostream>, or explicitly constructs an ios_base::Init object, dynamic initialization of objects later in that translation unit may assume that these stream objects have been constructed and destructors may assume that these stream objects have not yet been destroyed.
[Lillehammer: Matt provided wording.]
Rationale:
The original proposed resolution unconditionally required implementations to define an ios_base::Init object of some implementation-defined name in the header <iostream>. That's an overspecification. First, defining the object may be unnecessary and even detrimental to performance if an implementation can guarantee that the 8 standard iostream objects will be initialized before any other user-defined object in a program. Second, there is no need to require implementations to document the name of the object.
The new proposed resolution gives users guidance on what they need to do to ensure that stream objects are constructed during startup.
Section: 23.1 [lib.container.requirements] Status: Review Submitter: Frank Compagner Date: 20 Jul 2002
The requirements for multiset and multimap containers (23.1 [lib.containers.requirements], 23.1.2 [lib.associative.reqmnts], 23.3.2 [lib.multimap] and 23.3.4 [lib.multiset]) make no mention of the stability of the required (mutating) member functions. It appears the standard allows these functions to reorder equivalent elements of the container at will, yet the pervasive red-black tree implementation appears to provide stable behaviour.
This is of most concern when considering the behaviour of erase(). A stability requirement would guarantee the correct working of the following 'idiom' that removes elements based on a certain predicate function.
multimap<int, int> m; multimap<int, int>::iterator i = m.begin(); while (i != m.end()) { if (pred(i)) m.erase (i++); else ++i; }
Although clause 23.1.2/8 guarantees that i remains a valid iterator througout this loop, absence of the stability requirement could potentially result in elements being skipped. This would make this code incorrect, and, furthermore, means that there is no way of erasing these elements without iterating first over the entire container, and second over the elements to be erased. This would be unfortunate, and have a negative impact on both performance and code simplicity.
If the stability requirement is intended, it should be made explicit (probably through an extra paragraph in clause 23.1.2).
If it turns out stability cannot be guaranteed, i'd argue that a remark or footnote is called for (also somewhere in clause 23.1.2) to warn against relying on stable behaviour (as demonstrated by the code above). If most implementations will display stable behaviour, any problems emerging on an implementation without stable behaviour will be hard to track down by users. This would also make the need for an erase_if() member function that much greater.
This issue is somewhat related to LWG issue 130.
Proposed resolution:
Add the following to the end of 23.1.2 [lib.associative.reqmts] paragraph 4: "For set and map, insertand erase are stable: they preserve the relative ordering of equivalent elements.
[Lillehammer: Matt provided wording]
[Joe Gottman points out that the provided wording does not address multimap and multiset. N1780 also addresses this issue and suggests wording.]
Rationale:
The LWG agrees that this guarantee is necessary for common user idioms to work, and that all existing implementations provide this property. Note that this resolution guarantees stability for multimap and multiset, not for all associative containers in general.
Section: 27.7.1.3 [lib.stringbuf.virtuals] Status: Review Submitter: Ray Lischner Date: 14 Aug 2002
In Section 27.7.1.3 [lib.stringbuf.virtuals], Table 90, the implication is that the four conditions should be mutually exclusive, but they are not. The first two cases, as written, are subcases of the third.
As written, it is unclear what should be the result if cases 1 and 2 are both true, but case 3 is false.
Proposed resolution:
Rewrite these conditions as:
(which & (ios_base::in|ios_base::out)) == ios_base::in
(which & (ios_base::in|ios_base::out)) == ios_base::out
(which & (ios_base::in|ios_base::out)) == (ios_base::in|ios_base::out) and way == either ios_base::beg or ios_base::end
Otherwise
Rationale:
It's clear what we wanted to say, we just failed to say it. This fixes it.
Section: 22.2.1.5 [lib.locale.codecvt] Status: Open Submitter: Martin Sebor Date: 30 Aug 2002
It seems that the descriptions of codecvt do_in() and do_out() leave sufficient room for interpretation so that two implementations of codecvt may not work correctly with the same filebuf. Specifically, the following seems less than adequately specified:
Finally, the conditions described at the end of 22.2.1.5.2 [lib.locale.codecvt.virtuals], p4 don't seem to be possible:
"A return value of partial, if (from_next == from_end), indicates that either the destination sequence has not absorbed all the available destination elements, or that additional source elements are needed before another destination element can be produced."
If the value is partial, it's not clear to me that (from_next ==from_end) could ever hold if there isn't enough room in the destination buffer. In order for (from_next==from_end) to hold, all characters in that range must have been successfully converted (according to 22.2.1.5.2 [lib.locale.codecvt.virtuals], p2) and since there are no further source characters to convert, no more room in the destination buffer can be needed.
It's also not clear to me that (from_next==from_end) could ever hold if additional source elements are needed to produce another destination character (not element as incorrectly stated in the text). partial is returned if "not all source characters have been converted" according to Table 53, which also implies that (from_next==from) does NOT hold.
Could it be that the intended qualifying condition was actually (from_next != from_end), i.e., that the sentence was supposed to read
"A return value of partial, if (from_next != from_end),..."
which would make perfect sense, since, as far as I understand it, partial can only occur if (from_next != from_end)?
Proposed resolution:
[Lillehammer: Defer for the moment, but this really needs to be fixed. Right now, the description of codecvt is too vague for it to be a useful contract between providers and clients of codecvt facets. (Note that both vendors and users can be both providers and clients of codecvt facets.) The major philosophical issue is whether the standard should only describe mappings that take a single wide character to multiple narrow characters (and vice versa), or whether it should describe fully general N-to-M conversions. When the original standard was written only the former was contemplated, but today, in light of the popularity of utf8 and utf16, that doesn't seem sufficient for C++0x. Bill supports general N-to-M conversions; we need to make sure Martin and Howard agree.]
Section: 25.3.3.3 [lib.equal.range] Status: Review Submitter: Hans Bos Date: 18 Oct 2002
Section 25.3.3.3 [lib.equal.range] states that at most 2 * log(last - first) + 1 comparisons are allowed for equal_range.
It is not possible to implement equal_range with these constraints.
In a range of one element as in:
int x = 1; equal_range(&x, &x + 1, 1)
it is easy to see that at least 2 comparison operations are needed.
For this case at most 2 * log(1) + 1 = 1 comparison is allowed.
I have checked a few libraries and they all use the same (nonconforming) algorithm for equal_range that has a complexity of
2* log(distance(first, last)) + 2.
I guess this is the algorithm that the standard assumes for equal_range.
It is easy to see that 2 * log(distance) + 2 comparisons are enough since equal range can be implemented with lower_bound and upper_bound (both log(distance) + 1).
I think it is better to require something like 2log(distance) + O(1) (or even logarithmic as multiset::equal_range). Then an implementation has more room to optimize for certain cases (e.g. have log(distance) characteristics when at most match is found in the range but 2log(distance) + 4 for the worst case).
Proposed resolution:
In 25.3.3.1 [lib.lower.bound]/4, change log(last - first) + 1 to log2(last - first) + O(1).
In 25.3.3.2 [lib.upper.bound]/4, change log(last - first) + 1 to log2(last - first) + O(1).
In 25.3.3.3 [lib.equal.range]/4, change 2*log(last - first) + 1 to 2*log2(last - first) + O(1).
[Matt provided wording]
Rationale:
The LWG considered just saying O(log n) for all three, but Ê decided that threw away too much valuable information.Ê The fact Ê that lower_bound is twice as fast as equal_range is important. Ê However, it's better to allow an arbitrary additive constant than to Ê specify an exact count.Ê An exact count would have to Ê involve floor or ceil.Ê It would be too easy to Ê get this wrong, and don't provide any substantial value for users.
Section: 17 [lib.library] Status: Open Submitter: Matt Austern Date: 23 Oct 2002
Many function templates have parameters that are passed by value; a typical example is find_if's pred parameter in 25.1.2 [lib.alg.find]. Are the corresponding template parameters (Predicate in this case) implicitly required to be CopyConstructible, or does that need to be spelled out explicitly?
This isn't quite as silly a question as it might seem to be at first sight. If you call find_if in such a way that template argument deduction applies, then of course you'll get call by value and you need to provide a copy constructor. If you explicitly provide the template arguments, however, you can force call by reference by writing something like find_if<my_iterator, my_predicate&>. The question is whether implementation are required to accept this, or whether this is ill-formed because my_predicate& is not CopyConstructible.
The scope of this problem, if it is a problem, is unknown. Function object arguments to generic algorithms in clauses 25 [lib.algorithms] and 26 [lib.numerics] are obvious examples. A review of the whole library is necessary.
Proposed resolution:
[ This is really two issues. First, predicates are typically passed by value but we don't say they must be Copy Constructible. They should be. Second: is specialization allowed to transform value arguments into references? References aren't copy constructible, so this should not be allowed. ]
Section: 26.2 [lib.complex.numbers] Status: Open Submitter: Gabriel Dos Reis Date: 8 Nov 2002
The absence of explicit description of std::complex<T> layout makes it imposible to reuse existing software developed in traditional languages like Fortran or C with unambigous and commonly accepted layout assumptions. There ought to be a way for practitioners to predict with confidence the layout of std::complex<T> whenever T is a numerical datatype. The absence of ways to access individual parts of a std::complex<T> object as lvalues unduly promotes severe pessimizations. For example, the only way to change, independently, the real and imaginary parts is to write something like
complex<T> z; // ... // set the real part to r z = complex<T>(r, z.imag()); // ... // set the imaginary part to i z = complex<T>(z.real(), i);
At this point, it seems appropriate to recall that a complex number is, in effect, just a pair of numbers with no particular invariant to maintain. Existing practice in numerical computations has it that a complex number datatype is usually represented by Cartesian coordinates. Therefore the over-encapsulation put in the specification of std::complex<> is not justified.
Proposed resolution:
Add the following requirements to 26.2 [lib.complex.numbers] as 26.2/4:
If z is an lvalue expression of type cv std::complex<T> then
- the expression reinterpret_cast<cv T(&)[2]>(z) is well-formed; and
- reinterpret_cast<cvT(&)[2]>(z)[0]designates the real part of z; and
- reinterpret_cast<cvT(&)[2]>(z)[1]designates the imaginary part of z.
Moreover, if a is an expression of pointer type cv complex<T>* and the expression a[i] is well-defined for an integer expression i then:
- reinterpret_cast<cvT*>(a)[2+i] designates the real part of a[i]; and
- reinterpret_cast<cv T*>(a)[2+i+1] designates the imaginary part of a[i].
In the header synopsis in 26.2.1 [lib.complex.synopsis], replace
template<class T> T real(const complex<T>&); template<class T> T imag(const complex<T>&);
with
template<class T> const T& real(const complex<T>&); template<class T> T& real( complex<T>&); template<class T> const T& imag(const complex<T>&); template<class T> T& imag( complex<T>&);
In 26.2.7 [lib.complex.value.ops] paragraph 1, change
template<class T> T real(const complex<T>&);
to
template<class T> const T& real(const complex<T>&); template<class T> T& real( complex<T>&);
and change the Returns clause to "Returns: The real part of x
.In 26.2.7 [lib.complex.value.ops] paragraph 2, change
template<class T> T imag(const complex<T>&);
to
template<class T> const T& imag(const complex<T>&); template<class T> T& imag( complex<T>&);
and change the Returns clause to "Returns: The imaginary part of x
.[Kona: The layout guarantee is absolutely necessary for C compatibility. However, there was disagreement about the other part of this proposal: retrieving elements of the complex number as lvalues. An alternative: continue to have real() and imag() return rvalues, but add set_real() and set_imag(). Straw poll: return lvalues - 2, add setter functions - 5. Related issue: do we want reinterpret_cast as the interface for converting a complex to an array of two reals, or do we want to provide a more explicit way of doing it? Howard will try to resolve this issue for the next meeting.]
[pre-Sydney: Howard summarized the options in n1589.]
Rationale:
The LWG believes that C99 compatibility would be enough justification for this change even without other considerations. All existing implementations already have the layout proposed here.
Section: 27.6.2.5.1 [lib.ostream.formatted.reqmts] Status: Open Submitter: Martin Sebor Date: 27 Dec 2002
There is a contradiction in Formatted output about what bit is supposed to be set if the formatting fails. On sentence says it's badbit and another that it's failbit.
27.6.2.5.1, p1 says in the Common Requirements on Formatted output functions:
... If the generation fails, then the formatted output function does setstate(ios::failbit), which might throw an exception.
27.6.2.5.2, p1 goes on to say this about Arithmetic Inserters:
... The formatting conversion occurs as if it performed the following code fragment:
bool failed = use_facet<num_put<charT,ostreambuf_iterator<charT,traits> > > (getloc()).put(*this, *this, fill(), val). failed(); ... If failed is true then does setstate(badbit) ...
The original intent of the text, according to Jerry Schwarz (see c++std-lib-10500), is captured in the following paragraph:
In general "badbit" should mean that the stream is unusable because of some underlying failure, such as disk full or socket closure; "failbit" should mean that the requested formatting wasn't possible because of some inconsistency such as negative widths. So typically if you clear badbit and try to output something else you'll fail again, but if you clear failbit and try to output something else you'll succeed.
In the case of the arithmetic inserters, since num_put cannot report failure by any means other than exceptions (in response to which the stream must set badbit, which prevents the kind of recoverable error reporting mentioned above), the only other detectable failure is if the iterator returned from num_put returns true from failed().
Since that can only happen (at least with the required iostream specializations) under such conditions as the underlying failure referred to above (e.g., disk full), setting badbit would seem to be the appropriate response (indeed, it is required in 27.6.2.5.2, p1). It follows that failbit can never be directly set by the arithmetic (it can only be set by the sentry object under some unspecified conditions).
The situation is different for other formatted output functions which can fail as a result of the streambuf functions failing (they may do so by means other than exceptions), and which are then required to set failbit.
The contradiction, then, is that ostream::operator<<(int) will set badbit if the disk is full, while operator<<(ostream&, char) will set failbit under the same conditions. To make the behavior consistent, the Common requirements sections for the Formatted output functions should be changed as proposed below.
Proposed resolution:
[Kona: There's agreement that this is a real issue. What we decided at Kona: 1. An error from the buffer (which can be detected either directly from streambuf's member functions or by examining a streambuf_iterator) should always result in badbit getting set. 2. There should never be a circumstance where failbit gets set. That represents a formatting error, and there are no circumstances under which the output facets are specified as signaling a formatting error. (Even more so for string output that for numeric because there's nothing to format.) If we ever decide to make it possible for formatting errors to exist then the facets can signal the error directly, and that should go in clause 22, not clause 27. 3. The phrase "if generation fails" is unclear and should be eliminated. It's not clear whether it's intended to mean a buffer error (e.g. a full disk), a formatting error, or something else. Most people thought it was supposed to refer to buffer errors; if so, we should say so. Martin will provide wording.]
Rationale:
Section: 23.3.5.1 [lib.bitset.cons] Status: Open Submitter: Martin Sebor Date: 5 Jan 2003
23.3.5.1, p6 [lib.bitset.cons] talks about a generic character having the value of 0 or 1 but there is no definition of what that means for charT other than char and wchar_t. And even for those two types, the values 0 and 1 are not actually what is intended -- the values '0' and '1' are. This, along with the converse problem in the description of to_string() in 23.3.5.2, p33, looks like a defect remotely related to DR 303.
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-defects.html#303
23.3.5.1: -6- An element of the constructed string has value zero if the corresponding character in str, beginning at position pos, is 0. Otherwise, the element has the value one.
23.3.5.2: -33- Effects: Constructs a string object of the appropriate type and initializes it to a string of length N characters. Each character is determined by the value of its corresponding bit position in *this. Character position N ?- 1 corresponds to bit position zero. Subsequent decreasing character positions correspond to increasing bit positions. Bit value zero becomes the character 0, bit value one becomes the character 1.
Also note the typo in 23.3.5.1, p6: the object under construction is a bitset, not a string.
Proposed resolution:
Change the constructor's function declaration immediately before 23.3.5.1 [lib.bitset.cons] p3 to:
template <class charT, class traits, class Allocator> explicit bitset(const basic_string<charT, traits, Allocator>& str, typename basic_string<charT, traits, Allocator>::size_type pos = 0, typename basic_string<charT, traits, Allocator>::size_type n = basic_string<charT, traits, Allocator>::npos, charT zero = charT('0'), charT one = charT('1'))
Change the first two sentences of 23.3.5.1 [lib.bitset.cons] p6 to: "An element of the constructed string has value 0 if the corresponding character in str, beginning at position pos, is zero. Otherwise, the element has the value 1.
Change the text of the second sentence in 23.3.5.1, p5 to read: "The function then throws invalid_argument if any of the rlen characters in str beginning at position pos is other than zero or one. The function uses traits::eq() to compare the character values."
Change the declaration of the to_string member function immediately before 23.3.5.2 [lib.bitset.members] p33 to:
template <class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> to_string(charT zero = charT('0'), charT one = charT('1')) const;
Change the last sentence of 23.3.5.2 [lib.bitset.members] p33 to: "Bit value 0 becomes the character zero, bit value 1 becomes the character one.
Change 23.3.5.3 [lib.bitset.operators] p8 to:
Returns:
os << x.template to_string<charT,traits,allocator<charT> >( use_facet<ctype<charT> >(os.getloc()).widen('0'), use_facet<ctype<charT> >(os.getloc()).widen('1'));
Rationale:
There is a real problem here: we need the character values of '0' and '1', and we have no way to get them since strings don't have imbued locales. In principle the "right" solution would be to provide an extra object, either a ctype facet or a full locale, which would be used to widen '0' and '1'. However, there was some discomfort about using such a heavyweight mechanism. The proposed resolution allows those users who care about this issue to get it right.
We fix the inserter to use the new arguments. Note that we already fixed the analogous problem with the extractor in issue 303.
Section: 27.6.2.3 [lib.ostream::sentry] Status: Open Submitter: Martin Sebor Date: 5 Jan 2003
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.
Proposed resolution:
[ 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. ]
Section: 27.6.2.3 [lib.ostream::sentry] Status: Open Submitter: Martin Sebor Date: 5 Jan 2003
While reviewing unformatted input member functions of istream for their behavior when they encounter end-of-file during input I found that the requirements vary, sometimes unexpectedly, and in more than one case even contradict established practice (GNU libstdc++ 3.2, IBM VAC++ 6.0, STLPort 4.5, SunPro 5.3, HP aCC 5.38, Rogue Wave libstd 3.1, and Classic Iostreams).
The following unformatted input member functions set eofbit if they encounter an end-of-file (this is the expected behavior, and also the behavior of all major implementations):
basic_istream<charT, traits>& get (char_type*, streamsize, char_type);
Also sets failbit if it fails to extract any characters.
basic_istream<charT, traits>& get (char_type*, streamsize);
Also sets failbit if it fails to extract any characters.
basic_istream<charT, traits>& getline (char_type*, streamsize, char_type);
Also sets failbit if it fails to extract any characters.
basic_istream<charT, traits>& getline (char_type*, streamsize);
Also sets failbit if it fails to extract any characters.
basic_istream<charT, traits>& ignore (int, int_type);
basic_istream<charT, traits>& read (char_type*, streamsize);
Also sets failbit if it encounters end-of-file.
streamsize readsome (char_type*, streamsize);
The following unformated input member functions set failbit but not eofbit if they encounter an end-of-file (I find this odd since the functions make it impossible to distinguish a general failure from a failure due to end-of-file; the requirement is also in conflict with all major implementation which set both eofbit and failbit):
int_type get();
basic_istream<charT, traits>& get (char_type&);
These functions only set failbit of they extract no characters, otherwise they don't set any bits, even on failure (I find this inconsistency quite unexpected; the requirement is also in conflict with all major implementations which set eofbit whenever they encounter end-of-file):
basic_istream<charT, traits>& get (basic_streambuf<charT, traits>&, char_type);
basic_istream<charT, traits>& get (basic_streambuf<charT, traits>&);
This function sets no bits (all implementations except for STLport and Classic Iostreams set eofbit when they encounter end-of-file):
int_type peek ();
Proposed resolution:
Informally, what we want is a global statement of intent saying that eofbit gets set if we trip across EOF, and then we can take away the specific wording for individual functions. A full review is necessary. The wording currently in the standard is a mishmash, and changing it on an individual basis wouldn't make things better. Dietmar will do this work.
Section: 20.1.5 [lib.allocator.requirements] Status: Open Submitter: Markus Mauhart Date: 27 Feb 2003
I think that in par2 of 20.1.5 [lib.allocator.requirements] the last two lines of table 32 contain two incorrect type casts. The lines are ...
a.construct(p,t) Effect: new((void*)p) T(t) a.destroy(p) Effect: ((T*)p)?->~T()
.... with the prerequisits coming from the preceding two paragraphs, especially from table 31:
alloc<T> a ;// an allocator for T alloc<T>::pointer p ;// random access iterator // (may be different from T*) alloc<T>::reference r = *p;// T& T const& t ;
For that two type casts ("(void*)p" and "(T*)p") to be well-formed this would require then conversions to T* and void* for all alloc<T>::pointer, so it would implicitely introduce extra requirements for alloc<T>::pointer, additionally to the only current requirement (being a random access iterator).
Proposed resolution:
"(void*)p" should be replaced with "(void*)&*p" and that "((T*)p)?->" should be replaced with "(*p)." or with "(&*p)->".
Note: Actually I would prefer to replace "((T*)p)?->dtor_name" with "p?->dtor_name", but AFAICS this is not possible cause of an omission in 13.5.6 [over.ref] (for which I have filed another DR on 29.11.2002).
[Kona: The LWG thinks this is somewhere on the border between Open and NAD. The intend is clear: construct constructs an object at the location p. It's reading too much into the description to think that literally calling new is required. Tweaking this description is low priority until we can do a thorough review of allocators, and, in particular, allocators with non-default pointer types.]
Section: 24.1 [lib.iterator.requirements] Status: Open Submitter: Nathan Myers Date: 3 June 2003
I've been discussing iterator semantics with Dave Abrahams, and a surprise has popped up. I don't think this has been discussed before.
24.1 [lib.iterator.requirements] 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.
Related issue: 407
Proposed resolution:
[ 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. ]
Section: 18.2.2 [lib.c.limits] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
Given two overloads of the function foo(), one taking an argument of type
int and the other taking a long, which one will the call foo(LONG_MAX)
resolve to? The expected answer should be foo(long), but whether that
is true depends on the #defintion of the LONG_MAX macro, specifically
its type. This issue is about the fact that the type of these macros
is not actually required to be the same as the the type each respective
limit.
Section 18.2.2 of the C++ Standard does not specify the exact types of
the XXX_MIN and XXX_MAX macros #defined in the <climits> and <limits.h>
headers such as INT_MAX and LONG_MAX and instead defers to the C standard.
Section 5.2.4.2.1, p1 of the C standard specifies that "The values [of
these constants] shall be replaced by constant expressions suitable for use
in #if preprocessing directives. Moreover, except for CHAR_BIT and MB_LEN_MAX,
the following shall be replaced by expressions that have the same type as
would an expression that is an object of the corresponding type converted
according to the integer promotions."
The "corresponding type converted according to the integer promotions" for
LONG_MAX is, according to 6.4.4.1, p5 of the C standard, the type of long
converted to the first of the following set of types that can represent it:
int, long int, long long int. So on an implementation where (sizeof(long)
== sizeof(int)) this type is actually int, while on an implementation where
(sizeof(long) > sizeof(int)) holds this type will be long.
This is not an issue in C since the type of the macro cannot be detected
by any conforming C program, but it presents a portability problem in C++
where the actual type is easily detectable by overload resolution.
Proposed resolution:
[Kona: the LWG does not believe this is a defect. The C macro definitions are what they are; we've got a better mechanism, std::numeric_limits, that is specified more precisely than the C limit macros. At most we should add a nonnormative note recommending that users who care about the exact types of limit quantities should use <limits> instead of <climits>.]
Section: 22.2.1.1.2 [lib.locale.ctype.virtuals] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
The Effects and Returns clauses of the do_widen() member function of the ctype facet fail to specify the behavior of the function on failure. That the function may not be able to simply cast the narrow character argument to the type of the result since doing so may yield the wrong value for some wchar_t encodings. Popular implementations of ctype<wchar_t> that use mbtowc() and UTF-8 as the native encoding (e.g., GNU glibc) will fail when the argument's MSB is set. There is no way for the the rest of locale and iostream to reliably detect this failure.
Proposed resolution:
[Kona: This is a real problem. Widening can fail. It's unclear what the solution should be. Returning WEOF works for the wchar_t specialization, but not in general. One option might be to add a default, like narrow. But that's an incompatible change. Using traits::eof might seem like a good idea, but facets don't have access to traits (a recurring problem). We could have widen throw an exception, but that's a scary option; existing library components aren't written with the assumption that widen can throw.]
Section: 27.4.2.1.6 [lib.ios::Init] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
The dtor of the ios_base::Init object is supposed to call flush() on the 6 standard iostream objects cout, cerr, clog, wcout, wcerr, and wclog. This call may cause an exception to be thrown.
17.4.4.8, p3 prohibits all library destructors from throwing exceptions.
The question is: What should this dtor do if one or more of these calls to flush() ends up throwing an exception? This can happen quite easily if one of the facets installed in the locale imbued in the iostream object throws.
Proposed resolution:
[Kona: We probably can't do much better than what we've got, so the LWG is leaning toward NAD. At the point where the standard stream objects are being cleaned up, the usual error reporting mechanism are all unavailable. And exception from flush at this point will definitely cause problems. A quality implementation might reasonably swallow the exception, or call abort, or do something even more drastic.]
Section: 27.6.1.1.2 [lib.istream::sentry] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
27.6.1.1.2, p2 says that istream::sentry ctor prepares for input if is.good() is true. p4 then goes on to say that the ctor sets the sentry::ok_ member to true if the stream state is good after any preparation. 27.6.1.2.1, p1 then says that a formatted input function endeavors to obtain the requested input if the sentry's operator bool() returns true. Given these requirements, no formatted extractor should ever set failbit if the initial stream rdstate() == eofbit. That is contrary to the behavior of all implementations I tested. The program below prints out eof = 1, fail = 0 eof = 1, fail = 1 on all of them.
#include <sstream> #include <cstdio> int main() { std::istringstream strm ("1"); int i = 0; strm >> i; std::printf ("eof = %d, fail = %d\n", !!strm.eof (), !!strm.fail ()); strm >> i; std::printf ("eof = %d, fail = %d\n", !!strm.eof (), !!strm.fail ()); }
Comments from Jerry Schwarz (c++std-lib-11373):
Jerry Schwarz wrote:
I don't know where (if anywhere) it says it in the standard, but the
formatted extractors are supposed to set failbit if they don't extract
any characters. If they didn't then simple loops like
while (cin >> x);
would loop forever.
Further comments from Martin Sebor:
The question is which part of the extraction should prevent this from happening
by setting failbit when eofbit is already set. It could either be the sentry
object or the extractor. It seems that most implementations have chosen to
set failbit in the sentry [...] so that's the text that will need to be
corrected.
Proposed resolution:
Kona: Possibly NAD. If eofbit is set then good() will return false. We then set ok to false. We believe that the sentry's constructor should always set failbit when ok is false, and we also think the standard already says that. Possibly it could be clearer.
Section: 27.5.2.1 [lib.streambuf.cons] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
The reflector thread starting with c++std-lib-11346 notes that the class template basic_streambuf, along with basic_stringbuf and basic_filebuf, is copy-constructible but that the semantics of the copy constructors are not defined anywhere. Further, different implementations behave differently in this respect: some prevent copy construction of objects of these types by declaring their copy ctors and assignment operators private, others exhibit undefined behavior, while others still give these operations well-defined semantics.
Note that this problem doesn't seem to be isolated to just the three types mentioned above. A number of other types in the library section of the standard provide a compiler-generated copy ctor and assignment operator yet fail to specify their semantics. It's believed that the only types for which this is actually a problem (i.e. types where the compiler-generated default may be inappropriate and may not have been intended) are locale facets. See issue 439.
Proposed resolution:
27.5.2 [lib.streambuf]: Add into the synopsis, public section, just above the destructor declaration:
basic_streambuf(const basic_streambuf& sb); basic_streambuf& operator=(const basic_streambuf& sb);
Insert after 27.5.2.1, paragraph 2:
basic_streambuf(const basic_streambuf& sb);Constructs a copy of sb.
Postcondtions:
eback() == sb.eback() gptr() == sb.gptr() egptr() == sb.egptr() pbase() == sb.pbase() pptr() == sb.pptr() epptr() == sb.epptr() getloc() == sb.getloc()basic_streambuf& operator=(const basic_streambuf& sb);Assigns the data members of sb to this.
Postcondtions:
eback() == sb.eback() gptr() == sb.gptr() egptr() == sb.egptr() pbase() == sb.pbase() pptr() == sb.pptr() epptr() == sb.epptr() getloc() == sb.getloc()Returns: *this.
27.7.1 [lib.stringbuf]:
Option A:Option B:Insert into the basic_stringbuf synopsis in the private section:
basic_stringbuf(const basic_stringbuf&); // not defined basic_stringbuf& operator=(const basic_stringbuf&); // not defined
Insert into the basic_stringbuf synopsis in the public section:
basic_stringbuf(const basic_stringbuf& sb); basic_stringbuf& operator=(const basic_stringbuf& sb);27.7.1.1, insert after paragraph 4:
basic_stringbuf(const basic_stringbuf& sb);Constructs an independent copy of sb as if with sb.str(), and with the openmode that sb was constructed with.
Postcondtions:
str() == sb.str() gptr() - eback() == sb.gptr() - sb.eback() egptr() - eback() == sb.egptr() - sb.eback() pptr() - pbase() == sb.pptr() - sb.pbase() getloc() == sb.getloc()Note: The only requirement on epptr() is that it point beyond the initialized range if an output sequence exists. There is no requirement that epptr() - pbase() == sb.epptr() - sb.pbase().
basic_stringbuf& operator=(const basic_stringbuf& sb);After assignment the basic_stringbuf has the same state as if it were initially copy constructed from sb, except that the basic_stringbuf is allowed to retain any excess capacity it might have, which may in turn effect the value of epptr().
27.8.1.1 [lib.filebuf]
Insert at the bottom of the basic_filebuf synopsis:
private: basic_filebuf(const basic_filebuf&); // not defined basic_filebuf& operator=(const basic_filebuf&); // not defined
[Kona: this is an issue for basic_streambuf itself and for its derived classes. We are leaning toward allowing basic_streambuf to be copyable, and specifying its precise semantics. (Probably the obvious: copying the buffer pointers.) We are less sure whether the streambuf derived classes should be copyable. Howard will write up a proposal.]
[Sydney: Dietmar presented a new argument against basic_streambuf being copyable: it can lead to an encapsulation violation. Filebuf inherits from streambuf. Now suppose you inhert a my_hijacking_buf from streambuf. You can copy the streambuf portion of a filebuf to a my_hijacking_buf, giving you access to the pointers into the filebuf's internal buffer. Perhaps not a very strong argument, but it was strong enough to make people nervous. There was weak preference for having streambuf not be copyable. There was weak preference for having stringbuf not be copyable even if streambuf is. Move this issue to open for now. ]
Rationale:
27.5.2 [lib.streambuf]: The proposed basic_streambuf copy constructor and assignment operator are the same as currently implied by the lack of declarations: public and simply copies the data members. This resolution is not a change but a clarification of the current standard.
27.7.1 [lib.stringbuf]: There are two reasonable options: A) Make basic_stringbuf not copyable. This is likely the status-quo of current implementations. B) Reasonable copy semantics of basic_stringbuf can be defined and implemented. A copyable basic_streambuf is arguably more useful than a non-copyable one. This should be considered as new functionality and not the fixing of a defect. If option B is chosen, ramifications from issue 432 are taken into account.
27.8.1.1 [lib.filebuf]: There are no reasonable copy semantics for basic_filebuf.
Section: 17.4.3.1 [lib.reserved.names] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
It has been suggested that 17.4.3.1, p1 may or may not allow programs to explicitly specialize members of standard templates on user-defined types. The answer to the question might have an impact where library requirements are given using the "as if" rule. I.e., if programs are allowed to specialize member functions they will be able to detect an implementation's strict conformance to Effects clauses that describe the behavior of the function in terms of the other member function (the one explicitly specialized by the program) by relying on the "as if" rule.
Proposed resolution:
Add the following sentence immediately after the text of 17.4.3.1 [lib.reserved.names], p1:
The behavior of a program that declares explicit specializations of any members of class templates or explicit specializations of any member templates of classes or class templates defined in this library is undefined.
[Kona: straw poll was 6-1 that user programs should not be allowed to specialize individual member functions of standard library class templates, and that doing so invokes undefined behavior. Post-Kona: Martin provided wording.]
[Sydney: The LWG agrees that the standard shouldn't permit users to specialize individual member functions unless they specialize the whole class, but we're not sure these words say what we want them to; they could be read as prohibiting the specialization of any standard library class templates. We need to consult with CWG to make sure we use the right wording.]
Section: 27 [lib.input.output] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
A third party test suite tries to exercise istream::ignore(N) with a negative value of N and expects that the implementation will treat N as if it were 0. Our implementation asserts that (N >= 0) holds and aborts the test.
I can't find anything in section 27 that prohibits such values but I don't see what the effects of such calls should be, either (this applies to a number of unformatted input functions as well as some member functions of the basic_streambuf template).
Proposed resolution:
I propose that we add to each function in clause 27 that takes an argument, say N, of type streamsize a Requires clause saying that "N >= 0." The intent is to allow negative streamsize values in calls to precision() and width() but disallow it in calls to streambuf::sgetn(), istream::ignore(), or ostream::write().
[Kona: The LWG agreed that this is probably what we want. However, we need a review to find all places where functions in clause 27 take arguments of type streamsize that shouldn't be allowed to go negative. Martin will do that review.]
Section: 17.3.1.1 [lib.structure.summary] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
The text in 17.3.1.1, p1 says:
"Paragraphs labelled "Note(s):" or "Example(s):" are informative, other
paragraphs are normative."
The library section makes heavy use of paragraphs labeled "Notes(s),"
some of which are clearly intended to be normative (see list 1), while
some others are not (see list 2). There are also those where the intent
is not so clear (see list 3).
List 1 -- Examples of (presumably) normative Notes:
20.4.1.1, p3, 20.4.1.1, p10, 21.3.1, p11, 22.1.1.2, p11, 23.2.1.3, p2,
25.3.7, p3, 26.2.6, p14a, 27.5.2.4.3, p7.
List 2 -- Examples of (presumably) informative Notes:
18.4.1.3, p3, 21.3.5.6, p14, 22.2.1.5.2, p3, 25.1.1, p4, 26.2.5, p1,
27.4.2.5, p6.
List 3 -- Examples of Notes that are not clearly either normative
or informative:
22.1.1.2, p8, 22.1.1.5, p6, 27.5.2.4.5, p4.
None of these lists is meant to be exhaustive.
Proposed resolution:
[Definitely a real problem. The big problem is there's material that doesn't quite fit any of the named paragraph categories (e.g. Effects). Either we need a new kind of named paragraph, or we need to put more material in unnamed paragraphs jsut after the signature. We need to talk to the Project Editor about how to do this. ]
Section: 22.2.2.1.2 [lib.facet.num.get.virtuals] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
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.
Proposed resolution:
[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>
.Section: 26.3.2.4 [lib.valarray.sub] Status: Open Submitter: Martin Sebor Date: 18 Sep 2003
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).
Proposed resolution:
[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.]
Section: 20.1.5 [lib.allocator.requirements], 25 [lib.algorithms] Status: Open Submitter: Matt Austern Date: 20 Sep 2003
Clause 20.1.5 [lib.allocator.requirements] paragraph 4 says that implementations are permitted to supply containers that are unable to cope with allocator instances and that container implementations may assume that all instances of an allocator type compare equal. We gave implementers this latitude as a temporary hack, and eventually we want to get rid of it. What happens when we're dealing with allocators that don't compare equal?
In particular: suppose that v1 and v2 are both objects of type vector<int, my_alloc> and that v1.get_allocator() != v2.get_allocator(). What happens if we write v1.swap(v2)? Informally, three possibilities:
1. This operation is illegal. Perhaps we could say that an implementation is required to check and to throw an exception, or perhaps we could say it's undefined behavior.
2. The operation performs a slow swap (i.e. using three invocations of operator=, leaving each allocator with its original container. This would be an O(N) operation.
3. The operation swaps both the vectors' contents and their allocators. This would be an O(1) operation. That is:
my_alloc a1(...); my_alloc a2(...); assert(a1 != a2); vector<int, my_alloc> v1(a1); vector<int, my_alloc> v2(a2); assert(a1 == v1.get_allocator()); assert(a2 == v2.get_allocator()); v1.swap(v2); assert(a1 == v2.get_allocator()); assert(a2 == v1.get_allocator());
Proposed resolution:
[Kona: This is part of a general problem. We need a paper saying how to deal with unequal allocators in general.]
[pre-Sydney: Howard argues for option 3 in n1599.]
Section: 24.1 [lib.iterator.requirements], 23.1 [lib.container.requirements] Status: Open Submitter: Andy Koenig Date: 16 Dec 2003
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.
Proposed resolution:
[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. ]
Section: 27.8.1.3 [lib.filebuf.members] Status: Open Submitter: Bill Plauger Date: 30 Jan 2004
basic_filebuf *basic_filebuf::open(const char *, ios_base::open_mode);
should be supplemented with the overload:
basic_filebuf *basic_filebuf::open(const wchar_t *, ios_base::open_mode);
Depending on the operating system, one of these forms is fundamental and the other requires an implementation-defined mapping to determine the actual filename.
[Sydney: Yes, we want to allow wchar_t filenames. Bill will provide wording.]
Proposed resolution:
Change from:
basic_filebuf<charT,traits>* open( const char* s, ios_base::openmode mode );Effects: If is_open() != false, returns a null pointer. Otherwise, initializes the filebuf as required. It then opens a file, if possible, whose name is the NTBS s ("as if" by calling std::fopen(s,modstr)).
to:
basic_filebuf<charT,traits>* open( const char* s, ios_base::openmode mode ); basic_filebuf<charT,traits>* open( const wchar_t* ws, ios_base::openmode mode );Effects: If is_open() != false, returns a null pointer. Otherwise, initializes the filebuf as required. It then opens a file, if possible, whose name is the NTBS s ("as if" by calling std::fopen(s,modstr)). For the second signature, the NTBS s is determined from the WCBS ws in an implementation-defined manner.
(NOTE: For a system that "naturally" represents a filename as a WCBS, the NTBS s in the first signature may instead be mapped to a WCBS; if so, it follows the same mapping rules as the first argument to open.)
Rationale:
Slightly controversial, but by a 7-1 straw poll the LWG agreed to move this to Ready. The controversy was because the mapping between wide names and files in a filesystem is implementation defined. The counterargument, which most but not all LWG members accepted, is that the mapping between narrow files names and files is also implemenation defined.
[Lillehammer: Moved back to "open" status, at Beman's urging. (1) Why just basic_filebuf, instead of also basic_fstream (and possibly other things too). (2) Why not also constructors that take std::basic_string? (3) We might want to wait until we see Beman's filesystem library; we might decide that it obviates this.]
Section: 17.4.1.2 [lib.headers] Status: Open Submitter: Bill Plauger Date: 30 Jan 2004
The C++ Standard effectively requires that the traditional C headers (of the form <xxx.h>) be defined in terms of the newer C++ headers (of the form <cxxx>). Clauses 17.4.1.2/4 and D.5 combine to require that:
The rules were left in this form despited repeated and heated objections from several compiler vendors. The C headers are often beyond the direct control of C++ implementors. In some organizations, it's all they can do to get a few #ifdef __cplusplus tests added. Third-party library vendors can perhaps wrap the C headers. But neither of these approaches supports the drastic restructuring required by the C++ Standard. As a result, it is still widespread practice to ignore this conformance requirement, nearly seven years after the committee last debated this topic. Instead, what is often implemented is:
The practical benefit for implementors with the second approach is that they can use existing C library headers, as they are pretty much obliged to do. The practical cost for programmers facing a mix of implementations is that they have to assume weaker rules:
There also exists the possibility of subtle differences due to Koenig lookup, but there are so few non-builtin types defined in the C headers that I've yet to see an example of any real problems in this area.
It is worth observing that the rate at which programmers fall afoul of these differences has remained small, at least as measured by newsgroup postings and our own bug reports. (By an overwhelming margin, the commonest problem is still that programmers include <string> and can't understand why the typename string isn't defined -- this a decade after the committee invented namespace std, nominally for the benefit of all programmers.)
We should accept the fact that we made a serious mistake and rectify it, however belatedly, by explicitly allowing either of the two schemes for declaring C names in headers.
[Sydney: This issue has been debated many times, and will certainly have to be discussed in full committee before any action can be taken. However, the preliminary sentiment of the LWG was in favor of the change. (6 yes, 0 no, 2 abstain) Robert Klarer suggests that we might also want to undeprecate the C-style .h headers.]
Proposed resolution:
Section: 24.1.5 [lib.random.access.iterators] Status: Open Submitter: Daniel Frey Date: 27 Feb 2004
In 24.1.5 [lib.random.access.iterators], table 76 the operational semantics for the expression "r -= n" are defined as "return r += -n". This means, that the expression -n must be valid, which is not the case for unsigned types.
[ Sydney: Possibly not a real problem, since difference type is required to be a signed integer type. However, the wording in the standard may be less clear than we would like. ]
Proposed resolution:
To remove this limitation, I suggest to change the operational semantics for this column to:
{ Distance m = n;
if (m >= 0)
while (m--) --r;
else
while (m++) ++r;
return r; }
Section: 22.2.2.1.2 [lib.facet.num.get.virtuals] Status: Open Submitter: Martin Sebor Date: 16 Mar 2004
When parsing strings of wide-character digits, the standard requires the library to widen narrow-character "atoms" and compare the widened atoms against the characters that are being parsed. Simply narrowing the wide characters would be far simpler, and probably more efficient. The two choices are equivalent except in convoluted test cases, and many implementations already ignore the standard and use narrow instead of widen.
First, I disagree that using narrow() instead of widen() would necessarily have unfortunate performance implications. A possible implementation of narrow() that allows num_get to be implemented in a much simpler and arguably comparably efficient way as calling widen() allows, i.e. without making a virtual call to do_narrow every time, is as follows:
inline char ctype<wchar_t>::narrow (wchar_t wc, char dflt) const { const unsigned wi = unsigned (wc); if (wi > UCHAR_MAX) return typeid (*this) == typeid (ctype<wchar_t>) ? dflt : do_narrow (wc, dflt); if (narrow_ [wi] < 0) { const char nc = do_narrow (wc, dflt); if (nc == dflt) return dflt; narrow_ [wi] = nc; } return char (narrow_ [wi]); }
Second, I don't think the change proposed in the issue (i.e., to use narrow() instead of widen() during Stage 2) would be at all drastic. Existing implementations with the exception of libstdc++ currently already use narrow() so the impact of the change on programs would presumably be isolated to just a single implementation. Further, since narrow() is not required to translate alternate wide digit representations such as those mentioned in issue 303 to their narrow equivalents (i.e., the portable source characters '0' through '9'), the change does not necessarily imply that these alternate digits would be treated as ordinary digits and accepted as part of numbers during parsing. In fact, the requirement in 22.2.1.1.2 [lib.locale.ctype.virtuals], p13 forbids narrow() to translate an alternate digit character, wc, to an ordinary digit in the basic source character set unless the expression (ctype<charT>::is(ctype_base::digit, wc) == true) holds. This in turn is prohibited by the C standard (7.25.2.1.5, 7.25.2.1.5, and 5.2.1, respectively) for charT of either char or wchar_t.
[Sydney: To a large extent this is a nonproblem. As long as you're only trafficking in char and wchar_t we're only dealing with a stable character set, so you don't really need either 'widen' or 'narrow': can just use literals. Finally, it's not even clear whether widen-vs-narrow is the right question; arguably we should be using codecvt instead.]
Proposed resolution:
Change stage 2 so that implementations are permitted to use either technique to perform the comparison:
Section: 22.2.5.1.2 [lib.locale.time.get.virtuals] Status: Ready Submitter: Bill Plauger Date: 23 Mar 2004
Template time_get currently contains difficult, if not impossible, requirements for do_date_order, do_get_time, and do_get_date. All require the implementation to scan a field generated by the %x or %X conversion specifier in strftime. Yes, do_date_order can always return no_order, but that doesn't help the other functions. The problem is that %x can be nearly anything, and it can vary widely with locales. It's horribly onerous to have to parse "third sunday after Michaelmas in the year of our Lord two thousand and three," but that's what we currently ask of do_get_date. More practically, it leads some people to think that if %x produces 10.2.04, we should know to look for dots as separators. Still not easy.
Note that this is the opposite effect from the intent stated in the footnote earlier in this subclause:
"In other words, user confirmation is required for reliable parsing of user-entered dates and times, but machine-generated formats can be parsed reliably. This allows parsers to be aggressive about interpreting user variations on standard formats."
We should give both implementers and users an easier and more reliable alternative: provide a (short) list of alternative delimiters and say what the default date order is for no_order. For backward compatibility, and maximum latitude, we can permit an implementation to parse whatever %x or %X generates, but we shouldn't require it.
Proposed resolution:
In the description:
iter_type do_get_time(iter_type s, iter_type end, ios_base& str, ios_base::iostate& err, tm* t) const;
2 Effects: Reads characters starting at suntil it has extracted those struct tm members, and remaining format characters, used by time_put<>::put to produce the format specified by 'X', or until it encounters an error or end of sequence.
change: 'X'
to: "%H:%M:%S"
Change
iter_type do_get_date(iter_type s, iter_type end, ios_base& str, ios_base::iostate& err, tm* t) const; 4 Effects: Reads characters starting at s until it has extracted those struct tm members, and remaining format characters, used by time_put<>::put to produce the format specified by 'x', or until it encounters an error.
to
iter_type do_get_date(iter_type s, iter_type end, ios_base& str, ios_base::iostate& err, tm* t) const; 4 Effects: Reads characters starting at s until it has extracted those struct tm members, and remaining format characters, used by time_put<>::put to produce one of the following formats, or until it encounters an error. The format depends on the value returned by date_order() as follows: date_order() format no_order "%m/%d/%y" dmy "%d/%m/%y" mdy "%m/%d/%y" ymd "%y/%m/%d" ydm "%y/%d/%m" An implementation may also accept additional implementation-defined formats.[Redmond: agreed that this is a real problem. The solution is probably to match C99's parsing rules. Bill provided wording. ]
Section: 3.6.3 [basic.start.term], 18.3 [lib.support.start.term] Status: Open Submitter: Bill Plauger Date: 23 Mar 2004
3.6.3 Termination spells out in detail the interleaving of static destructor calls and calls to functions registered with atexit. To match this behavior requires intimate cooperation between the code that calls destructors and the exit/atexit machinery. The former is tied tightly to the compiler; the latter is a primitive mechanism inherited from C that traditionally has nothing to do with static construction and destruction. The benefits of intermixing destructor calls with atexit handler calls is questionable at best, and very difficult to get right, particularly when mixing third-party C++ libraries with different third-party C++ compilers and C libraries supplied by still other parties.
I believe the right thing to do is defer all static destruction until after all atexit handlers are called. This is a change in behavior, but one that is likely visible only to perverse test suites. At the very least, we should permit deferred destruction even if we don't require it.
Proposed resolution:
[If this is to be changed, it should probably be changed by CWG. At this point, however, the LWG is leaning toward NAD. Implementing what the standard says is hard work, but it's not impossible and most vendors went through that pain years ago. Changing this behavior would be a user-visible change, and would break at least one real application.]
Section: 20.4.5 [lib.auto.ptr] Status: Open Submitter: Rani Sharoni Date: 7 Dec 2003
TC1 CWG DR #84 effectively made the template<class Y> operator auto_ptr<Y>() member of auto_ptr (20.4.5.3/4) obsolete.
The sole purpose of this obsolete conversion member is to enable copy initialization base from r-value derived (or any convertible types like cv-types) case:
#include <memory> using std::auto_ptr; struct B {}; struct D : B {}; auto_ptr<D> source(); int sink(auto_ptr<B>); int x1 = sink( source() ); // #1 EDG - no suitable copy constructor
The excellent analysis of conversion operations that was given in the final auto_ptr proposal (http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/1997/N1128.pdf) explicitly specifies this case analysis (case 4). DR #84 makes the analysis wrong and actually comes to forbid the loophole that was exploited by the auto_ptr designers.
I didn't encounter any compliant compiler (e.g. EDG, GCC, BCC and VC) that ever allowed this case. This is probably because it requires 3 user defined conversions and in fact current compilers conform to DR #84.
I was surprised to discover that the obsolete conversion member actually has negative impact of the copy initialization base from l-value derived case:
auto_ptr<D> dp; int x2 = sink(dp); // #2 EDG - more than one user-defined conversion applies
I'm sure that the original intention was allowing this initialization using the template<class Y> auto_ptr(auto_ptr<Y>& a) constructor (20.4.5.1/4) but since in this copy initialization it's merely user defined conversion (UDC) and the obsolete conversion member is UDC with the same rank (for the early overloading stage) there is an ambiguity between them.
Removing the obsolete member will have impact on code that explicitly invokes it:
int y = sink(source().operator auto_ptr<B>());
IMHO no one ever wrote such awkward code and the reasonable workaround for #1 is:
int y = sink( auto_ptr<B>(source()) );
I was even more surprised to find out that after removing the obsolete conversion member the initialization was still ill-formed: int x3 = sink(dp); // #3 EDG - no suitable copy constructor
This copy initialization semantically requires copy constructor which means that both template conversion constructor and the auto_ptr_ref conversion member (20.4.5.3/3) are required which is what was explicitly forbidden in DR #84. This is a bit amusing case in which removing ambiguity results with no candidates.
I also found exception safety issue with auto_ptr related to auto_ptr_ref:
int f(auto_ptr<B>, std::string); auto_ptr<B> source2(); // string constructor throws while auto_ptr_ref // "holds" the pointer int x4 = f(source2(), "xyz"); // #4
The theoretic execution sequence that will cause a leak:
According to 20.4.5.3/3 and 20.4.5/2 the auto_ptr_ref conversion member returns auto_ptr_ref<Y> that holds *this and this is another defect since the type of *this is auto_ptr<X> where X might be different from Y. Several library vendors (e.g. SGI) implement auto_ptr_ref<Y> with Y* as member which is much more reasonable. Other vendor implemented auto_ptr_ref as defectively required and it results with awkward and catastrophic code: int oops = sink(auto_ptr<B>(source())); // warning recursive on all control paths
Dave Abrahams noticed that there is no specification saying that auto_ptr_ref copy constructor can't throw.
My proposal comes to solve all the above issues and significantly simplify auto_ptr implementation. One of the fundamental requirements from auto_ptr is that it can be constructed in an intuitive manner (i.e. like ordinary pointers) but with strict ownership semantics which yield that source auto_ptr in initialization must be non-const. My idea is to add additional constructor template with sole propose to generate ill-formed, diagnostic required, instance for const auto_ptr arguments during instantiation of declaration. This special constructor will not be instantiated for other types which is achievable using 14.8.2/2 (SFINAE). Having this constructor in hand makes the constructor template<class Y> auto_ptr(auto_ptr<Y> const&) legitimate since the actual argument can't be const yet non const r-value are acceptable.
This implementation technique makes the "private auxiliary class" auto_ptr_ref obsolete and I found out that modern C++ compilers (e.g. EDG, GCC and VC) consume the new implementation as expected and allow all intuitive initialization and assignment cases while rejecting illegal cases that involve const auto_ptr arguments.
The proposed auto_ptr interface:
namespace std { template<class X> class auto_ptr { public: typedef X element_type; // 20.4.5.1 construct/copy/destroy: explicit auto_ptr(X* p=0) throw(); auto_ptr(auto_ptr&) throw(); template<class Y> auto_ptr(auto_ptr<Y> const&) throw(); auto_ptr& operator=(auto_ptr&) throw(); template<class Y> auto_ptr& operator=(auto_ptr<Y>) throw(); ~auto_ptr() throw(); // 20.4.5.2 members: X& operator*() const throw(); X* operator->() const throw(); X* get() const throw(); X* release() throw(); void reset(X* p=0) throw(); private: template<class U> auto_ptr(U& rhs, typename unspecified_error_on_const_auto_ptr<U>::type = 0); }; }
One compliant technique to implement the unspecified_error_on_const_auto_ptr helper class is using additional private auto_ptr member class template like the following:
template<typename T> struct unspecified_error_on_const_auto_ptr; template<typename T> struct unspecified_error_on_const_auto_ptr<auto_ptr<T> const> { typedef typename auto_ptr<T>::const_auto_ptr_is_not_allowed type; };
There are other techniques to implement this helper class that might work better for different compliers (i.e. better diagnostics) and therefore I suggest defining its semantic behavior without mandating any specific implementation. IMO, and I didn't found any compiler that thinks otherwise, 14.7.1/5 doesn't theoretically defeat the suggested technique but I suggest verifying this with core language experts.
Further changes in standard text:
Remove section 20.4.5.3
Change 20.4.5/2 to read something like: Initializing auto_ptr<X> from const auto_ptr<Y> will result with unspecified ill-formed declaration that will require unspecified diagnostic.
Change 20.4.5.1/4,5,6 to read:
template<class Y> auto_ptr(auto_ptr<Y> const& a) throw();
4 Requires: Y* can be implicitly converted to X*.
5 Effects: Calls const_cast<auto_ptr<Y>&>(a).release().
6 Postconditions: *this holds the pointer returned from a.release().
Change 20.4.5.1/10
template<class Y> auto_ptr& operator=(auto_ptr<Y> a) throw();
10 Requires: Y* can be implicitly converted to X*. The expression delete get() is well formed.
LWG TC DR #127 is obsolete.
Notice that the copy constructor and copy assignment operator should remain as before and accept non-const auto_ptr& since they have effect on the form of the implicitly declared copy constructor and copy assignment operator of class that contains auto_ptr as member per 12.8/5,10:
struct X { // implicit X(X&) // implicit X& operator=(X&) auto_ptr<D> aptr_; };
In most cases this indicates about sloppy programming but preserves the current auto_ptr behavior.
Dave Abrahams encouraged me to suggest fallback implementation in case that my suggestion that involves removing of auto_ptr_ref will not be accepted. In this case removing the obsolete conversion member to auto_ptr<Y> and 20.4.5.3/4,5 is still required in order to eliminate ambiguity in legal cases. The two constructors that I suggested will co exist with the current members but will make auto_ptr_ref obsolete in initialization contexts. auto_ptr_ref will be effective in assignment contexts as suggested in DR #127 and I can't see any serious exception safety issues in those cases (although it's possible to synthesize such). auto_ptr_ref<X> semantics will have to be revised to say that it strictly holds pointer of type X and not reference to an auto_ptr for the favor of cases in which auto_ptr_ref<Y> is constructed from auto_ptr<X> in which X is different from Y (i.e. assignment from r-value derived to base).
Proposed resolution:
[Redmond: punt for the moment. We haven't decided yet whether we want to fix auto_ptr for C++-0x, or remove it and replace it with move_ptr and unique_ptr.]
Section: 23.2.4 [lib.vector], 23.3.1 [lib.map] Status: Ready Submitter: Thorsten Ottosen Date: 12 May 2004
To add slightly more convenience to vector<T> and map<Key,T> we should consider to add
Rationale:
Proposed resolution:
In 23.2.4 [lib.vector], add the following to the vector synopsis after "element access" and before "modifiers":
// [lib.vector.data] data access pointer data(); const_pointer data() const;
Add a new subsection of 23.2.4 [lib.vector]:
23.2.4.x vector data access
pointer data(); const_pointer data() const;Returns: A pointer such that [data(), data() + size()) is a valid range. For a non-empty vector, data() == &front().
Complexity: Constant time.
Throws: Nothing.
In 23.3.1 [lib.map], add the following to the map synopsis immediately after the line for operator[]:
T& at(const key_type& x); const T& at(const key_type& x) const;
Add the following to 23.3.1.2 [lib.map.access]:
T& at(const key_type& x); const T& at(const key_type& x) const;Returns: A reference to the element whose key is equivalent to x, if such an element is present in the map.
Throws: out_of_range if no such element is present.
Rationale:
Neither of these additions provides any new functionality but the LWG agreed that they are convenient, especially for novices. The exception type chosen for at, std::out_of_range, was chosen to match vector::at.
Section: 17.4.1.2 [lib.headers] Status: Ready Submitter: Steve Clamage Date: 3 Jun 2004
C header <iso646.h> defines macros for some operators, such as not_eq for !=.
Section 17.4.1.2 [lib.headers] "Headers" says that except as noted in clauses 18 through 27, the <cname> C++ header contents are the same as the C header <name.h>. In particular, table 12 lists <ciso646> as a C++ header.
I don't find any other mention of <ciso646>, or any mention of <iso646.h>, in clauses 17 thorough 27. That implies that the contents of <ciso646> are the same as C header <iso646.h>.
Annex C (informative, not normative) in [diff.header.iso646.h] C.2.2.2 "Header <iso646.h>" says that the alternative tokens are not defined as macros in <ciso646>, but does not mention the contents of <iso646.h>.
I don't find any normative text to support C.2.2.2.
Proposed resolution:
Add to section 17.4.1.2 Headers [lib.headers] a new paragraph after paragraph 6 (the one about functions must be functions):
Identifiers that are keywords or operators in C++ shall not be defined as macros in C++ standard library headers. [Footnote:In particular, including the standard header <iso646.h> or <ciso646> has no effect.
[post-Redmond: Steve provided wording.]
Section: 21.3.1 [lib.string.cons] Status: Open Submitter: Daniel Frey Date: 10 Jun 2004
Today, my colleagues and me wasted a lot of time. After some time, I found the problem. It could be reduced to the following short example:
#include <string> int main() { std::string( 0 ); }
The problem is that the tested compilers (GCC 2.95.2, GCC 3.3.1 and Comeau online) compile the above without errors or warnings! The programs (at least for the GCC) resulted in a SEGV.
I know that the standard explicitly states that the ctor of string requires a char* which is not zero. STLs could easily detect the above case with a private ctor for basic_string which takes a single 'int' argument. This would catch the above code at compile time and would not ambiguate any other legal ctors.
Proposed resolution:
[Redmond: No great enthusiasm for doing this. If we do, however, we want to do it for all places that take charT* pointers, not just the single-argument constructor. The other question is whether we want to catch this at compile time (in which case we catch the error of a literal 0, but not an expression whose value is a null pointer), at run time, or both.]
Section: 21.1.3.1 [lib.char.traits.specializations.char] Status: Ready Submitter: Martin Sebor Date: 28 Jun 2004
Table 37 describes the requirements on Traits::compare() in terms of those on Traits::lt(). 21.1.3.1, p6 requires char_traits<char>::lt() to yield the same result as operator<(char, char).
Most, if not all, implementations of char_traits<char>::compare() call memcmp() for efficiency. However, the C standard requires both memcmp() and strcmp() to interpret characters under comparison as unsigned, regardless of the signedness of char. As a result, all these char_traits implementations fail to meet the requirement imposed by Table 37 on compare() when char is signed.
Read email thread starting with c++std-lib-13499 for more.
Proposed resolution:
Change 21.1.3.1, p6 from
The two-argument members assign, eq, and lt are defined identically to the built-in operators =, ==, and < respectively.
to
The two-argument member assign is defined identically to the built-in operator =. The two argument members eq and lt are defined identically to the built-in operators == and < for type unsigned char.
[Redmond: The LWG agreed with this general direction, but we also need to change eq to be consistent with this change. Post-Redmond: Martin provided wording.]
Section: 27.4.4.3 [lib.iostate.flags] Status: Ready Submitter: Martin Sebor Date: 28 Jun 2004
The program below is required to compile but when run it typically produces unexpected results due to the user-defined conversion from std::cout or any object derived from basic_ios to void*.
#include <cassert> #include <iostream> int main () { assert (std::cin.tie () == std::cout); // calls std::cout.ios::operator void*() }
Proposed resolution:
Replace std::basic_ios<charT, traits>::operator void*() with another conversion operator to some unspecified type that is guaranteed not to be convertible to any other type except for bool (a pointer-to-member might be one such suitable type). In addition, make it clear that the pointer type need not be a pointer to a complete type and when non-null, the value need not be valid.
Specifically, change in [lib.ios] the signature of
operator void*() const;
to
operator unspecified-bool-type() const;
and change [lib.iostate.flags], p1 from
operator void*() const;
to
operator unspecified-bool-type() const; -1- Returns: if fail() then a value that will evaluate false in a boolean context; otherwise a value that will evaluate true in a boolean context. The value type returned shall not be convertible to int. -2- [Note: This conversion can be used in contexts where a bool is expected (e.g., an if condition); however, implicit conversions (e.g., to int) that can occur with bool are not allowed, eliminating some sources of user error. One possible implementation choice for this type is pointer-to-member. - end note]
[Redmond: 5-4 straw poll in favor of doing this.]
[Lillehammer: Doug provided revised wording for "unspecified-bool-type".]
Section: 23 [lib.containers] Status: Open Submitter: Martin Sebor Date: 28 Jun 2004
The standard doesn't prohibit the destructors (or any other special functions) of containers' elements invoked from a member function of the container from "recursively" calling the same (or any other) member function on the same container object, potentially while the container is in an intermediate state, or even changing the state of the container object while it is being modified. This may result in some surprising (i.e., undefined) behavior.
Read email thread starting with c++std-lib-13637 for more.
Proposed resolution:
Add to Container Requirements the following new paragraph:
Unless otherwise specified, the behavior of a program that invokes a container member function f from a member function g of the container's value_type on a container object c that called g from its mutating member function h, is undefined. I.e., if v is an element of c, directly or indirectly calling c.h() from v.g() called from c.f(), is undefined.
[Redmond: This is a real issue, but it's probably a clause 17 issue, not clause 23. We get the same issue, for example, if we try to destroy a stream from one of the stream's callback functions.]
Section: 18.6.1 [lib.exception] Status: Open Submitter: Martin Sebor Date: 28 Jun 2004
[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.]
Proposed resolution:
Section: 22.2.1.1 [lib.locale.ctype] Status: Open Submitter: Martin Sebor Date: 1 Jul 2004
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.
Proposed resolution:
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.
Section: 27.6.2.5.4 [lib.ostream.inserters.character] Status: Ready Submitter: Martin Sebor Date: 1 Jul 2004
I think Footnote 297 is confused. The paragraph it applies to seems quite clear in that widen() is only called if the object is not a char stream (i.e., not basic_ostream<char>), so it's irrelevant what the value of widen(c) is otherwise.
Proposed resolution:
I propose to strike the Footnote.
Section: 25.1.1 [lib.alg.foreach] Status: Review Submitter: Stephan T. Lavavej, Jaakko Jarvi Date: 9 Jul 2004
It is not clear whether the function object passed to for_each is allowed to modify the elements of the sequence being iterated over.
for_each is classified without explanation in [lib.alg.nonmodifying], "25.1 Non-modifying sequence operations". 'Non-modifying sequence operation' is never defined.
25(5) says: "If an algorithm's Effects section says that a value pointed to by any iterator passed as an argument is modified, then that algorithm has an additional type requirement: The type of that argument shall satisfy the requirements of a mutable iterator (24.1)."
for_each's Effects section does not mention whether arguments can be modified:
"Effects: Applies f to the result of dereferencing every iterator in the range [first, last), starting from first and proceeding to last - 1."
Every other algorithm in [lib.alg.nonmodifying] is "really" non-modifying in the sense that neither the algorithms themselves nor the function objects passed to the algorithms may modify the sequences or elements in any way. This DR affects only for_each.
We suspect that for_each's classification in "non-modifying sequence operations" means that the algorithm itself does not inherently modify the sequence or the elements in the sequence, but that the function object passed to it may modify the elements it operates on.
The original STL document by Stepanov and Lee explicitly prohibited the function object from modifying its argument. The "obvious" implementation of for_each found in several standard library implementations, however, does not impose this restriction. As a result, we suspect that the use of for_each with function objects that modify their arguments is wide-spread. If the restriction was reinstated, all such code would become non-conforming. Further, none of the other algorithms in the Standard could serve the purpose of for_each (transform does not guarantee the order in which its function object is called).
We suggest that the standard be clarified to explicitly allow the function object passed to for_each modify its argument.
Proposed resolution:
Add a nonnormative note to the Effects in 25.1.1 [lib.alg.foreach]: If the type of 'first' satisfies the requirements of a mutable iterator, 'f' may apply nonconstant functions through the dereferenced iterators passed to it.
Rationale:
The LWG believes that nothing in the standard prohibits function objects that modify the sequence elements. The problem is that for_each is in a secion entitled "nonmutating algorithms", and the title may be confusing. A nonnormative note should clarify that.
Section: 24.1.3 [lib.forward.iterators] Status: Review Submitter: Dave Abrahams Date: 11 Jul 2004
The Forward Iterator requirements table contains the following:
expression return type operational precondition semantics ========== ================== =========== ========================== a->m U& if X is mutable, (*a).m pre: (*a).m is well-defined. otherwise const U& r->m U& (*r).m pre: (*r).m is well-defined.
The second line may be unnecessary. Paragraph 11 of [lib.iterator.requirements] says:
In the following sections, a and b denote values of type const X, n denotes a value of the difference type Distance, u, tmp, and m denote identifiers, r denotes a value of X&, t denotes a value of value type T, o denotes a value of some type that is writable to the output iterator.
Because operators can be overloaded on an iterator's const-ness, the current requirements allow iterators to make many of the operations specified using the identifiers a and b invalid for non-const iterators.
Related issue: 477
Proposed resolution:
Remove the "r->m" line from the Forward Iterator requirements table. Change
"const X"
to
"X or const X"
in paragraph 11 of [lib.iterator.requirements].
Section: 23.1 [lib.container.requirements] Status: Open Submitter: Herb Sutter Date: 1 Aug 2004
Nothing in the standard appears to make this program ill-formed:
struct C { void* operator new( size_t s ) { return ::operator new( s ); } // NOTE: this hides in-place and nothrow new }; int main() { vector<C> v; v.push_back( C() ); }
Is that intentional? We should clarify whether or not we intended to require containers to support types that define their own special versions of operator new.
Proposed resolution:
Section: 20.2.2 [lib.pairs], 25.2.2 [lib.alg.swap] Status: Open Submitter: Andrew Koenig Date: 14 Sep 2004
(Based on recent comp.std.c++ discussion)
Pair (and tuple) should specialize std::swap to work in terms of std::swap on their components. For example, there's no obvious reason why swapping two objects of type pair<vector<int>, list<double> > should not take O(1).
Proposed resolution:
[Lillehammer: We agree it should be swappable. Howard will provide wording.]
Section: 24.1.1 [lib.input.iterators] Status: Open Submitter: Chris Date: 16 Sep 2004
From comp.std.c++:
I note that given an input iterator a for type T, then *a only has to be "convertable to T", not actually of type T.
Firstly, I can't seem to find an exact definition of "convertable to T". While I assume it is the obvious definition (an implicit conversion), I can't find an exact definition. Is there one?
Slightly more worryingly, there doesn't seem to be any restriction on the this type, other than it is "convertable to T". Consider two input iterators a and b. I would personally assume that most people would expect *a==*b would perform T(*a)==T(*b), however it doesn't seem that the standard requires that, and that whatever type *a is (call it U) could have == defined on it with totally different symantics and still be a valid inputer iterator.
Is this a correct reading? When using input iterators should I write T(*a) all over the place to be sure that the object i'm using is the class I expect?
This is especially a nuisance for operations that are defined to be "convertible to bool". (This is probably allowed so that implementations could return say an int and avoid an unnessary conversion. However all implementations I have seen simply return a bool anyway. Typical implemtations of STL algorithms just write things like while(a!=b && *a!=0). But strictly speaking, there are lots of types that are convertible to T but that also overload the appropriate operators so this doesn't behave as expected.
If we want to make code like this legal (which most people seem to expect), then we'll need to tighten up what we mean by "convertible to T".
Proposed resolution:
[Lillehammer: The first part is NAD, since "convertible" is well-defined in core. The second part is basically about pathological overloads. It's a minor problem but a real one. So leave open for now, hope we solve it as part of iterator redesign.]
Section: 24.1.2 [lib.output.iterators] Status: Open Submitter: Chris Date: 13 Oct 2004
The note on 24.1.2 Output iterators insufficently 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?
Proposed resolution:
[Lillehammer: Real issue. There are lots of constraints we intended but didn't specify. Should be solved as part of iterator redesign.]
Section: 25.2.10 [lib.alg.rotate] Status: Open Submitter: Howard Hinnant Date: 22 Nov 2004
rotate takes 3 iterators: first, middle and last which point into a sequence, and rearranges the sequence such that the subrange [middle, last) is now at the beginning of the sequence and the subrange [first, middle) follows. The return type is void.
In many use cases of rotate, the client needs to know where the subrange [first, middle) starts after the rotate is performed. This might look like:
rotate(first, middle, last); Iterator i = advance(first, distance(middle, last));
Unless the iterators are random access, the computation to find the start of the subrange [first, middle) has linear complexity. However, it is not difficult for rotate to return this information with negligible additional computation expense. So the client could code:
Iterator i = rotate(first, middle, last);
and the resulting program becomes significantly more efficient.
While the backwards compatibility hit with this change is not zero, it is very small (similar to that of lwg 130), and there is a significant benefit to the change.
Proposed resolution:
In 25p2, change:
template<class ForwardIterator> void rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last);
to:
template<class ForwardIterator> ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last);
In 25.2.10, change:
template<class ForwardIterator> void rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last);
to:
template<class ForwardIterator> ForwardIterator rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last);
In 25.2.10 insert a new paragraph after p1:
Returns: first + (last - middle).
[ The LWG agrees with this idea, but has one quibble: we want to make sure not to give the impression that the function "advance" is actually called, just that the nth iterator is returned. (Calling advance is observable behavior, since users can specialize it for their own iterators.) Howard will provide wording. ]
[Howard provided wording for mid-meeting-mailing Jun. 2005.]
Section: 23 [lib.containers], 24 [lib.iterators], 25 [lib.algorithms] Status: Open Submitter: Thomas Mang Date: 12 Dec 2004
Various clauses other than clause 25 make use of iterator arithmetic not supported by the iterator category in question. Algorithms in clause 25 are exceptional because of 25 [lib.algorithms], paragraph 9, but this paragraph does not provide semantics to the expression "iterator - n", where n denotes a value of a distance type between iterators.
1) Examples of current wording:
Current wording outside clause 25:
23.2.2.4 [lib.list.ops], paragraphs 19-21: "first + 1", "(i - 1)", "(last - first)" 23.3.1.1 [lib.map.cons], paragraph 4: "last - first" 23.3.2.1 [lib.multimap.cons], paragraph 4: "last - first" 23.3.3.1 [lib.set.cons], paragraph 4: "last - first" 23.3.4.1 [lib.multiset.cons], paragraph 4: "last - first" 24.4.1 [lib.reverse.iterators], paragraph 1: "(i - 1)"
[Important note: The list is not complete, just an illustration. The same issue might well apply to other paragraphs not listed here.]
None of these expressions is valid for the corresponding iterator category.
Current wording in clause 25:
25.1.1 [lib.alg.foreach], paragraph 1: "last - 1" 25.1.3 [lib.alg.find.end], paragraph 2: "[first1, last1 - (last2-first2))" 25.2.8 [lib.alg.unique], paragraph 1: "(i - 1)" 25.2.8 [lib.alg.unique], paragraph 5: "(i - 1)"
However, current wording of 25 [lib.algorithms], paragraph 9 covers neither of these four cases:
Current wording of 25 [lib.algorithms], paragraph 9:
"In the description of the algorithms operator + and - are used for some of the iterator categories for which they do not have to be defined. In these cases the semantics of a+n is the same as that of
{X tmp = a; advance(tmp, n); return tmp; }
and that of b-a is the same as of return distance(a, b)"
This paragrpah does not take the expression "iterator - n" into account, where n denotes a value of a distance type between two iterators [Note: According to current wording, the expression "iterator - n" would be resolved as equivalent to "return distance(n, iterator)"]. Even if the expression "iterator - n" were to be reinterpreted as equivalent to "iterator + -n" [Note: This would imply that "a" and "b" were interpreted implicitly as values of iterator types, and "n" as value of a distance type], then 24.3.4/2 interfers because it says: "Requires: n may be negative only for random access and bidirectional iterators.", and none of the paragraphs quoted above requires the iterators on which the algorithms operate to be of random access or bidirectional category.
2) Description of intended behavior:
For the rest of this Defect Report, it is assumed that the expression "iterator1 + n" and "iterator1 - iterator2" has the semantics as described in current 25 [lib.algorithms], paragraph 9, but applying to all clauses. The expression "iterator1 - n" is equivalent to an result-iterator for which the expression "result-iterator + n" yields an iterator denoting the same position as iterator1 does. The terms "iterator1", "iterator2" and "result-iterator" shall denote the value of an iterator type, and the term "n" shall denote a value of a distance type between two iterators.
All implementations known to the author of this Defect Report comply with these assumptions. No impact on current code is expected.
3) Proposed fixes:
Change 25 [lib.algorithms], paragraph 9 to:
"In the description of the algorithms operator + and - are used for some of the iterator categories for which they do not have to be defined. In this paragraph, a and b denote values of an iterator type, and n denotes a value of a distance type between two iterators. In these cases the semantics of a+n is the same as that of
{X tmp = a; advance(tmp, n); return tmp; }
,the semantics of a-n denotes the value of an iterator i for which the following condition holds: advance(i, n) == a, and that of b-a is the same as of return distance(a, b)".
Comments to the new wording:
a) The wording " In this paragraph, a and b denote values of an iterator type, and n denotes a value of a distance type between two iterators." was added so the expressions "b-a" and "a-n" are distinguished regarding the types of the values on which they operate. b) The wording ",the semantics of a-n denotes the value of an iterator i for which the following condition holds: advance(i, n) == a" was added to cover the expression 'iterator - n'. The wording "advance(i, n) == a" was used to avoid a dependency on the semantics of a+n, as the wording "i + n == a" would have implied. However, such a dependency might well be deserved. c) DR 225 is not considered in the new wording.
Proposed fixes regarding invalid iterator arithmetic expressions outside clause 25:
Either a) Move modified 25 [lib.algorithms], paragraph 9 (as proposed above) before any current invalid iterator arithmetic expression. In that case, the first sentence of 25 [lib.algorithms], paragraph 9, need also to be modified and could read: "For the rest of this International Standard, ...." / "In the description of the following clauses including this ...." / "In the description of the text below ..." etc. - anyways substituting the wording "algorithms", which is a straight reference to clause 25. In that case, 25 [lib.algorithms] paragraph 9 will certainly become obsolete. Alternatively, b) Add an appropiate paragraph similar to resolved 25 [lib.algorithms], paragraph 9, to the beginning of each clause containing invalid iterator arithmetic expressions. Alternatively, c) Fix each paragraph (both current wording and possible resolutions of DRs) containing invalid iterator arithmetic expressions separately.
5) References to other DRs:
See DR 225. See DR 237. The resolution could then also read "Linear in last - first".
Proposed resolution:
[Lillehammer: Minor issue, but real. We have a blanket statement about this in 25/11. But (a) it should be in 17, not 25; and (b) it's not quite broad enough, because there are some arithmetic expressions it doesn't cover. Bill will provide wording.]
Section: 22 [lib.localization] Status: Review Submitter: Beman Dawes Date: 10 Jan 2005
It appears that there are no requirements specified for many of the template parameters in clause 22. It looks like this issue has never come up, except perhaps for Facet.
Clause 22 isn't even listed in 17.3.2.1 [lib.type.descriptions], either, which is the wording that allows requirements on template parameters to be identified by name.
So one issue is that 17.3.2.1 [lib.type.descriptions] Should be changed to cover clause 22. A better change, which will cover us in the future, would be to say that it applies to all the library clauses. Then if a template gets added to any library clause we are covered.
charT, InputIterator, and other names with requirements defined elsewhere are fine, assuming the 17.3.2.1 [lib.type.descriptions] fix. But there are a few template arguments names which I don't think have requirements given elsewhere:
Proposed resolution:
Change 17.3.2.1 [lib.type.descriptions], paragraph 1, from:
The Requirements subclauses may describe names that are used to specify constraints on template arguments.153) These names are used in clauses 20, 23, 25, and 26 to describe the types that may be supplied as arguments by a C++ program when instantiating template components from the library.
to:
The Requirements subclauses may describe names that are used to specify constraints on template arguments.153) These names are used in library clauses to describe the types that may be supplied as arguments by a C++ program when instantiating template components from the library.
In the front matter of class 22, locales, add:
Template parameter types internT and externT shall meet the requirements of charT (described in 21 [lib.strings]).
Rationale:
Again, a blanket clause isn't blanket enough. Also, we've got a couple of names that we don't have blanket requirement statements for. The only issue is what to do about stateT. This wording is thin, but probably adequate.
Section: 23.2.5 [lib.vector.bool] Status: Ready Submitter: richard@ex-parrot.com Date: 10 Feb 2005
In the synopsis of the std::vector<bool> specialisation in 23.2.5 [lib.vector.bool], the non-template assign() function has the signature
void assign( size_type n, const T& t );
The type, T, is not defined in this context.
Proposed resolution:
Replace "T" with "value_type".
Section: 18.2.1.2 [lib.numeric.limits.members] Status: Review Submitter: Martin Sebor Date: 2 Mar 2005
18.2.1.2, p59 says this much about the traps member of numeric_limits:
static const bool traps;
-59- true if trapping is implemented for the type.204)
Footnote 204: Required by LIA-1.
It's not clear what is meant by "is implemented" here.
In the context of floating point numbers it seems reasonable to expect to be able to use traps to determine whether a program can "safely" use infinity(), quiet_NaN(), etc., in arithmetic expressions, that is without causing a trap (i.e., on UNIX without having to worry about getting a signal). When traps is true, I would expect any of the operations in section 7 of IEEE 754 to cause a trap (and my program to get a SIGFPE). So, for example, on Alpha, I would expect traps to be true by default (unless I compiled my program with the -ieee option), false by default on most other popular architectures, including IA64, MIPS, PA-RISC, PPC, SPARC, and x86 which require traps to be explicitly enabled by the program.
Another possible interpretation of p59 is that traps should be true on any implementation that supports traps regardless of whether they are enabled by default or not. I don't think such an interpretation makes the traps member very useful, even though that is how traps is implemented on several platforms. It is also the only way to implement traps on platforms that allow programs to enable and disable trapping at runtime.
Proposed resolution:
Change p59 to read:
True if, at program startup, there exists a value of the type that would cause an arithmetic operation using that value to trap.
Rationale:
Real issue, since trapping can be turned on and off. Unclear what a static query can say about a dynamic issue. The real advice we should give users is to use cfenv for these sorts of queries. But this new proposed resolution is at least consistent and slightly better than nothing.
Section: 25.2.12 [lib.alg.partitions] Status: New Submitter: Sean Parent, Joe Gottman Date: 4 May 2005
Problem: The iterator requirements for partition() and stable_partition() [25.2.12] are listed as BidirectionalIterator, however, there are efficient algorithms for these functions that only require ForwardIterator that have been known since before the standard existed. The SGI implementation includes these (see http://www.sgi.com/tech/stl/partition.html and http://www.sgi.com/tech/stl/stable_partition.html).
Proposed resolution:
Change 25.2.12 from
template<class BidirectionalIterator, class Predicate> BidirectionalIterator partition(BidirectionalIterato r first, BidirectionalIterator last, Predicate pred);
to
template<class ForwardIterator, class Predicate> ForwardIterator partition(ForwardIterator first, ForwardIterator last, Predicate pred);
Change the complexity from
At most (last - first)/2 swaps are done. Exactly (last - first) applications of the predicate are done.
to
If ForwardIterator is a bidirectional_iterator, at most (last - first)/2 swaps are done; otherwise at most (last - first) swaps are done. Exactly (last - first) applications of the predicate are done.
Section: 25.3.1.2 [lib.stable.sort] Status: New Submitter: Prateek Karandikar Date: 12 Apr 2005
17.3.1.1 Summary
1 The Summary provides a synopsis of the category, and introduces the first-level subclauses. Each subclause also provides a summary, listing the headers specified in the subclause and the library entities provided in each header.
2 Paragraphs labelled "Note(s):" or "Example(s):" are informative, other paragraphs are normative.
So this means that a "Notes" paragraph wouldn't be normative.
25.3.1.2 stable_sort
template<class RandomAccessIterator> void stable_sort(RandomAccessIterat or first, RandomAccessIterator last); template<class RandomAccessIterator, class Compare> void stable_sort(RandomAccessIterat or first, RandomAccessIterator last, Compare comp);1 Effects: Sorts the elements in the range [first, last).
2 Complexity: It does at most N(log N)^2 (where N == last - first) comparisons; if enough extra memory is available, it is N log N.
3 Notes: Stable: the relative order of the equivalent elements is preserved.
The Notes para is informative, and nowhere else is stability mentioned above.
Also, I just searched for the word "stable" in my copy of the Standard. and the phrase "Notes: Stable: the relative order of the elements..." is repeated several times in the Standard library clauses for describing various functions. How is it that stability is talked about in the informative paragraph? Or am I missing something obvious?
Proposed resolution:
Section: 22.2.1.5.2 [lib.locale.codecvt.virtuals] Status: New Submitter: Krzysztof ¯elechowski Date: 24 May 2005
Contradiction.
Proposed resolution:
Section: 20.3.3 [lib.comparisons] Status: New Submitter: Me <anti_spam_email2003@yahoo.com> Date: 7 Jun 2005
"For templates greater, less, greater_equal, and less_equal, the specializations for any pointer type yield a total order, even if the built-in operators <, >, <=, >= do not."
The standard should do much better than guarantee that these provide a total order, it should guarantee that it can be used to test if memory overlaps, i.e. write a portable memmove. You can imagine a platform where the built-in operators use a uint32_t comparison (this tests for overlap on this platform) but the less<T*> functor is allowed to be defined to use a int32_t comparison. On this platform, if you use std::less with the intent of making a portable memmove, comparison on an array that straddles the 0x7FFFFFFF/0x8000000 boundary can give incorrect results.
Proposed resolution:
Add a footnote to 20.3.3/8 saying:
Given a p1 and p2 such that p1 points to N objects of type T and p2 points to M objects of type T. If [p1,p1+N) does not overlap [p2,p2+M), less returns the same value when comparing all pointers in [p1,p1+N) to all pointers in [p2,p2+M). Otherwise, there is a value Q and a value R such that less returns the same value when comparing all pointers in [p1,p1+Q) to all pointers in [p2,p2+R) and an opposite value when comparing all pointers in [p1+Q,p1+N) to all pointers in [p2+R,p2+M). For the sake of completeness, the null pointer value (4.10) for T is considered to be an array of 1 object that doesn't overlap with any non-null pointer to T. less_equal, greater, greater_equal, equal_to, and not_equal_to give the expected results based on the total ordering semantics of less. For T of void, treat it as having similar semantics as T of char i.e. less<cv T*>(a, b) gives the same results as less<cv void*>(a, b) which gives the same results as less<cv char*>((cv char*)(cv void*)a, (cv char*)(cv void*)b).
I'm also thinking there should be a footnote to 20.3.3/1 saying that if A and B are similar types (4.4/4), comp<A>(a,b) returns the same value as comp<B>(a,b) (where comp is less, less_equal, etc.). But this might be problematic if there is some really funky operator overloading going on that does different things based on cv (that should be undefined behavior if somebody does that though). This at least should be guaranteed for all POD types (especially pointers) that use the built-in comparison operators.
Section: 22.1.1.1.1 [lib.locale.category] Status: New Submitter: Christopher Conrade Zseleghovski Date: 7 Jun 2005
Motivation:
This requirement seems obvious to me, it is the essence of code modularity. I have complained to Mr. Plauger that the Dinkumware library does not observe this principle but he objected that this behaviour is not covered in the standard.
Proposed resolution:
Append the following point to 22.1.1.1.1:
6. The implementation of a facet of Table 52 parametrized with an InputIterator/OutputIterator should use that iterator only as character source/sink respectively. For a *_get facet, it means that the value received depends only on the sequence of input characters and not on how they are accessed. For a *_put facet, it means that the sequence of characters output depends only on the value to be formatted and not of how the characters are stored.
Section: 22.2 [lib.locale.categories] Status: New Submitter: P.J. Plauger Date: 20 Jun 2005
a) In 22.2.1.1 para. 2 we refer to "the instantiations required in Table 51" to refer to the facet *objects* associated with a locale. And we almost certainly mean just those associated with the default or "C" locale. Otherwise, you can't switch to a locale that enforces a different mapping between narrow and wide characters, or that defines additional uppercase characters.
b) 22.2.1.5 para. 3 (codecvt) has the same issues.
c) 22.2.1.5.2 (do_unshift) is even worse. It *forbids* the generation of a homing sequence for the basic character set, which might very well need one.
d) 22.2.1.5.2 (do_length) likewise dictates that the default mapping between wide and narrow characters be taken as one-for-one.
e) 22.2.2 para. 2 (num_get/put) is both muddled and vacuous, as far as I can tell. The muddle is, as before, calling Table 51 a list of instantiations. But the constraint it applies seems to me to cover *all* defined uses of num_get/put, so why bother to say so?
f) 22.2.3.1.2 para. 1(do_decimal_point) says "The required instantiations return '.' or L'.'.) Presumably this means "as appropriate for the character type. But given the vague definition of "required" earlier, this overrules *any* change of decimal point for non "C" locales. Surely we don't want to do that.
g) 22.2.3.1.2 para. 2 (do_thousands_sep) says "The required instantiations return ',' or L','.) As above, this probably means "as appropriate for the character type. But this overrules the "C" locale, which requires *no* character ('\0') for the thousands separator. Even if we agree that we don't mean to block changes in decimal point or thousands separator, we should also eliminate this clear incompatibility with C.
h) 22.2.3.1.2 para. 2 (do_grouping) says "The required instantiations return the empty string, indicating no grouping." Same considerations as for do_decimal_point.
i) 22.2.4.1 para. 1 (collate) refers to "instantiations required in Table 51". Same bad jargon.
j) 22.2.4.1.2 para. 1 (do_compare) refers to "instantiations required in Table 51". Same bad jargon.
k) 22.2.5 para. 1 (time_get/put) uses the same muddled and vacuous as num_get/put.
l) 22.2.6 para. 2 (money_get/put) uses the same muddled and vacuous as num_get/put.
m) 22.2.6.3.2 (do_pos/neg_format) says "The instantiations required in Table 51 ... return an object of type pattern initialized to {symbol, sign, none, value}." This once again *overrides* the "C" locale, as well as any other locale."
3) We constrain the use_facet calls that can be made by num_get/put, so why don't we do the same for money_get/put? Or for any of the other facets, for that matter?
4) As an almost aside, we spell out when a facet needs to use the ctype facet, but several also need to use a codecvt facet and we don't say so.
Proposed resolution:
----- End of document -----