| Doc. no. | N1684=04-0124 |
| Date: | 10 Sep 2004 |
| Project: | Programming Language C++ |
| Reply to: | Matt Austern <austern@apple.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 or other extensions.
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.
Further discussion from Santa Cruz:
There was some discussion of what the intent of our error reporting mechanism was. There was general agreement on the following principles:
The crux of the disagreement was that some people, but not all, believed that the design was also based on a fourth principle: whenever converstion fails and failbit is set, nothing is to be extracted and the value of the variable being extracted into is guaranteed to be unchanged.
Some people believe that upon overflow, an implementation should "extract" a special value that allows the user to tell that it was overflow instead of some other kind of error. Straw poll: 1 person believed the standard should require that, 2 thought it should forbid it, and 6 thought the standard should allow but not require it.
Proposed resolution:
typo: 22.2.2.2.2 [lib.facet.num.put.virtuals], para 2, bullet 3. Strike "in." from the end.
Change 22.2.2.2 [lib.locale.nm.put], para 11, bullet 2 from:
The sequence of chars accumulated in stage 2 would have caused scanf to report an input failure. ios_base::failbit is assigned to err.
to:
The sequence of chars accumulated in stage 2 would have caused scanf to report an input failure or to store a value outside the range representable by val. ios_base::failbit is assigned to err.
[PJP provided wording. this treats overflow or underflow the same as an ill-formed field. It's not exactly the consensus from Santa Cruz, but he thinks it's the simplest and most robust rule and that it corresponds to widespread common practice.]
[Kona: Wording here still isn't quite right, partly because it refers to scanf and the scanf description of error conditions is murky. The LWG had to do a very close reading of scanf in an attempt to figure out what this proposed resolution means. General agreement that the correct solution: (1) should not refer to scanf behavior, (2) should not set errno, (3) should allow users who care to figure out what kind of error happened. Martin will provide wording, Howard may help.]
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.]
[Tokyo: Not discussed by the full LWG; no one claimed new insights and so time was more productively spent on other issues. In private discussions it was asserted that requirements for any solution include 1) Increasing the full committee's understanding of the problem, and 2) providing compiler vendors, authors, teachers, and of course users with specific suggestions as to how to apply the eventual solution.]
Section: 23.1.2 [lib.associative.reqmts], 23.1.1 [lib.sequence.reqmts] Status: Review Submitter: Andrew Koenig Date: 2 Mar 1999
Table 67 (23.1.1) says that container::erase(iterator) returns an iterator. Table 69 (23.1.2) says that in addition to this requirement, associative containers also say that container::erase(iterator) returns void. That's not an addition; it's a change to the requirements, which has the effect of making associative containers fail to meet the requirements for containers.
Proposed resolution:
In 23.1.2 [lib.associative.reqmts], in Table 69 Associative container requirements, change the return type of a.erase(q) from void to iterator. Change the assertion/not/pre/post-condition from "erases the element pointed to by q" to "erases the element pointed to by q. Returns an iterator pointing to the element immediately following q prior to the element being erased. If no such element exists, a.end() is returned."
In 23.1.2 [lib.associative.reqmts], in Table 69 Associative container requirements, change the return type of a.erase(q1, q2) from void to iterator. Change the assertion/not/pre/post-condition from "erases the elements in the range [q1, q2)" to "erases the elements in the range [q1, q2). Returns an iterator pointing to the element immediately following q2 prior to any elements being erased. If no such element exists, a.end() is returned."
In 23.3.1 [lib.map], in the map class synopsis; and in 23.3.2 [lib.multimap], in the multimap class synopsis; and in 23.3.3 [lib.set], in the set class synopsis; and in 23.3.4 [lib.multiset], in the multiset class synopsis: change the signature of the first erase overload to
iterator erase(iterator position);
[Pre-Kona: reopened at the request of Howard Hinnant]
[Post-Kona: the LWG agrees the return type should be iterator, not void. (Alex Stepanov agrees too.) Matt provided wording.]
[ Sydney: the proposed wording went in the right direction, but it wasn't good enough. We want to return an iterator from the range form of erase as well as the single-iterator form. Also, the wording is slightly different from the wording we have for sequences; there's no good reason for having a difference. Matt provided new wording, which we will review at the next meeting. ]
Section: 20.1.5 [lib.allocator.requirements], 23.1 [lib.container.requirements] Status: Open Submitter: Andy Sawyer Date: 21 Oct 1999
Must the value returned by max_size() be unchanged from call to call?
Must the value returned from max_size() be meaningful?
Possible meanings identified in lib-6827:
1) The largest container the implementation can support given "best case"
conditions - i.e. assume the run-time platform is "configured to the max", and
no overhead from the program itself. This may possibly be determined at the
point the library is written, but certainly no later than compile
time.
2) The largest container the program could create, given "best
case" conditions - i.e. same platform assumptions as (1), but take into account
any overhead for executing the program itself. (or, roughly
"storage=storage-sizeof(program)"). This does NOT include any resource allocated
by the program. This may (or may not) be determinable at compile time.
3)
The largest container the current execution of the program could create, given
knowledge of the actual run-time platform, but again, not taking into account
any currently allocated resource. This is probably best determined at program
start-up.
4) The largest container the current execution program could
create at the point max_size() is called (or more correctly at the point
max_size() returns :-), given it's current environment (i.e. taking into account
the actual currently available resources). This, obviously, has to be determined
dynamically each time max_size() is called.
Proposed resolution:
Change 20.1.5 [lib.allocator.requirements]
table 32 max_size() wording from:
the
largest value that can meaningfully be passed to
X::allocate
to:
the value of the largest
constant expression (5.19 [expr.const])
that could ever meaningfully be passed to X::allocate
Change 23.1 [lib.container.requirements]
table 65 max_size() wording from:
size()
of the largest possible container.
to:
the
value of the largest constant expression (5.19 [expr.const])
that could ever meaningfully be returned by X::size().
[Kona: The LWG informally discussed this and asked Andy Sawyer to submit an issue.]
[Tokyo: The LWG believes (1) above is the intended meaning.]
[Post-Tokyo: Beman Dawes supplied the above resolution at the request of the LWG. 21.3.3 [lib.string.capacity] was not changed because it references max_size() in 23.1. The term "compile-time" was avoided because it is not defined anywhere in the standard (even though it is used several places in the library clauses).]
[Copenhagen: Exactly what max_size means is still unclear. It may have a different meaning as a container member function than as an allocator member function. For the latter, it is probably best thought of as an architectural limit. Nathan will provide new wording.]
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:
Change 18.2 [lib.support.limits] para 1 from:
The headers <limits>, <climits>, and <cfloat> supply characteristics of implementation-dependent fundamental types (3.9.1).
to:
The headers <limits>, <climits>, and <cfloat> supply characteristics of implementation-dependent arithmetic types (3.9.1).
Change 18.2.1 [lib.limits] para 1 from:
The numeric_limits component provides a C++ program with information about various properties of the implementation's representation of the fundamental types.
to:
The numeric_limits component provides a C++ program with information about various properties of the implementation's representation of the arithmetic types.
Change 18.2.1 [lib.limits] para 2 from:
Specializations shall be provided for each fundamental type. . .
to:
Specializations shall be provided for each arithmetic type. . .
Change 18.2.1 [lib.limits] para 4 from:
Non-fundamental standard types. . .
to:
Non-arithmetic standard types. . .
Change 18.2.1.1 [lib.numeric.limits] para 1 from:
The member is_specialized makes it possible to distinguish between fundamental types, which have specializations, and non-scalar types, which do not.
to:
The member is_specialized makes it possible to distinguish between arithmetic types, which have specializations, and non-arithmetic types, which do not.
[post-Toronto: The opinion of the LWG is that the wording in the standard, as well as the wording of the proposed resolution, is flawed. The term "arithmetic types" is well defined in C and C++, and it is not clear that the term is being used correctly. It is also not clear that the term "implementation dependent" has any useful meaning in this context. The biggest problem is that numeric_limits seems to be intended both for built-in types and for user-defined types, and the standard doesn't make it clear how numeric_limits applies to each of those cases. A wholesale review of numeric_limits is needed. A paper would be welcome.]
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.]
Section: 23.2.4.3 [lib.vector.modifiers] Status: Open 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.
At the risk of strengthening the requirement, I suggest simply
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.
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.
I suggest:
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.
Proposed resolution:
[Toronto: It's agreed that there is a defect in complexity of multi-element insert for vector and deque. For vector, the complexity should probably be something along the lines of c1 * N + c2 * distance(i, end()). However, there is some concern about whether it is reasonable to amortize away the copies that we get from a reallocation whenever we exceed the vector's capacity. For deque, the situation is somewhat less clear. Deque is notoriously complicated, and we may not want to impose complexity requirements that would imply any implementation technique more complicated than a while loop whose body is a single-element insert.]
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 is already 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).
The copy constructors of all exception-derived classes already have a no throw spec. Reference 18.6.1, 19.1 and 15.4/13.
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.]
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: Open 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. ]
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:
Add a "Requires" section to section 25.1.1 similar to those proposed for transform and the numeric algorithms (see issue 242):
-2- Requires: In the range [first, last], f shall not invalidate iterators or subranges.
[Copenhagen: The LWG agrees that a function object passed to an algorithm should not invalidate iterators in the range that the algorithm is operating on. The LWG believes that this should be a blanket statement in Clause 25, not just a special requirement for for_each. ]
Section: 17.4.3.1.1 [lib.macro.names] Status: Open 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:
In paragraph 2 of 17.4.3.1.1 [lib.macro.names], change "A translation unit that includes a header shall not contain any macros that define names declared in that header." to "A translation unit that includes a header shall not contain any macros that define names declared in any standard header."
[Copenhagen: the general idea is clearly correct, but there is concern about making sure that the two paragraphs in 17.4.3.1.1 [lib.macro.names] remain consistent. Nathan will provide 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.
Section: 27.6 [lib.iostream.format] Status: Open Submitter: Martin Sebor Date: 19 Mar 2001
The descriptions of the constructors of basic_istream<>::sentry (27.6.1.1.2 [lib.istream::sentry]) and basic_ostream<>::sentry (27.6.2.3 [lib.ostream::sentry]) do not explain what the functions do in case an exception is thrown while they execute. Some current implementations allow all exceptions to propagate, others catch them and set ios_base::badbit instead, still others catch some but let others propagate.
The text also mentions that the functions may call setstate(failbit) (without actually saying on what object, but presumably the stream argument is meant). That may have been fine for basic_istream<>::sentry prior to issue 195, since the function performs an input operation which may fail. However, issue 195 amends 27.6.1.1.2 [lib.istream::sentry], p2 to clarify that the function should actually call setstate(failbit | eofbit), so the sentence in p3 is redundant or even somewhat contradictory.
The same sentence that appears in 27.6.2.3 [lib.ostream::sentry], p3 doesn't seem to be very meaningful for basic_istream<>::sentry which performs no input. It is actually rather misleading since it would appear to guide library implementers to calling setstate(failbit) when os.tie()->flush(), the only called function, throws an exception (typically, it's badbit that's set in response to such an event).
Additional comments from Martin, who isn't comfortable with the current proposed resolution (see c++std-lib-11530)
The istream::sentry ctor says nothing about how the function deals with exemptions (27.6.1.1.2, p1 says that the class is responsible for doing "exception safe"(*) prefix and suffix operations but it doesn't explain what level of exception safety the class promises to provide). The mockup example of a "typical implementation of the sentry ctor" given in 27.6.1.1.2, p6, removed in ISO/IEC 14882:2003, doesn't show exception handling, either. Since the ctor is not classified as a formatted or unformatted input function, the text in 27.6.1.1, p1 through p4 does not apply. All this would seem to suggest that the sentry ctor should not catch or in any way handle exceptions thrown from any functions it may call. Thus, the typical implementation of an istream extractor may look something like [1].
The problem with [1] is that while it correctly sets ios::badbit if an exception is thrown from one of the functions called from the sentry ctor, if the sentry ctor reaches EOF while extracting whitespace from a stream that has eofbit or failbit set in exceptions(), it will cause an ios::failure to be thrown, which will in turn cause the extractor to set ios::badbit.
The only straightforward way to prevent this behavior is to move the definition of the sentry object in the extractor above the try block (as suggested by the example in 22.2.8, p9 and also indirectly supported by 27.6.1.3, p1). See [2]. But such an implementation will allow exceptions thrown from functions called from the ctor to freely propagate to the caller regardless of the setting of ios::badbit in the stream object's exceptions().
So since neither [1] nor [2] behaves as expected, the only possible solution is to have the sentry ctor catch exceptions thrown from called functions, set badbit, and propagate those exceptions if badbit is also set in exceptions(). (Another solution exists that deals with both kinds of sentries, but the code is non-obvious and cumbersome -- see [3].)
Please note that, as the issue points out, current libraries do not behave consistently, suggesting that implementors are not quite clear on the exception handling in istream::sentry, despite the fact that some LWG members might feel otherwise. (As documented by the parenthetical comment here: http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2003/n1480.html#309)
Also please note that those LWG members who in Copenhagen felt that "a sentry's constructor should not catch exceptions, because sentries should only be used within (un)formatted input functions and that exception handling is the responsibility of those functions, not of the sentries," as noted here http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/papers/2001/n1310.html#309 would in effect be either arguing for the behavior described in [1] or for extractors implemented along the lines of [3].
The original proposed resolution (Revision 25 of the issues list) clarifies the role of the sentry ctor WRT exception handling by making it clear that extractors (both library or user-defined) should be implemented along the lines of [2] (as opposed to [1]) and that no exception thrown from the callees should propagate out of either function unless badbit is also set in exceptions().
[1] Extractor that catches exceptions thrown from sentry:
struct S { long i; };
istream& operator>> (istream &strm, S &s)
{
ios::iostate err = ios::goodbit;
try {
const istream::sentry guard (strm, false);
if (guard) {
use_facet<num_get<char> >(strm.getloc ())
.get (istreambuf_iterator<char>(strm),
istreambuf_iterator<char>(),
strm, err, s.i);
}
}
catch (...) {
bool rethrow;
try {
strm.setstate (ios::badbit);
rethrow = false;
}
catch (...) {
rethrow = true;
}
if (rethrow)
throw;
}
if (err)
strm.setstate (err);
return strm;
}
[2] Extractor that propagates exceptions thrown from sentry:
istream& operator>> (istream &strm, S &s)
{
istream::sentry guard (strm, false);
if (guard) {
ios::iostate err = ios::goodbit;
try {
use_facet<num_get<char> >(strm.getloc ())
.get (istreambuf_iterator<char>(strm),
istreambuf_iterator<char>(),
strm, err, s.i);
}
catch (...) {
bool rethrow;
try {
strm.setstate (ios::badbit);
rethrow = false;
}
catch (...) {
rethrow = true;
}
if (rethrow)
throw;
}
if (err)
strm.setstate (err);
}
return strm;
}
[3] Extractor that catches exceptions thrown from sentry but doesn't set badbit if the exception was thrown as a result of a call to strm.clear().
istream& operator>> (istream &strm, S &s)
{
const ios::iostate state = strm.rdstate ();
const ios::iostate except = strm.exceptions ();
ios::iostate err = std::ios::goodbit;
bool thrown = true;
try {
const istream::sentry guard (strm, false);
thrown = false;
if (guard) {
use_facet<num_get<char> >(strm.getloc ())
.get (istreambuf_iterator<char>(strm),
istreambuf_iterator<char>(),
strm, err, s.i);
}
}
catch (...) {
if (thrown && state & except)
throw;
try {
strm.setstate (ios::badbit);
thrown = false;
}
catch (...) {
thrown = true;
}
if (thrown)
throw;
}
if (err)
strm.setstate (err);
return strm;
}
Proposed resolution:
Remove the last sentence of 27.6.1.1.2 [lib.istream::sentry] p5 (but not the footnote, which should be moved to the preceding sentence).
Remove the last sentence of 27.6.2.3 [lib.ostream::sentry] p3 (but not the footnote, which should be moved to the preceding sentence).
Rationale:
The LWG feels that no clarification of EH policy is necessary: the standard is precise about which operations sentry's constructor performs, and about which of those operations can throw. However, the sentence at the end should be removed because it's redundant.
Section: 27.6.1.3 [lib.istream.unformatted] Status: Open Submitter: Howard Hinnant Date: 09 Oct 201
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.
Dietmar: nothing should depend on eofbit. Eofbit should only be examined by the user to determine why something failed.
[Taken from c++std-lib-8873, c++std-lib-8874, c++std-lib-8876]
Proposed resolution:
[Santa Cruz: On the one hand, it would clearly be silly to seek to a non-EOF position without resetting eofbit. On the other hand, having seek clear eofbit explicitly would set a major precedent: there is currently no place where any of the flags are reset without the user explicitly asking for them to be. This is the tip of a general problem, that the various flags are stickier than many users might expect. Bill, Gaby, and Howard will discuss this issue and propose a resolution.]
Section: 22.2.1 [lib.category.ctype] Status: Open Submitter: Matt Austern Date: 23 Jan 2002
What should the following program print?
#include <locale>
#include <iostream>
class my_ctype : public std::ctype<char>
{
typedef std::ctype<char> base;
public:
my_ctype(std::size_t refs = 0) : base(my_table, false, refs)
{
std::copy(base::classic_table(), base::classic_table() + base::table_size,
my_table);
my_table[(unsigned char) '_'] = (base::mask) (base::print | base::space);
}
private:
mask my_table[base::table_size];
};
int main()
{
my_ctype ct;
std::cout << "isspace: " << ct.is(std::ctype_base::space, '_') << " "
<< "isalpha: " << ct.is(std::ctype_base::alpha, '_') << std::endl;
}
The goal is to create a facet where '_' is treated as whitespace.
On gcc 3.0, this program prints "isspace: 1 isalpha: 0". On Microsoft C++ it prints "isspace: 1 isalpha: 1".
I believe that both implementations are legal, and the standard does not give enough guidance for users to be able to use std::ctype's protected interface portably.
The above program assumes that ctype_base::mask enumerators like space and print are disjoint, and that the way to say that a character is both a space and a printing character is to or those two enumerators together. This is suggested by the "exposition only" values in 22.2.1 [lib.category.ctype], but it is nowhere specified in normative text. An alternative interpretation is that the more specific categories subsume the less specific. The above program gives the results it does on the Microsoft compiler because, on that compiler, print has all the bits set for each specific printing character class.
From the point of view of std::ctype's public interface, there's no important difference between these two techniques. From the point of view of the protected interface, there is. If I'm defining a facet that inherits from std::ctype<char>, I'm the one who defines the value that table()['a'] returns. I need to know what combination of mask values I should use. This isn't so very esoteric: it's exactly why std::ctype has a protected interface. If we care about users being able to write their own ctype facets, we have to give them a portable way to do it.
Related reflector messages: lib-9224, lib-9226, lib-9229, lib-9270, lib-9272, lib-9273, lib-9274, lib-9277, lib-9279.
Issue 339 is related, but not identical. The proposed resolution if issue 339 says that ctype_base::mask must be a bitmask type. It does not say that the ctype_base::mask elements are bitmask elements, so it doesn't directly affect this issue.
More comments from Benjamin Kosnik, who believes that that C99 compatibility essentially requires what we're calling option 1 below.
I think the C99 standard is clear, that isspace -> !isalpha.
--------
#include <locale>
#include <iostream>
class my_ctype : public std::ctype<char>
{
private:
typedef std::ctype<char> base;
mask my_table[base::table_size];
public:
my_ctype(std::size_t refs = 0) : base(my_table, false, refs)
{
std::copy(base::classic_table(), base::classic_table() + base::table_size,
my_table);
mask both = base::print | base::space;
my_table[static_cast<mask>('_')] = both;
}
};
int main()
{
using namespace std;
my_ctype ct;
cout << "isspace: " << ct.is(ctype_base::space, '_') << endl;
cout << "isprint: " << ct.is(ctype_base::print, '_') << endl;
// ISO C99, isalpha iff upper | lower set, and !space.
// 7.5, p 193
// -> looks like g++ behavior is correct.
// 356 -> bitmask elements are required for ctype_base
// 339 -> bitmask type required for mask
cout << "isalpha: " << ct.is(ctype_base::alpha, '_') << endl;
}
Proposed resolution:
Informally, we have three choices:
Either of the first two options is just as good from the standpoint of portability. Either one will require some implementations to change.
[ More discussion is needed. Nobody likes option 3. Options 1 and 2 are both controversial, 2 perhaps less so. Benjamin thinks that option 1 is required for C99 compatibility. ]
Section: 20.3.6.2 [lib.bind.1st] Status: Open 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:
The simplest and most localized change to prevent such errors is to require bind1st() use a static_cast expression rather than the functional-style conversion; that is, have bind1st() return:
binder1st<Operation>( op,
static_cast<typename Operation::first_argument_type>(x)).
A more agressive solution is to change the semantics of functional-style conversions to not permit a reinterpret_cast. For contexts that require the semantics of reinterpret_cast, the language may want to require the use of an explicit cast expression such as '(T) x' or 'reinterpret_cast<T>(x)' and limit the behavior of the functional notation to match statically-checked and standard conversions (as defined by 5.2.9 and 4.10, etc.). Although changing the semantics of functional-style conversions may seem drastic and does have language-wide ramifications, it has the benefit of better unifying the conversion rules for user defined types and built-in types, which can be especially important for generic template programming.
[Santa Cruz: it's clear that a function-style cast is wrong. Maybe a static cast would be better, or maybe no cast at all. Jeremy will check with the original author of this part of the Standard and will see what the original intent was.]
Section: 27 [lib.input.output] Status: Open Submitter: Walter Brown, Marc Paterno Date: 10 May 2002
The following member functions are declared const, yet return non-const pointers. We believe they are should be changed, because they allow code that may surprise the user. See document N1360 for details and rationale.
[Santa Cruz: the real issue is that we've got const member functions that return pointers to non-const, and N1360 proposes replacing them by overloaded pairs. There isn't a consensus about whether this is a real issue, since we've never said what our constness policy is for iostreams. N1360 relies on a distinction between physical constness and logical constness; that distinction, or those terms, does not appear in the standard.]
Proposed resolution:
In 27.4.4 and 27.4.4.2
Replace
basic_ostream<charT,traits>* tie() const;
with
basic_ostream<charT,traits>* tie(); const basic_ostream<charT,traits>* tie() const;
and replace
basic_streambuf<charT,traits>* rdbuf() const;
with
basic_streambuf<charT,traits>* rdbuf(); const basic_streambuf<charT,traits>* rdbuf() const;
In 27.5.2 and 27.5.2.3.1
Replace
char_type* eback() const;
with
char_type* eback(); const char_type* eback() const;
Replace
char_type gptr() const;
with
char_type* gptr(); const char_type* gptr() const;
Replace
char_type* egptr() const;
with
char_type* egptr(); const char_type* egptr() const;
In 27.5.2 and 27.5.2.3.2
Replace
char_type* pbase() const;
with
char_type* pbase(); const char_type* pbase() const;
Replace
char_type* pptr() const;
with
char_type* pptr(); const char_type* pptr() const;
Replace
char_type* epptr() const;
with
char_type* epptr(); const char_type* epptr() const;
In 27.7.2, 27.7.2.2, 27.7.3 27.7.3.2, 27.7.4, and 27.7.6
Replace
basic_stringbuf<charT,traits,Allocator>* rdbuf() const;
with
basic_stringbuf<charT,traits,Allocator>* rdbuf(); const basic_stringbuf<charT,traits,Allocator>* rdbuf() const;
In 27.8.1.5, 27.8.1.7, 27.8.1.8, 27.8.1.10, 27.8.1.11, and 27.8.1.13
Replace
basic_filebuf<charT,traits>* rdbuf() const;
with
basic_filebuf<charT,traits>* rdbuf(); const basic_filebuf<charT,traits>* rdbuf() const;
Section: 21.3.5.6 [lib.string::replace] Status: Open Submitter: Beman Dawes Date: 3 Jun 2002
21.3.5.6 [lib.string::replace] basic_string::replace, second signature, given in paragraph 1, has two "Throws" paragraphs (3 and 5).
In addition, the second "Throws" paragraph (5) includes specification (beginning with "Otherwise, the function replaces ...") that should be part of the "Effects" paragraph.
Proposed resolution:
[This is a typo that escalated. It's clear that what's in the Standard is wrong. It's less clear what the fix ought to be. Someone who understands string replace well needs to work on this.]
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 [lib.iostream.objects], p2, immediately before the last sentence of the paragraph, the following two sentences:
It is implementation-defined whether the header <iostream> defines an ios_base::Init object or not. If it does not, an implementation must specify the means of achieving safe access to the standard objects for input and output during program startup.
[Santa Cruz: The LWG is leaning toward NAD. There isn't any normative wording saying that the Init scheme will be used, but that is probably intentional. Implementers use dirty tricks for iostream initialization, and doing it portably is somewhere between difficult and impossible. Too much constraint in this area is dangerous, and if we are to make any changes it would probably be more appropriate for them to be nonnormative. Summer '04 mid-meeting mailing: Martin provided wording for resolution and rationale.]
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 specifies that implementations may (but need not) define an ios_base::Init object, while requiring them to document whether they do or not, and if not, to document how portable programs achieve safe access to the 8 standard iostream objects during program startup (3.6)(*). The intent is that if an implementation documents that <iostream> defines an ios_base::Init object, it implies that the header must be #included before any references to the standard iostream objects. Otherwise, if an implementation does not define an ios_base::Init object in <iostream> it must either assure and document that the standard iostream objects are safely accessible at startup, or specify what a portable program must do to safely access them (e.g., it may require that a program define an ios_base::Init object before doing so, or that it call ios::sync_with_stdio(), etc.).
(*) Note that the term startup is broader than the term "Constructors and destructors for static objects" used in Footnote 265 since the former includes other functions besides constructors and destructors, including the following example:
int foo () { return (std::cout << "foo()\n").rdstate (); }
int i = foo ();
int main () { return i; }
Section: 23.1 [lib.container.requirements] Status: Open 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.
[Santa Cruz: More people need to look at this. Much user code may assume stability. On the other hand, it seems drastic to add a new requirement now.]
Proposed resolution:
Section: 27.7.1.3 [lib.stringbuf.virtuals] Status: Open 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. I think it would be clearer if the conditions were rewritten as follows:
(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
As written, it is unclear what should be the result if cases 1 & 2 are true, but case 3 is false, e.g.,
seekoff(0, ios_base::cur, ios_base::in | ios_base::out)
[Santa Cruz: The ambiguity seems real. We need to do a survey of implementations before we decide on a solution.]
Proposed resolution:
Section: 22.1.1 [lib.locale] Status: Review Submitter: Martin Sebor Date: 6 Sep 2002
I think there is a problem with 22.1.1, p6 which says that
-6- An instance of locale is im