| Doc. no. | N2180=07-0040 |
| Date: | 2007-03-09 |
| 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.
Tentatively Ready - The issue has been reviewed online, but not in a meeting, and some support has been formed for the proposed resolution. Tentatively Ready issues may be moved to Ready and forwarded to full committee within the same meeting. Unlike Ready issues they will be reviewed in subcommittee prior to forwarding to full committee.
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 [facet.num.get.virtuals] Status: Open Submitter: Nathan Myers Date: 1998-08-06
View other active issues in [facet.num.get.virtuals].
View all other issues in [facet.num.get.virtuals].
View all issues with Open status.
Discussion:
The 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.
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.
Proposed resolution:
Section: 23.2.5 [vector] Status: Open Submitter: AFNOR Date: 1998-10-07
View all other issues in [vector].
View all issues with Open status.
Discussion:
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.
[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.
Proposed resolution:
[ Batavia: The LWG feels we need something closer to SGI's bitvector to ease migration from vector<bool>. Although some of the funcitonality from N2050 could well be used in such a template. The concern is easing the API migration for those users who want to continue using a bit-packed container. Alan and Beman to work. ]
Section: 18.2.1 [limits] Status: Tentatively Ready Submitter: Stephen Cleary Date: 1999-12-21
View other active issues in [limits].
View all other issues in [limits].
View all issues with Tentatively Ready status.
Discussion:
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.
[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.]
Proposed resolution:
Change 18.2 [support.limits] to:
-1- The headers <limits>, <climits>, <cfloat>, and <cinttypes> supply characteristics of implementation-dependent
fundamentalarithmetic types (3.9.1).
Change 18.2.1 [limits] to:
-1- The numeric_limits component provides a C++ program with information about various properties of the implementation's representation of the
fundamentalarithmetic types.-2- Specializations shall be provided for each
fundamentalarithmetic type, both floating point and integer, including bool. The member is_specialized shall be true for all such specializations of numeric_limits.-4- Non-
fundamentalarithmetic standard types, such as complex<T> (26.3.2), shall not have specializations.
Change 18.2.1.1 [numeric.limits] to:
-1- The member is_specialized makes it possible to distinguish between fundamental types, which have specializations, and non-scalar types, which do not.
Section: 18.5.1.1 [new.delete.single] Status: Tentatively Ready Submitter: Howard Hinnant Date: 1999-08-29
View other active issues in [new.delete.single].
View all other issues in [new.delete.single].
View all issues with Tentatively Ready status.
Discussion:
As specified, the implementation of the nothrow version of operator new does not necessarily call the ordinary operator new, but may instead simply call the same underlying allocator and return a null pointer instead of throwing an exception in case of failure.
Such an implementation breaks code that replaces the ordinary version of new, but not the nothrow version. If the ordinary version of new/delete is replaced, and if the replaced delete is not compatible with pointers returned from the library versions of new, then when the replaced delete receives a pointer allocated by the library new(nothrow), crash follows.
The fix appears to be that the lib version of new(nothrow) must call the ordinary new. Thus when the ordinary new gets replaced, the lib version will call the replaced ordinary new and things will continue to work.
An alternative would be to have the ordinary new call new(nothrow). This seems sub-optimal to me as the ordinary version of new is the version most commonly replaced in practice. So one would still need to replace both ordinary and nothrow versions if one wanted to replace the ordinary version.
Another alternative is to put in clear text that if one version is replaced, then the other must also be replaced to maintain compatibility. Then the proposed resolution below would just be a quality of implementation issue. There is already such text in paragraph 7 (under the new(nothrow) version). But this nuance is easily missed if one reads only the paragraphs relating to the ordinary new.
N2158 has been written explaining the rationale for the proposed resolution below.
Proposed resolution:
Change 18.5.1.1 [new.delete.single]:
void* operator new(std::size_t size, const std::nothrow_t&) throw();-5- Effects: Same as above, except that it is called by a placement version of a new-expression when a C++ program prefers a null pointer result as an error indication, instead of a bad_alloc exception.
-6- Replaceable: a C++ program may define a function with this function signature that displaces the default version defined by the C++ Standard library.
-7- Required behavior: Return a non-null pointer to suitably aligned storage (3.7.4), or else return a null pointer. This nothrow version of operator new returns a pointer obtained as if acquired from the (possibly replaced) ordinary version. This requirement is binding on a replacement version of this function.
-8- Default behavior:
- Calls operator new(size).
- If the call to operator new(size) returns normally, returns the result of that call, else
- if the call to operator new(size) throws an exception, returns a null pointer.
Executes a loop: Within the loop, the function first attempts to allocate the requested storage. Whether the attempt involves a call to the Standard C library function malloc is unspecified.Returns a pointer to the allocated storage if the attempt is successful. Otherwise, if the last argument to set_new_handler() was a null pointer, return a null pointer.Otherwise, the function calls the current new_handler (18.5.2.2). If the called function returns, the loop repeats.The loop terminates when an attempt to allocate the requested storage is successful or when a called new_handler function does not return. If the called new_handler function terminates by throwing a bad_alloc exception, the function returns a null pointer.-9- [Example:
T* p1 = new T; // throws bad_alloc if it fails T* p2 = new(nothrow) T; // returns 0 if it fails--end example]
void operator delete(void* ptr) throw();void operator delete(void* ptr, const std::nothrow_t&) throw();-10- Effects: The deallocation function (3.7.4.2) called by a delete-expression to render the value of ptr invalid.
-11- Replaceable: a C++ program may define a function with this function signature that displaces the default version defined by the C++ Standard library.
-12- Requires: the value of ptr is null or the value returned by an earlier call to the
default(possibly replaced) operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&).-13- Default behavior:
- For a null value of ptr, do nothing.
- Any other value of ptr shall be a value returned earlier by a call to the default operator new, which was not invalidated by an intervening call to operator delete(void*) (17.4.3.7). For such a non-null value of ptr, reclaims storage allocated by the earlier call to the default operator new.
-14- Remarks: It is unspecified under what conditions part or all of such reclaimed storage is allocated by a subsequent call to operator new or any of calloc, malloc, or realloc, declared in <cstdlib>.
void operator delete(void* ptr, const std::nothrow_t&) throw();-15- Effects: Same as above, except that it is called by the implementation when an exception propagates from a nothrow placement version of the new-expression (i.e. when the constructor throws an exception).
-16- Replaceable: a C++ program may define a function with this function signature that displaces the default version defined by the C++ Standard library.
-17- Requires: the value of ptr is null or the value returned by an earlier call to the (possibly replaced) operator new(std::size_t) or operator new(std::size_t, const std::nothrow_t&).
-18- Default behavior: Calls operator delete(ptr).
Change 18.5.1.2 [new.delete.array]
void* operator new[](std::size_t size, const std::nothrow_t&) throw();-5- Effects: Same as above, except that it is called by a placement version of a new-expression when a C++ program prefers a null pointer result as an error indication, instead of a bad_alloc exception.
-6- Replaceable: a C++ program can define a function with this function signature that displaces the default version defined by the C++ Standard library.
-7- Required behavior:
Same as for operator new(std::size_t, const std::nothrow_t&). This nothrow version of operator new[] returns a pointer obtained as if acquired from the ordinary version.Return a non-null pointer to suitably aligned storage (3.7.4), or else return a null pointer. This nothrow version of operator new returns a pointer obtained as if acquired from the (possibly replaced) operator new[](std::size_t size). This requirement is binding on a replacement version of this function.-8- Default behavior:
Returns operator new(size, nothrow).
- Calls operator new[](size).
- If the call to operator new[](size) returns normally, returns the result of that call, else
- if the call to operator new[](size) throws an exception, returns a null pointer.
void operator delete[](void* ptr) throw(); void operator delete[](void* ptr, const std::nothrow_t&) throw();-9- Effects: The deallocation function (3.7.4.2) called by the array form of a delete-expression to render the value of ptr invalid.
-10- Replaceable: a C++ program can define a function with this function signature that displaces the default version defined by the C++ Standard library.
-11- Requires: the value of ptr is null or the value returned by an earlier call to operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&).
-12- Default behavior: Calls operator delete(ptr) or operator delete[](ptr
, std::nothrow) respectively.
Rationale:
Yes, they may become unlinked, and that is by design. If a user replaces one, the user should also replace the other.
[ Reopened due to a gcc conversation between Howard, Martin and Gaby. Forwarding or not is visible behavior to the client and it would be useful for the client to know which behavior it could depend on. ]
[ Batavia: Robert voiced serious reservations about backwards compatibility for his customers. ]
Section: 23.1.2 [associative.reqmts] Status: Tentatively Ready Submitter: Andrew Koenig Date: 2000-04-30
View all other issues in [associative.reqmts].
View all issues with Tentatively Ready status.
Discussion:
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 [...]".
[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. ]
[ Batavia: 1780 accepted with minor wording changes in the proposed wording (reflected in the proposed resolution below). Concerns about the performance of the algorithm were satisfactorily met by 1780. 371 already handles the stability of equal ranges and so that part of the resolution from 1780 is no longer needed (or reflected in the proposed wording below). ]
Proposed resolution:
Change the indicated rows of the "Associative container requirements" Table in 23.1.2 [associative.reqmts] to:
| expression | return type | assertion/note pre/post-condition |
complexity |
|---|---|---|---|
| a_eq.insert(t) | iterator | inserts t and returns the iterator pointing to the newly inserted element. If a range containing elements equivalent to t exists in a_eq, t is inserted at the end of that range. | logarithmic |
| a.insert(p,t) | iterator |
inserts t if and only if there is no element with key equivalent to the
key of t in containers with unique keys; always inserts t in containers
with equivalent keys. always returns the iterator pointing to the element with key
equivalent to the key of t. |
logarithmic in general, but amortized constant if t is inserted right |
Section: 19.1 [std.exceptions], 27.4.2.1.1 [ios::failure] Status: Tentatively Ready Submitter: Dave Abrahams Date: 2000-08-01
View all issues with Tentatively Ready status.
Discussion:
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:
Change 19.1.1 [logic.error]
namespace std { class logic_error : public exception { public: explicit logic_error(const string& what_arg); explicit logic_error(const char* what_arg); }; }...
logic_error(const char* what_arg);
-4- Effects: Constructs an object of class logic_error.
-5- Postcondition: strcmp(what(), what_arg) == 0.
Change 19.1.2 [domain.error]
namespace std { class domain_error : public logic_error { public: explicit domain_error(const string& what_arg); explicit domain_error(const char* what_arg); }; }...
domain_error(const char* what_arg);
-4- Effects: Constructs an object of class domain_error.
-5- Postcondition: strcmp(what(), what_arg) == 0.
Change 19.1.3 [invalid.argument]
namespace std { class invalid_argument : public logic_error { public: explicit invalid_argument(const string& what_arg); explicit invalid_argument(const char* what_arg); }; }...
invalid_argument(const char* what_arg);
-4- Effects: Constructs an object of class invalid_argument.
-5- Postcondition: strcmp(what(), what_arg) == 0.
Change 19.1.4 [length.error]
namespace std { class length_error : public logic_error { public: explicit length_error(const string& what_arg); explicit length_error(const char* what_arg); }; }...
length_error(const char* what_arg);
-4- Effects: Constructs an object of class length_error.
-5- Postcondition: strcmp(what(), what_arg) == 0.
Change 19.1.5 [out.of.range]
namespace std { class out_of_range : public logic_error { public: explicit out_of_range(const string& what_arg); explicit out_of_range(const char* what_arg); }; }...
out_of_range(const char* what_arg);
-4- Effects: Constructs an object of class out_of_range.
-5- Postcondition: strcmp(what(), what_arg) == 0.
Change 19.1.6 [runtime.error]
namespace std { class runtime_error : public exception { public: explicit runtime_error(const string& what_arg); explicit runtime_error(const char* what_arg); }; }...
runtime_error(const char* what_arg);
-4- Effects: Constructs an object of class runtime_error.
-5- Postcondition: strcmp(what(), what_arg) == 0.
Change 19.1.7 [range.error]
namespace std { class range_error : public runtime_error { public: explicit range_error(const string& what_arg); explicit range_error(const char* what_arg); }; }...
range_error(const char* what_arg);
-4- Effects: Constructs an object of class range_error.
-5- Postcondition: strcmp(what(), what_arg) == 0.
Change 19.1.8 [overflow.error]
namespace std { class overflow_error : public runtime_error { public: explicit overflow_error(const string& what_arg); explicit overflow_error(const char* what_arg); }; }...
overflow_error(const char* what_arg);
-4- Effects: Constructs an object of class overflow_error.
-5- Postcondition: strcmp(what(), what_arg) == 0.
Change 19.1.9 [underflow.error]
namespace std { class underflow_error : public runtime_error { public: explicit underflow_error(const string& what_arg); explicit underflow_error(const char* what_arg); }; }...
underflow_error(const char* what_arg);
-4- Effects: Constructs an object of class underflow_error.
-5- Postcondition: strcmp(what(), what_arg) == 0.
Change 27.4.2.1.1 [ios::failure]
namespace std { class ios_base::failure : public exception { public: explicit failure(const string& msg); explicit failure(const char* msg); virtual const char* what() const throw(); }; }...
failure(const char* msg);
-4- Effects: Constructs an object of class failure.
-5- Postcondition: strcmp(what(), msg) == 0.
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.]
[ Batavia: Merged copy constructor and assignment operator spec into exception and added ios::failure into the proposed resolution. ]
Section: 27.5.2 [streambuf] Status: Open Submitter: Martin Sebor Date: 2000-08-12
View all other issues in [streambuf].
View all issues with Open status.
Discussion:
The basic_streambuf members gbump() and pbump() are specified to take an int argument. This requirement prevents the functions from effectively manipulating buffers larger than std::numeric_limits<int>::max() characters. It also makes the common use case for these functions somewhat difficult as many compilers will issue a warning when an argument of type larger than int (such as ptrdiff_t on LLP64 architectures) is passed to either of the function. Since it's often the result of the subtraction of two pointers that is passed to the functions, a cast is necessary to silence such warnings. Finally, the usage of a native type in the functions signatures is inconsistent with other member functions (such as sgetn() and sputn()) that manipulate the underlying character buffer. Those functions take a streamsize argument.
Proposed resolution:
Change the signatures of these functions in the synopsis of template class basic_streambuf (27.5.2) and in their descriptions (27.5.2.3.1, p4 and 27.5.2.3.2, p4) to take a streamsize argument.
Although this change has the potential of changing the ABI of the library, the change will affect only platforms where int is different than the definition of streamsize. However, since both functions are typically inline (they are on all known implementations), even on such platforms the change will not affect any user code unless it explicitly relies on the existing type of the functions (e.g., by taking their address). Such a possibility is IMO quite remote.
Alternate Suggestion from Howard Hinnant, c++std-lib-7780:
This is something of a nit, but I'm wondering if streamoff wouldn't be a better choice than streamsize. The argument to pbump and gbump MUST be signed. But the standard has this to say about streamsize (27.4.1/2/Footnote):
[Footnote: streamsize is used in most places where ISO C would use size_t. Most of the uses of streamsize could use size_t, except for the strstreambuf constructors, which require negative values. It should probably be the signed type corresponding to size_t (which is what Posix.2 calls ssize_t). --- end footnote]
This seems a little weak for the argument to pbump and gbump. Should we ever really get rid of strstream, this footnote might go with it, along with the reason to make streamsize signed.
Rationale:
The LWG believes this change is too big for now. We may wish to reconsider this for a future revision of the standard. One possibility is overloading pbump, rather than changing the signature.
[ [2006-05-04: Reopened at the request of Chris (Krzysztof ?elechowski)] ]
Section: 20.1.6 [allocator.requirements] Status: Tentatively Ready Submitter: Matt Austern Date: 2000-08-22
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with Tentatively Ready status.
Discussion:
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
[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.]
Proposed resolution:
Add row to Table 35: Allocator requirements, right after the row defining operator!=:
If a1 == a2 then Y(a1) == Y(a2)
[Lillehammer: Same conclusion as before: this should be considered as part of an allocator redesign, not solved on its own.]
[ Batavia: An allocator redesign is not forthcoming and thus we fixed this one issue. ]
Section: 25.1.1 [alg.foreach] Status: Open Submitter: Angelika Langer Date: 2001-01-03
View all other issues in [alg.foreach].
View all issues with Open status.
Discussion:
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.
[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.]
Proposed resolution:
Section: 24.1.4 [bidirectional.iterators], 24.1.5 [random.access.iterators] Status: Open Submitter: John Potter Date: 2001-01-22
View all other issues in [bidirectional.iterators].
View all issues with Open status.
Discussion:
In section 24.1.4 [bidirectional.iterators], Table 75 gives the return type of *r-- as convertible to T. This is not consistent with Table 74 which gives the return type of *r++ as T&. *r++ = t is valid while *r-- = t is invalid.
In section 24.1.5 [random.access.iterators], Table 76 gives the return type of a[n] as convertible to T. This is not consistent with the semantics of *(a + n) which returns T& by Table 74. *(a + n) = t is valid while a[n] = t is invalid.
Discussion from the Copenhagen meeting: the first part is uncontroversial. The second part, operator[] for Random Access Iterators, requires more thought. There are reasonable arguments on both sides. Return by value from operator[] enables some potentially useful iterators, e.g. a random access "iota iterator" (a.k.a "counting iterator" or "int iterator"). There isn't any obvious way to do this with return-by-reference, since the reference would be to a temporary. On the other hand, reverse_iterator takes an arbitrary Random Access Iterator as template argument, and its operator[] returns by reference. If we decided that the return type in Table 76 was correct, we would have to change reverse_iterator. This change would probably affect user code.
History: the contradiction between reverse_iterator and the Random Access Iterator requirements has been present from an early stage. In both the STL proposal adopted by the committee (N0527==94-0140) and the STL technical report (HPL-95-11 (R.1), by Stepanov and Lee), the Random Access Iterator requirements say that operator[]'s return value is "convertible to T". In N0527 reverse_iterator's operator[] returns by value, but in HPL-95-11 (R.1), and in the STL implementation that HP released to the public, reverse_iterator's operator[] returns by reference. In 1995, the standard was amended to reflect the contents of HPL-95-11 (R.1). The original intent for operator[] is unclear.
In the long term it may be desirable to add more fine-grained iterator requirements, so that access method and traversal strategy can be decoupled. (See "Improved Iterator Categories and Requirements", N1297 = 01-0011, by Jeremy Siek.) Any decisions about issue 299 should keep this possibility in mind.
Further discussion: I propose a compromise between John Potter's resolution, which requires T& as the return type of a[n], and the current wording, which requires convertible to T. The compromise is to keep the convertible to T for the return type of the expression a[n], but to also add a[n] = t as a valid expression. This compromise "saves" the common case uses of random access iterators, while at the same time allowing iterators such as counting iterator and caching file iterators to remain random access iterators (iterators where the lifetime of the object returned by operator*() is tied to the lifetime of the iterator).
Note that the compromise resolution necessitates a change to reverse_iterator. It would need to use a proxy to support a[n] = t.
Note also there is one kind of mutable random access iterator that will no longer meet the new requirements. Currently, iterators that return an r-value from operator[] meet the requirements for a mutable random access iterartor, even though the expression a[n] = t will only modify a temporary that goes away. With this proposed resolution, a[n] = t will be required to have the same operational semantics as *(a + n) = t.
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 [iostream.format] Status: Open Submitter: Martin Sebor Date: 2001-03-19
View other active issues in [iostream.format].
View all other issues in [iostream.format].
View all issues with Open status.
Discussion:
The descriptions of the constructors of basic_istream<>::sentry (27.6.1.1.2 [istream::sentry]) and basic_ostream<>::sentry (27.6.2.3 [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 [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 [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;
}
[Pre-Berlin] Reopened at the request of Paolo Carlini and Steve Clamage.
[Pre-Portland] A relevant newsgroup post:
The current proposed resolution of issue #309 (http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#309) is unacceptable. I write commerical software and coding around this makes my code ugly, non-intuitive, and requires comments referring people to this very issue. Following is the full explanation of my experience.
In the course of writing software for commercial use, I constructed std::ifstream's based on user-supplied pathnames on typical POSIX systems.
It was expected that some files that opened successfully might not read successfully -- such as a pathname which actually refered to a directory. Intuitively, I expected the streambuffer underflow() code to throw an exception in this situation, and recent implementations of libstdc++'s basic_filebuf do just that (as well as many of my own custom streambufs).
I also intuitively expected that the istream code would convert these exceptions to the "badbit' set on the stream object, because I had not requested exceptions. I refer to 27.6.1.1. P4.
However, this was not the case on at least two implementations -- if the first thing I did with an istream was call operator>>( T& ) for T among the basic arithmetic types and std::string. Looking further I found that the sentry's constructor was invoking the exception when it pre-scanned for whitespace, and the extractor function (operator>>()) was not catching exceptions in this situation.
So, I was in a situation where setting 'noskipws' would change the istream's behavior even though no characters (whitespace or not) could ever be successfully read.
Also, calling .peek() on the istream before calling the extractor() changed the behavior (.peek() had the effect of setting the badbit ahead of time).
I found this all to be so inconsistent and inconvenient for me and my code design, that I filed a bugzilla entry for libstdc++. I was then told that the bug cannot be fixed until issue #309 is resolved by the committee.
Proposed resolution:
Rationale:
The LWG agrees there is minor variation between implementations, but believes that it doesn't matter. This is a rarely used corner case. There is no evidence that this has any commercial importance or that it causes actual portability problems for customers trying to write code that runs on multiple implementations.
Section: 27.6.1.3 [istream.unformatted] Status: Open Submitter: Howard Hinnant Date: 2001-10-09
View other active issues in [istream.unformatted].
View all other issues in [istream.unformatted].
View all issues with Open status.
Discussion:
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 [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.
Pre-Berlin: Paolo points out several problems with the proposed resolution in Ready state:
Proposed resolution:
Change 27.6.1.3 [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: 21 [strings], 23 [containers], 27 [input.output] Status: Open Submitter: Martin Sebor Date: 2001-10-09
View all other issues in [strings].
View all issues with Open status.
Discussion:
The synopses of the C++ library headers clearly show which names are required to be defined in each header. Since in order to implement the classes and templates defined in these headers declarations of other templates (but not necessarily their definitions) are typically necessary the standard in 17.4.4, p1 permits library implementers to include any headers needed to implement the definitions in each header.
For instance, although it is not explicitly specified in the synopsis of <string>, at the point of definition of the std::basic_string template the declaration of the std::allocator template must be in scope. All current implementations simply include <memory> from within <string>, either directly or indirectly, to bring the declaration of std::allocator into scope.
Additionally, however, some implementation also include <istream> and <ostream> at the top of <string> to bring the declarations of std::basic_istream and std::basic_ostream into scope (which are needed in order to implement the string inserter and extractor operators (21.3.7.9 [lib.string.io])). Other implementations only include <iosfwd>, since strictly speaking, only the declarations and not the full definitions are necessary.
Obviously, it is possible to implement <string> without actually providing the full definitions of all the templates std::basic_string uses (std::allocator, std::basic_istream, and std::basic_ostream). Furthermore, not only is it possible, doing so is likely to have a positive effect on compile-time efficiency.
But while it may seem perfectly reasonable to expect a program that uses the std::basic_string insertion and extraction operators to also explicitly include <istream> or <ostream>, respectively, it doesn't seem reasonable to also expect it to explicitly include <memory>. Since what's reasonable and what isn't is highly subjective one would expect the standard to specify what can and what cannot be assumed. Unfortunately, that isn't the case.
The examples below demonstrate the issue.
Example 1:
It is not clear whether the following program is complete:
#include <string>
extern std::basic_ostream<char> &strm;
int main () {
strm << std::string ("Hello, World!\n");
}
or whether one must explicitly include <memory> or <ostream> (or both) in addition to <string> in order for the program to compile.
Example 2:
Similarly, it is unclear whether the following program is complete:
#include <istream>
extern std::basic_iostream<char> &strm;
int main () {
strm << "Hello, World!\n";
}
or whether one needs to explicitly include <ostream>, and perhaps even other headers containing the definitions of other required templates:
#include <ios>
#include <istream>
#include <ostream>
#include <streambuf>
extern std::basic_iostream<char> &strm;
int main () {
strm << "Hello, World!\n";
}
Example 3:
Likewise, it seems unclear whether the program below is complete:
#include <iterator>
bool foo (std::istream_iterator<int> a, std::istream_iterator<int> b)
{
return a == b;
}
int main () { }
or whether one should be required to include <istream>.
There are many more examples that demonstrate this lack of a requirement. I believe that in a good number of cases it would be unreasonable to require that a program explicitly include all the headers necessary for a particular template to be specialized, but I think that there are cases such as some of those above where it would be desirable to allow implementations to include only as much as necessary and not more.
Proposed resolution:
For every C++ library header, supply a minimum set of other C++ library headers that are required to be included by that header. The proposed list is below (C++ headers for C Library Facilities, table 12 in 17.4.1.2, p3, are omitted):
+------------+--------------------+ | C++ header |required to include | +============+====================+ |<algorithm> | | +------------+--------------------+ |<bitset> | | +------------+--------------------+ |<complex> | | +------------+--------------------+ |<deque> |<memory> | +------------+--------------------+ |<exception> | | +------------+--------------------+ |<fstream> |<ios> | +------------+--------------------+ |<functional>| | +------------+--------------------+ |<iomanip> |<ios> | +------------+--------------------+ |<ios> |<streambuf> | +------------+--------------------+ |<iosfwd> | | +------------+--------------------+ |<iostream> |<istream>, <ostream>| +------------+--------------------+ |<istream> |<ios> | +------------+--------------------+ |<iterator> | | +------------+--------------------+ |<limits> | | +------------+--------------------+ |<list> |<memory> | +------------+--------------------+ |<locale> | | +------------+--------------------+ |<map> |<memory> | +------------+--------------------+ |<memory> | | +------------+--------------------+ |<new> |<exception> | +------------+--------------------+ |<numeric> | | +------------+--------------------+ |<ostream> |<ios> | +------------+--------------------+ |<queue> |<deque> | +------------+--------------------+ |<set> |<memory> | +------------+--------------------+ |<sstream> |<ios>, <string> | +------------+--------------------+ |<stack> |<deque> | +------------+--------------------+ |<stdexcept> | | +------------+--------------------+ |<streambuf> |<ios> | +------------+--------------------+ |<string> |<memory> | +------------+--------------------+ |<strstream> | | +------------+--------------------+ |<typeinfo> |<exception> | +------------+--------------------+ |<utility> | | +------------+--------------------+ |<valarray> | | +------------+--------------------+ |<vector> |<memory> | +------------+--------------------+
Rationale:
The portability problem is real. A program that works correctly on one implementation might fail on another, because of different header dependencies. This problem was understood before the standard was completed, and it was a conscious design choice.
One possible way to deal with this, as a library extension, would be an <all> header.
Hinnant: It's time we dealt with this issue for C++0X. Reopened.
Section: 22.2.1.4 [locale.codecvt] Status: Open Submitter: Martin Sebor Date: 2002-08-30
View all other issues in [locale.codecvt].
View all issues with Open status.
Discussion:
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.4.2 [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.4.2 [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)?
[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.]
Proposed resolution:
Section: 17 [library] Status: Tentatively Ready Submitter: Matt Austern Date: 2002-10-23
View other active issues in [library].
View all other issues in [library].
View all issues with Tentatively Ready status.
Discussion:
Many function templates have parameters that are passed by value; a typical example is find_if's pred parameter in 25.1.2 [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 [algorithms] and 26 [numerics] are obvious examples. A review of the whole library is necessary.
[ 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. ]
[ 2007-01-12, Howard: First, despite the note above, references are copy constructible. They just aren't assignable. Second, this is very closely related to 92 and should be consistent with that. That issue already says that implementations are allowed to copy function objects. If one passes in a reference, it is copyable, but susceptible to slicing if one passes in a reference to a base. Third, with rvalue reference in the language one only needs to satisfy MoveConstructible to pass an rvalue "by value". Though the function might still copy the function object internally (requiring CopyConstructible). Finally (and fwiw), if we wanted to, it is easy to code all of the std::algorithms such that they do not copy function objects internally. One merely passes them by reference internally if desired (this has been fully implemented and shipped for several years). If this were mandated, it would reverse 92, allowing function objects to reliably maintain state. E.g. the example in 92 would reliably remove only the third element. ]
Proposed resolution:
Recommend NAD.
Rationale:
Generic algorithms will be marked with concepts and these will imply a requirement of MoveConstructible (not CopyConstructible). The signature of the function will then precisely describe and enforce the precise requirements.
Section: 26.3 [complex.numbers] Status: Open Submitter: Gabriel Dos Reis Date: 2002-11-08
View other active issues in [complex.numbers].
View all other issues in [complex.numbers].
View all issues with Open status.
Discussion:
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.3 [complex.numbers] as 26.3/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.3.1 [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.3.7 [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.3.7 [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: 22.2.1.4.2 [locale.codecvt.virtuals] Status: New Submitter: Alberto Barbati Date: 2002-12-24
View all issues with New status.
Discussion:
this DR follows the discussion on the previous thread "codecvt::do_in not consuming external characters". It's just a clarification issue and not a request for a change.
Can do_in()/do_out() produce output characters without consuming input characters as a result of operation on state?
Proposed resolution:
Add a note at the end of 22.2.1.5.2 [lib.locale.codecvt.virtuals], paragraph 3:
[Note: As a result of operations on state, it can return ok or partial and set from_next == from and to_next != to. --end note]
Rationale:
The submitter believes that standard already provides an affirmative answer to the question. However, the current wording has induced a few library implementors to make the incorrect assumption that do_in()/do_out() always consume at least one internal character when they succeed.
The submitter also believes that the proposed resolution is not in conflict with the related issue 76. Moreover, by explicitly allowing operations on state to produce characters, a codecvt implementation may effectively implement N-to-M translations without violating the "one character at a time" principle described in such issue. On a side note, the footnote in the proposed resolution of issue 76 that informally rules out N-to-M translations for basic_filebuf should be removed if this issue is accepted as valid.
Section: 27.6.2.5.1 [ostream.formatted.reqmts] Status: Open Submitter: Martin Sebor Date: 2002-12-27
View all issues with Open status.
Discussion:
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.
[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.]
Proposed re