Doc. no. N2703=08-0213
Date: 2008-07-27
Project: Programming Language C++
Reply to: Howard Hinnant <howard.hinnant@gmail.com>

C++ Standard Library Defect Report List (Revision R58)

Reference ISO/IEC IS 14882:1998(E)

Also see:

This document contains only library issues which have been closed by the Library Working Group (LWG) after being found to be defects in the standard. That is, issues which have a status of DR, TC, or RR. See the Library Closed Issues List for issues closed as non-defects. See the Library Active Issues List for active issues and more information. The introductory material in that document also applies to this document.

Revision History

Defect Reports


1. C library linkage editing oversight

Section: 17.4.2.2 [using.linkage] Status: TC Submitter: Beman Dawes Date: 1997-11-16

View all issues with TC status.

Discussion:

The change specified in the proposed resolution below did not make it into the Standard. This change was accepted in principle at the London meeting, and the exact wording below was accepted at the Morristown meeting.

Proposed resolution:

Change 17.4.2.2 [using.linkage] paragraph 2 from:

It is unspecified whether a name from the Standard C library declared with external linkage has either extern "C" or extern "C++" linkage.

to:

Whether a name from the Standard C library declared with external linkage has extern "C" or extern "C++" linkage is implementation defined. It is recommended that an implementation use extern "C++" linkage for this purpose.


3. Atexit registration during atexit() call is not described

Section: 18.4 [support.start.term] Status: TC Submitter: Steve Clamage Date: 1997-12-12

View all issues with TC status.

Discussion:

We appear not to have covered all the possibilities of exit processing with respect to atexit registration.

Example 1: (C and C++)

    #include <stdlib.h>
    void f1() { }
    void f2() { atexit(f1); }
    
    int main()
    {
        atexit(f2); // the only use of f2
        return 0; // for C compatibility
    }

At program exit, f2 gets called due to its registration in main. Running f2 causes f1 to be newly registered during the exit processing. Is this a valid program? If so, what are its semantics?

Interestingly, neither the C standard, nor the C++ draft standard nor the forthcoming C9X Committee Draft says directly whether you can register a function with atexit during exit processing.

All 3 standards say that functions are run in reverse order of their registration. Since f1 is registered last, it ought to be run first, but by the time it is registered, it is too late to be first.

If the program is valid, the standards are self-contradictory about its semantics.

Example 2: (C++ only)

    
    void F() { static T t; } // type T has a destructor

    int main()
    {
        atexit(F); // the only use of F
    }

Function F registered with atexit has a local static variable t, and F is called for the first time during exit processing. A local static object is initialized the first time control flow passes through its definition, and all static objects are destroyed during exit processing. Is the code valid? If so, what are its semantics?

Section 18.3 "Start and termination" says that if a function F is registered with atexit before a static object t is initialized, F will not be called until after t's destructor completes.

In example 2, function F is registered with atexit before its local static object O could possibly be initialized. On that basis, it must not be called by exit processing until after O's destructor completes. But the destructor cannot be run until after F is called, since otherwise the object could not be constructed in the first place.

If the program is valid, the standard is self-contradictory about its semantics.

I plan to submit Example 1 as a public comment on the C9X CD, with a recommendation that the results be undefined. (Alternative: make it unspecified. I don't think it is worthwhile to specify the case where f1 itself registers additional functions, each of which registers still more functions.)

I think we should resolve the situation in the whatever way the C committee decides.

For Example 2, I recommend we declare the results undefined.

[See reflector message lib-6500 for further discussion.]

Proposed resolution:

Change section 18.3/8 from:

First, objects with static storage duration are destroyed and functions registered by calling atexit are called. Objects with static storage duration are destroyed in the reverse order of the completion of their constructor. (Automatic objects are not destroyed as a result of calling exit().) Functions registered with atexit are called in the reverse order of their registration. A function registered with atexit before an object obj1 of static storage duration is initialized will not be called until obj1's destruction has completed. A function registered with atexit after an object obj2 of static storage duration is initialized will be called before obj2's destruction starts.

to:

First, objects with static storage duration are destroyed and functions registered by calling atexit are called. Non-local objects with static storage duration are destroyed in the reverse order of the completion of their constructor. (Automatic objects are not destroyed as a result of calling exit().) Functions registered with atexit are called in the reverse order of their registration, except that a function is called after any previously registered functions that had already been called at the time it was registered. A function registered with atexit before a non-local object obj1 of static storage duration is initialized will not be called until obj1's destruction has completed. A function registered with atexit after a non-local object obj2 of static storage duration is initialized will be called before obj2's destruction starts. A local static object obj3 is destroyed at the same time it would be if a function calling the obj3 destructor were registered with atexit at the completion of the obj3 constructor.

Rationale:

See 99-0039/N1215, October 22, 1999, by Stephen D. Clamage for the analysis supporting to the proposed resolution.


5. String::compare specification questionable

Section: 21.3.6.8 [string::swap] Status: TC Submitter: Jack Reeves Date: 1997-12-11

View all other issues in [string::swap].

View all issues with TC status.

Duplicate of: 87

Discussion:

At the very end of the basic_string class definition is the signature: int compare(size_type pos1, size_type n1, const charT* s, size_type n2 = npos) const; In the following text this is defined as: returns basic_string<charT,traits,Allocator>(*this,pos1,n1).compare( basic_string<charT,traits,Allocator>(s,n2);

Since the constructor basic_string(const charT* s, size_type n, const Allocator& a = Allocator()) clearly requires that s != NULL and n < npos and further states that it throws length_error if n == npos, it appears the compare() signature above should always throw length error if invoked like so: str.compare(1, str.size()-1, s); where 's' is some null terminated character array.

This appears to be a typo since the obvious intent is to allow either the call above or something like: str.compare(1, str.size()-1, s, strlen(s)-1);

This would imply that what was really intended was two signatures int compare(size_type pos1, size_type n1, const charT* s) const int compare(size_type pos1, size_type n1, const charT* s, size_type n2) const; each defined in terms of the corresponding constructor.

Proposed resolution:

Replace the compare signature in 21.3 [basic.string] (at the very end of the basic_string synopsis) which reads:

int compare(size_type pos1, size_type n1,
            const charT* s, size_type n2 = npos) const;

with:

int compare(size_type pos1, size_type n1,
            const charT* s) const;
int compare(size_type pos1, size_type n1,
            const charT* s, size_type n2) const;

Replace the portion of 21.3.6.8 [string::swap] paragraphs 5 and 6 which read:

int compare(size_type pos, size_type n1,
            charT * s, size_type n2 = npos) const;
Returns:
basic_string<charT,traits,Allocator>(*this, pos, n1).compare(
             basic_string<charT,traits,Allocator>( s, n2))

with:

int compare(size_type pos, size_type n1,
            const charT * s) const;
Returns:
basic_string<charT,traits,Allocator>(*this, pos, n1).compare(
             basic_string<charT,traits,Allocator>( s ))

int compare(size_type pos, size_type n1,
            const charT * s, size_type n2) const;
Returns:
basic_string<charT,traits,Allocator>(*this, pos, n1).compare(
             basic_string<charT,traits,Allocator>( s, n2))

Editors please note that in addition to splitting the signature, the third argument becomes const, matching the existing synopsis.

Rationale:

While the LWG dislikes adding signatures, this is a clear defect in the Standard which must be fixed.  The same problem was also identified in issues 7 (item 5) and 87.


7. String clause minor problems

Section: 21 [strings] Status: TC Submitter: Matt Austern Date: 1997-12-15

View all other issues in [strings].

View all issues with TC status.

Discussion:

(1) In 21.3.6.4 [string::insert], the description of template <class InputIterator> insert(iterator, InputIterator, InputIterator) makes no sense. It refers to a member function that doesn't exist. It also talks about the return value of a void function.

(2) Several versions of basic_string::replace don't appear in the class synopsis.

(3) basic_string::push_back appears in the synopsis, but is never described elsewhere. In the synopsis its argument is const charT, which doesn't makes much sense; it should probably be charT, or possible const charT&.

(4) basic_string::pop_back is missing.

(5) int compare(size_type pos, size_type n1, charT* s, size_type n2 = npos) make no sense. First, it's const charT* in the synopsis and charT* in the description. Second, given what it says in RETURNS, leaving out the final argument will always result in an exception getting thrown. This is paragraphs 5 and 6 of 21.3.6.8 [string::swap]

(6) In table 37, in section 21.1.1 [char.traits.require], there's a note for X::move(s, p, n). It says "Copies correctly even where p is in [s, s+n)". This is correct as far as it goes, but it doesn't go far enough; it should also guarantee that the copy is correct even where s in in [p, p+n). These are two orthogonal guarantees, and neither one follows from the other. Both guarantees are necessary if X::move is supposed to have the same sort of semantics as memmove (which was clearly the intent), and both guarantees are necessary if X::move is actually supposed to be useful.

Proposed resolution:

ITEM 1: In 21.3.5.4 [lib.string::insert], change paragraph 16 to

    EFFECTS: Equivalent to insert(p - begin(), basic_string(first, last)).

ITEM 2:  Not a defect; the Standard is clear.. There are ten versions of replace() in the synopsis, and ten versions in 21.3.5.6 [lib.string::replace].

ITEM 3: Change the declaration of push_back in the string synopsis (21.3, [lib.basic.string]) from:

     void push_back(const charT)

to

     void push_back(charT)

Add the following text immediately after 21.3.5.2 [lib.string::append], paragraph 10.

    void basic_string::push_back(charT c);
    EFFECTS: Equivalent to append(static_cast<size_type>(1), c);

ITEM 4: Not a defect. The omission appears to have been deliberate.

ITEM 5: Duplicate; see issue 5 (and 87).

ITEM 6: In table 37, Replace:

    "Copies correctly even where p is in [s, s+n)."

with:

     "Copies correctly even where the ranges [p, p+n) and [s, s+n) overlap."


8. Locale::global lacks guarantee

Section: 22.1.1.5 [locale.statics] Status: TC Submitter: Matt Austern Date: 1997-12-24

View all issues with TC status.

Discussion:

It appears there's an important guarantee missing from clause 22. We're told that invoking locale::global(L) sets the C locale if L has a name. However, we're not told whether or not invoking setlocale(s) sets the global C++ locale.

The intent, I think, is that it should not, but I can't find any such words anywhere.

Proposed resolution:

Add a sentence at the end of 22.1.1.5 [locale.statics], paragraph 2: 

No library function other than locale::global() shall affect the value returned by locale().


9. Operator new(0) calls should not yield the same pointer

Section: 18.5.1 [new.delete] Status: TC Submitter: Steve Clamage Date: 1998-01-04

View all issues with TC status.

Discussion:

Scott Meyers, in a comp.std.c++ posting: I just noticed that section 3.7.3.1 of CD2 seems to allow for the possibility that all calls to operator new(0) yield the same pointer, an implementation technique specifically prohibited by ARM 5.3.3.Was this prohibition really lifted? Does the FDIS agree with CD2 in the regard? [Issues list maintainer's note: the IS is the same.]

Proposed resolution:

Change the last paragraph of 3.7.3 from:

Any allocation and/or deallocation functions defined in a C++ program shall conform to the semantics specified in 3.7.3.1 and 3.7.3.2.

to:

Any allocation and/or deallocation functions defined in a C++ program, including the default versions in the library, shall conform to the semantics specified in 3.7.3.1 and 3.7.3.2.

Change 3.7.3.1/2, next-to-last sentence, from :

If the size of the space requested is zero, the value returned shall not be a null pointer value (4.10).

to:

Even if the size of the space requested is zero, the request can fail. If the request succeeds, the value returned shall be a non-null pointer value (4.10) p0 different from any previously returned value p1, unless that value p1 was since passed to an operator delete.

5.3.4/7 currently reads:

When the value of the expression in a direct-new-declarator is zero, the allocation function is called to allocate an array with no elements. The pointer returned by the new-expression is non-null. [Note: If the library allocation function is called, the pointer returned is distinct from the pointer to any other object.]

Retain the first sentence, and delete the remainder.

18.5.1 currently has no text. Add the following:

Except where otherwise specified, the provisions of 3.7.3 apply to the library versions of operator new and operator delete.

To 18.5.1.3, add the following text:

The provisions of 3.7.3 do not apply to these reserved placement forms of operator new and operator delete.

Rationale:

See 99-0040/N1216, October 22, 1999, by Stephen D. Clamage for the analysis supporting to the proposed resolution.


11. Bitset minor problems

Section: 23.3.5 [template.bitset] Status: TC Submitter: Matt Austern Date: 1998-01-22

View all other issues in [template.bitset].

View all issues with TC status.

Discussion:

(1) bitset<>::operator[] is mentioned in the class synopsis (23.3.5), but it is not documented in 23.3.5.2.

(2) The class synopsis only gives a single signature for bitset<>::operator[], reference operator[](size_t pos). This doesn't make much sense. It ought to be overloaded on const. reference operator[](size_t pos); bool operator[](size_t pos) const.

(3) Bitset's stream input function (23.3.5.3) ought to skip all whitespace before trying to extract 0s and 1s. The standard doesn't explicitly say that, though. This should go in the Effects clause.

Proposed resolution:

ITEMS 1 AND 2:

In the bitset synopsis (23.3.5 [template.bitset]), replace the member function

    reference operator[](size_t pos);

with the two member functions

    bool operator[](size_t pos) const;
    reference operator[](size_t pos);

Add the following text at the end of 23.3.5.2 [bitset.members], immediately after paragraph 45:

bool operator[](size_t pos) const;
Requires: pos is valid
Throws: nothing
Returns: test(pos)

bitset<N>::reference operator[](size_t pos);
Requires: pos is valid
Throws: nothing
Returns: An object of type bitset<N>::reference such that (*this)[pos] == this->test(pos), and such that (*this)[pos] = val is equivalent to this->set(pos, val);

Rationale:

The LWG believes Item 3 is not a defect. "Formatted input" implies the desired semantics. See 27.6.1.2 [istream.formatted].


13. Eos refuses to die

Section: 27.6.1.2.3 [istream::extractors] Status: TC Submitter: William M. Miller Date: 1998-03-03

View all other issues in [istream::extractors].

View all issues with TC status.

Discussion:

In 27.6.1.2.3, there is a reference to "eos", which is the only one in the whole draft (at least using Acrobat search), so it's undefined.

Proposed resolution:

In 27.6.1.2.3 [istream::extractors], replace "eos" with "charT()"


14. Locale::combine should be const

Section: 22.1.1.3 [locale.members] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale.members].

View all issues with TC status.

Discussion:

locale::combine is the only member function of locale (other than constructors and destructor) that is not const. There is no reason for it not to be const, and good reasons why it should have been const. Furthermore, leaving it non-const conflicts with 22.1.1 paragraph 6: "An instance of a locale is immutable."

History: this member function originally was a constructor. it happened that the interface it specified had no corresponding language syntax, so it was changed to a member function. As constructors are never const, there was no "const" in the interface which was transformed into member "combine". It should have been added at that time, but the omission was not noticed.

Proposed resolution:

In 22.1.1 [locale] and also in 22.1.1.3 [locale.members], add "const" to the declaration of member combine:

template <class Facet> locale combine(const locale& other) const; 

15. Locale::name requirement inconsistent

Section: 22.1.1.3 [locale.members] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale.members].

View all issues with TC status.

Discussion:

locale::name() is described as returning a string that can be passed to a locale constructor, but there is no matching constructor.

Proposed resolution:

In 22.1.1.3 [locale.members], paragraph 5, replace "locale(name())" with "locale(name().c_str())".


16. Bad ctype_byname<char> decl

Section: 22.2.1.4 [locale.codecvt] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale.codecvt].

View all issues with TC status.

Discussion:

The new virtual members ctype_byname<char>::do_widen and do_narrow did not get edited in properly. Instead, the member do_widen appears four times, with wrong argument lists.

Proposed resolution:

The correct declarations for the overloaded members do_narrow and do_widen should be copied from 22.2.1.3 [facet.ctype.special].


17. Bad bool parsing

Section: 22.2.2.1.2 [facet.num.get.virtuals] Status: TC 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 TC status.

Discussion:

This section describes the process of parsing a text boolean value from the input stream. It does not say it recognizes either of the sequences "true" or "false" and returns the corresponding bool value; instead, it says it recognizes only one of those sequences, and chooses which according to the received value of a reference argument intended for returning the result, and reports an error if the other sequence is found. (!) Furthermore, it claims to get the names from the ctype<> facet rather than the numpunct<> facet, and it examines the "boolalpha" flag wrongly; it doesn't define the value "loc"; and finally, it computes wrongly whether to use numeric or "alpha" parsing.

I believe the correct algorithm is "as if":

  // in, err, val, and str are arguments.
  err = 0;
  const numpunct<charT>& np = use_facet<numpunct<charT> >(str.getloc());
  const string_type t = np.truename(), f = np.falsename();
  bool tm = true, fm = true;
  size_t pos = 0;
  while (tm && pos < t.size() || fm && pos < f.size()) {
    if (in == end) { err = str.eofbit; }
    bool matched = false;
    if (tm && pos < t.size()) {
      if (!err && t[pos] == *in) matched = true;
      else tm = false;
    }
    if (fm && pos < f.size()) {
      if (!err && f[pos] == *in) matched = true;
      else fm = false;
    }
    if (matched) { ++in; ++pos; }
    if (pos > t.size()) tm = false;
    if (pos > f.size()) fm = false;
  }
  if (tm == fm || pos == 0) { err |= str.failbit; }
  else                      { val = tm; }
  return in;

Notice this works reasonably when the candidate strings are both empty, or equal, or when one is a substring of the other. The proposed text below captures the logic of the code above.

Proposed resolution:

In 22.2.2.1.2 [facet.num.get.virtuals], in the first line of paragraph 14, change "&&" to "&".

Then, replace paragraphs 15 and 16 as follows:

Otherwise target sequences are determined "as if" by calling the members falsename() and truename() of the facet obtained by use_facet<numpunct<charT> >(str.getloc()). Successive characters in the range [in,end) (see [lib.sequence.reqmts]) are obtained and matched against corresponding positions in the target sequences only as necessary to identify a unique match. The input iterator in is compared to end only when necessary to obtain a character. If and only if a target sequence is uniquely matched, val is set to the corresponding value.

The in iterator is always left pointing one position beyond the last character successfully matched. If val is set, then err is set to str.goodbit; or to str.eofbit if, when seeking another character to match, it is found that (in==end). If val is not set, then err is set to str.failbit; or to (str.failbit|str.eofbit)if the reason for the failure was that (in==end). [Example: for targets true:"a" and false:"abb", the input sequence "a" yields val==true and err==str.eofbit; the input sequence "abc" yields err=str.failbit, with in ending at the 'c' element. For targets true:"1" and false:"0", the input sequence "1" yields val==true and err=str.goodbit. For empty targets (""), any input sequence yields err==str.failbit. --end example]


18. Get(...bool&) omitted

Section: 22.2.2.1.1 [facet.num.get.members] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [facet.num.get.members].

View all issues with TC status.

Discussion:

In the list of num_get<> non-virtual members on page 22-23, the member that parses bool values was omitted from the list of definitions of non-virtual members, though it is listed in the class definition and the corresponding virtual is listed everywhere appropriate.

Proposed resolution:

Add at the beginning of 22.2.2.1.1 [facet.num.get.members] another get member for bool&, copied from the entry in 22.2.2.1 [locale.num.get].


19. "Noconv" definition too vague

Section: 22.2.1.4 [locale.codecvt] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale.codecvt].

View all issues with TC status.

Duplicate of: 10

Discussion:

In the definitions of codecvt<>::do_out and do_in, they are specified to return noconv if "no conversion is needed". This definition is too vague, and does not say normatively what is done with the buffers.

Proposed resolution:

Change the entry for noconv in the table under paragraph 4 in section 22.2.1.4.2 [locale.codecvt.virtuals] to read:

noconv: internT and externT are the same type, and input sequence is identical to converted sequence.

Change the Note in paragraph 2 to normative text as follows:

If returns noconv, internT and externT are the same type and the converted sequence is identical to the input sequence [from,from_next). to_next is set equal to to, the value of state is unchanged, and there are no changes to the values in [to, to_limit).


20. Thousands_sep returns wrong type

Section: 22.2.3.1.2 [facet.numpunct.virtuals] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all issues with TC status.

Discussion:

The synopsis for numpunct<>::do_thousands_sep, and the definition of numpunct<>::thousands_sep which calls it, specify that it returns a value of type char_type. Here it is erroneously described as returning a "string_type".

Proposed resolution:

In 22.2.3.1.2 [facet.numpunct.virtuals], above paragraph 2, change "string_type" to "char_type".


21. Codecvt_byname<> instantiations

Section: 22.1.1.1.1 [locale.category] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale.category].

View all issues with TC status.

Discussion:

In the second table in the section, captioned "Required instantiations", the instantiations for codecvt_byname<> have been omitted. These are necessary to allow users to construct a locale by name from facets.

Proposed resolution:

Add in 22.1.1.1.1 [locale.category] to the table captioned "Required instantiations", in the category "ctype" the lines

codecvt_byname<char,char,mbstate_t>,
codecvt_byname<wchar_t,char,mbstate_t> 

22. Member open vs. flags

Section: 27.8.1.9 [ifstream.members] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [ifstream.members].

View all issues with TC status.

Discussion:

The description of basic_istream<>::open leaves unanswered questions about how it responds to or changes flags in the error status for the stream. A strict reading indicates that it ignores the bits and does not change them, which confuses users who do not expect eofbit and failbit to remain set after a successful open. There are three reasonable resolutions: 1) status quo 2) fail if fail(), ignore eofbit 3) clear failbit and eofbit on call to open().

Proposed resolution:

In 27.8.1.9 [ifstream.members] paragraph 3, and in 27.8.1.13 [ofstream.members] paragraph 3, under open() effects, add a footnote:

A successful open does not change the error state.

Rationale:

This may seem surprising to some users, but it's just an instance of a general rule: error flags are never cleared by the implementation. The only way error flags are are ever cleared is if the user explicitly clears them by hand.

The LWG believed that preserving this general rule was important enough so that an exception shouldn't be made just for this one case. The resolution of this issue clarifies what the LWG believes to have been the original intent.


24. "do_convert" doesn't exist

Section: 22.2.1.4 [locale.codecvt] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale.codecvt].

View all issues with TC status.

Duplicate of: 72

Discussion:

The description of codecvt<>::do_out and do_in mentions a symbol "do_convert" which is not defined in the standard. This is a leftover from an edit, and should be "do_in and do_out".

Proposed resolution:

In 22.2.1.4 [locale.codecvt], paragraph 3, change "do_convert" to "do_in or do_out". Also, in 22.2.1.4.2 [locale.codecvt.virtuals], change "do_convert()" to "do_in or do_out".


25. String operator<< uses width() value wrong

Section: 21.3.8.9 [string.io] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [string.io].

View all issues with TC status.

Duplicate of: 67

Discussion:

In the description of operator<< applied to strings, the standard says that uses the smaller of os.width() and str.size(), to pad "as described in stage 3" elsewhere; but this is inconsistent, as this allows no possibility of space for padding.

Proposed resolution:

Change 21.3.8.9 [string.io] paragraph 4 from:

    "... where n is the smaller of os.width() and str.size(); ..."

to:

    "... where n is the larger of os.width() and str.size(); ..."


26. Bad sentry example

Section: 27.6.1.1.3 [istream::sentry] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [istream::sentry].

View all issues with TC status.

Discussion:

In paragraph 6, the code in the example:

  template <class charT, class traits = char_traits<charT> >
  basic_istream<charT,traits>::sentry(
           basic_istream<charT,traits>& is, bool noskipws = false) {
      ...
      int_type c;
      typedef ctype<charT> ctype_type;
      const ctype_type& ctype = use_facet<ctype_type>(is.getloc());
      while ((c = is.rdbuf()->snextc()) != traits::eof()) {
        if (ctype.is(ctype.space,c)==0) {
          is.rdbuf()->sputbackc (c);
          break;
        }
      }
      ...
   }

fails to demonstrate correct use of the facilities described. In particular, it fails to use traits operators, and specifies incorrect semantics. (E.g. it specifies skipping over the first character in the sequence without examining it.)

Proposed resolution:

Remove the example above from 27.6.1.1.3 [istream::sentry] paragraph 6.

Rationale:

The originally proposed replacement code for the example was not correct. The LWG tried in Kona and again in Tokyo to correct it without success. In Tokyo, an implementor reported that actual working code ran over one page in length and was quite complicated. The LWG decided that it would be counter-productive to include such a lengthy example, which might well still contain errors.


27. String::erase(range) yields wrong iterator

Section: 21.3.6.5 [string::erase] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [string::erase].

View all issues with TC status.

Discussion:

The string::erase(iterator first, iterator last) is specified to return an element one place beyond the next element after the last one erased. E.g. for the string "abcde", erasing the range ['b'..'d') would yield an iterator for element 'e', while 'd' has not been erased.

Proposed resolution:

In 21.3.6.5 [string::erase], paragraph 10, change:

Returns: an iterator which points to the element immediately following _last_ prior to the element being erased.

to read

Returns: an iterator which points to the element pointed to by _last_ prior to the other elements being erased.


28. Ctype<char>is ambiguous

Section: 22.2.1.3.2 [facet.ctype.char.members] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [facet.ctype.char.members].

View all issues with TC status.

Duplicate of: 236

Discussion:

The description of the vector form of ctype<char>::is can be interpreted to mean something very different from what was intended. Paragraph 4 says

Effects: The second form, for all *p in the range [low, high), assigns vec[p-low] to table()[(unsigned char)*p].

This is intended to copy the value indexed from table()[] into the place identified in vec[].

Proposed resolution:

Change 22.2.1.3.2 [facet.ctype.char.members], paragraph 4, to read

Effects: The second form, for all *p in the range [low, high), assigns into vec[p-low] the value table()[(unsigned char)*p].


29. Ios_base::init doesn't exist

Section: 27.3.1 [narrow.stream.objects] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [narrow.stream.objects].

View all issues with TC status.

Discussion:

Sections 27.3.1 [narrow.stream.objects] and 27.3.2 [wide.stream.objects] mention a function ios_base::init, which is not defined. Probably they mean basic_ios<>::init, defined in 27.4.4.1 [basic.ios.cons], paragraph 3.

Proposed resolution:

[R12: modified to include paragraph 5.]

In 27.3.1 [narrow.stream.objects] paragraph 2 and 5, change

ios_base::init

to

basic_ios<char>::init

Also, make a similar change in 27.3.2 [wide.stream.objects] except it should read

basic_ios<wchar_t>::init


30. Wrong header for LC_*

Section: 22.1.1.1.1 [locale.category] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale.category].

View all issues with TC status.

Discussion:

Paragraph 2 implies that the C macros LC_CTYPE etc. are defined in <cctype>, where they are in fact defined elsewhere to appear in <clocale>.

Proposed resolution:

In 22.1.1.1.1 [locale.category], paragraph 2, change "<cctype>" to read "<clocale>".


31. Immutable locale values

Section: 22.1.1 [locale] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale].

View all issues with TC status.

Duplicate of: 378

Discussion:

Paragraph 6, says "An instance of locale is immutable; once a facet reference is obtained from it, ...". This has caused some confusion, because locale variables are manifestly assignable.

Proposed resolution:

In 22.1.1 [locale] replace paragraph 6

An instance of locale is immutable; once a facet reference is obtained from it, that reference remains usable as long as the locale value itself exists.

with

Once a facet reference is obtained from a locale object by calling use_facet<>, that reference remains usable, and the results from member functions of it may be cached and re-used, as long as some locale object refers to that facet.


32. Pbackfail description inconsistent

Section: 27.5.2.4.4 [streambuf.virt.pback] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all issues with TC status.

Discussion:

The description of the required state before calling virtual member basic_streambuf<>::pbackfail requirements is inconsistent with the conditions described in 27.5.2.2.4 [lib.streambuf.pub.pback] where member sputbackc calls it. Specifically, the latter says it calls pbackfail if:

    traits::eq(c,gptr()[-1]) is false

where pbackfail claims to require:

    traits::eq(*gptr(),traits::to_char_type(c)) returns false

It appears that the pbackfail description is wrong.

Proposed resolution:

In 27.5.2.4.4 [streambuf.virt.pback], paragraph 1, change:

"traits::eq(*gptr(),traits::to_char_type( c))"

to

"traits::eq(traits::to_char_type(c),gptr()[-1])"

Rationale:

Note deliberate reordering of arguments for clarity in addition to the correction of the argument value.


33. Codecvt<> mentions from_type

Section: 22.2.1.4 [locale.codecvt] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale.codecvt].

View all issues with TC status.

Duplicate of: 43

Discussion:

In the table defining the results from do_out and do_in, the specification for the result error says

encountered a from_type character it could not convert

but from_type is not defined. This clearly is intended to be an externT for do_in, or an internT for do_out.

Proposed resolution:

In 22.2.1.4.2 [locale.codecvt.virtuals] paragraph 4, replace the definition in the table for the case of _error_ with

encountered a character in [from,from_end) that it could not convert.


34. True/falsename() not in ctype<>

Section: 22.2.2.2.2 [facet.num.put.virtuals] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [facet.num.put.virtuals].

View all issues with TC status.

Discussion:

In paragraph 19, Effects:, members truename() and falsename are used from facet ctype<charT>, but it has no such members. Note that this is also a problem in 22.2.2.1.2, addressed in (4).

Proposed resolution:

In 22.2.2.2.2 [facet.num.put.virtuals], paragraph 19, in the Effects: clause for member put(...., bool), replace the initialization of the string_type value s as follows:

const numpunct& np = use_facet<numpunct<charT> >(loc);
string_type s = val ? np.truename() : np.falsename(); 

35. No manipulator unitbuf in synopsis

Section: 27.4 [iostreams.base] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all issues with TC status.

Discussion:

In 27.4.5.1 [fmtflags.manip], we have a definition for a manipulator named "unitbuf". Unlike other manipulators, it's not listed in synopsis. Similarly for "nounitbuf".

Proposed resolution:

Add to the synopsis for <ios> in 27.4 [iostreams.base], after the entry for "nouppercase", the prototypes:

ios_base& unitbuf(ios_base& str);
ios_base& nounitbuf(ios_base& str); 

36. Iword & pword storage lifetime omitted

Section: 27.4.2.5 [ios.base.storage] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [ios.base.storage].

View all issues with TC status.

Discussion:

In the definitions for ios_base::iword and pword, the lifetime of the storage is specified badly, so that an implementation which only keeps the last value stored appears to conform. In particular, it says:

The reference returned may become invalid after another call to the object's iword member with a different index ...

This is not idle speculation; at least one implementation was done this way.

Proposed resolution:

Add in 27.4.2.5 [ios.base.storage], in both paragraph 2 and also in paragraph 4, replace the sentence:

The reference returned may become invalid after another call to the object's iword [pword] member with a different index, after a call to its copyfmt member, or when the object is destroyed.

with:

The reference returned is invalid after any other operations on the object. However, the value of the storage referred to is retained, so that until the next call to copyfmt, calling iword [pword] with the same index yields another reference to the same value.

substituting "iword" or "pword" as appropriate.


37. Leftover "global" reference

Section: 22.1.1 [locale] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [locale].

View all issues with TC status.

Discussion:

In the overview of locale semantics, paragraph 4, is the sentence

If Facet is not present in a locale (or, failing that, in the global locale), it throws the standard exception bad_cast.

This is not supported by the definition of use_facet<>, and represents semantics from an old draft.

Proposed resolution:

In 22.1.1 [locale], paragraph 4, delete the parenthesized expression

(or, failing that, in the global locale)


38. Facet definition incomplete

Section: 22.1.2 [locale.global.templates] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all issues with TC status.

Discussion:

It has been noticed by Esa Pulkkinen that the definition of "facet" is incomplete. In particular, a class derived from another facet, but which does not define a member id, cannot safely serve as the argument F to use_facet<F>(loc), because there is no guarantee that a reference to the facet instance stored in loc is safely convertible to F.

Proposed resolution:

In the definition of std::use_facet<>(), replace the text in paragraph 1 which reads:

Get a reference to a facet of a locale.

with:

Requires: Facet is a facet class whose definition contains the public static member id as defined in 22.1.1.1.2 [locale.facet].

[ Kona: strike as overspecification the text "(not inherits)" from the original resolution, which read "... whose definition contains (not inherits) the public static member id..." ]


39. istreambuf_iterator<>::operator++(int) definition garbled

Section: 24.5.3.4 [istreambuf.iterator::op++] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all issues with TC status.

Discussion:

Following the definition of istreambuf_iterator<>::operator++(int) in paragraph 3, the standard contains three lines of garbage text left over from a previous edit.

istreambuf_iterator<charT,traits> tmp = *this;
sbuf_->sbumpc();
return(tmp); 

Proposed resolution:

In 24.5.3.4 [istreambuf.iterator::op++], delete the three lines of code at the end of paragraph 3.


40. Meaningless normative paragraph in examples

Section: 22.2.8 [facets.examples] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [facets.examples].

View all issues with TC status.

Discussion:

Paragraph 3 of the locale examples is a description of part of an implementation technique that has lost its referent, and doesn't mean anything.

Proposed resolution:

Delete 22.2.8 [facets.examples] paragraph 3 which begins "This initialization/identification system depends...", or (at the editor's option) replace it with a place-holder to keep the paragraph numbering the same.


41. Ios_base needs clear(), exceptions()

Section: 27.4.2 [ios.base] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [ios.base].

View all issues with TC status.

Duplicate of: 157

Discussion:

The description of ios_base::iword() and pword() in 27.4.2.4 [ios.members.static], say that if they fail, they "set badbit, which may throw an exception". However, ios_base offers no interface to set or to test badbit; those interfaces are defined in basic_ios<>.

Proposed resolution:

Change the description in 27.4.2.5 [ios.base.storage] in paragraph 2, and also in paragraph 4, as follows. Replace

If the function fails it sets badbit, which may throw an exception.

with

If the function fails, and *this is a base sub-object of a basic_ios<> object or sub-object, the effect is equivalent to calling basic_ios<>::setstate(badbit) on the derived object (which may throw failure).

[Kona: LWG reviewed wording; setstate(failbit) changed to setstate(badbit).]


42. String ctors specify wrong default allocator

Section: 21.3 [basic.string] Status: TC Submitter: Nathan Myers Date: 1998-08-06

View other active issues in [basic.string].

View all other issues in [basic.string].

View all issues with TC status.

Discussion:

The basic_string<> copy constructor:

basic_string(const basic_string& str, size_type pos = 0,
             size_type n = npos, const Allocator& a = Allocator()); 

specifies an Allocator argument default value that is counter-intuitive. The natural choice for a the allocator to copy from is str.get_allocator(). Though this cannot be expressed in default-argument notation, overloading suffices.

Alternatively, the other containers in Clause 23 (deque, list, vector) do not have this form of constructor, so it is inconsistent, and an evident source of confusion, for basic_string<> to have it, so it might better be removed.

Proposed resolution:

In 21.3 [basic.string], replace the declaration of the copy constructor as follows:

basic_string(const basic_string& str);
basic_string(const basic_string& str, size_type pos, size_type n = npos,
             const Allocator& a = Allocator());

In 21.3.1 [string.require], replace the copy constructor declaration as above. Add to paragraph 5, Effects:

In the first form, the Allocator value used is copied from str.get_allocator().

Rationale:

The LWG believes the constructor is actually broken, rather than just an unfortunate design choice.

The LWG considered two other possible resolutions:

A. In 21.3 [basic.string], replace the declaration of the copy constructor as follows:

basic_string(const basic_string& str, size_type pos = 0,
             size_type n = npos);
basic_string(const basic_string& str, size_type pos,
             size_type n, const Allocator& a); 

In 21.3.1 [string.require], replace the copy constructor declaration as above. Add to paragraph 5, Effects:

When no Allocator argument is provided, the string is constructed using the value str.get_allocator().

B. In 21.3 [basic.string], and also in 21.3.1 [string.require], replace the declaration of the copy constructor as follows:

basic_string(const basic_string& str, size_type pos = 0,
             size_type n = npos); 

The proposed resolution reflects the original intent of the LWG. It was also noted by Pete Becker that this fix "will cause a small amount of existing code to now work correctly."

[ Kona: issue editing snafu fixed - the proposed resolution now correctly reflects the LWG consensus. ]


44. Iostreams use operator== on int_type values

Section: 27 [input.output] Status: WP Submitter: Nathan Myers Date: 1998-08-06

View all other issues in [input.output].

View all issues with WP status.

Discussion:

Many of the specifications for iostreams specify that character values or their int_type equivalents are compared using operators == or !=, though in other places traits::eq() or traits::eq_int_type is specified to be used throughout. This is an inconsistency; we should change uses of == and != to use the traits members instead.

Proposed resolution:

[Pre-Kona: Dietmar supplied wording]

List of changes to clause 27:

  1. In lib.basic.ios.members paragraph 13 (postcondition clause for 'fill(cT)') change
         fillch == fill()
    
    to
         traits::eq(fillch, fill())
    
  2. In lib.istream.unformatted paragraph 7 (effects clause for 'get(cT,streamsize,cT)'), third bullet, change
         c == delim for the next available input character c
    
    to
         traits::eq(c, delim) for the next available input character c
    
  3. In lib.istream.unformatted paragraph 12 (effects clause for 'get(basic_streambuf<cT,Tr>&,cT)'), third bullet, change
         c == delim for the next available input character c
    
    to
         traits::eq(c, delim) for the next available input character c
    
  4. In lib.istream.unformatted paragraph 17 (effects clause for 'getline(cT,streamsize,cT)'), second bullet, change
         c == delim for the next available input character c
    
    to
         traits::eq(c, delim) for the next available input character c
      
  5. In lib.istream.unformatted paragraph 24 (effects clause for 'ignore(int,int_type)'), second bullet, change
         c == delim for the next available input character c
    
    to
         traits::eq_int_type(c, delim) for the next available input
         character c
    
  6. In lib.istream.unformatted paragraph 25 (notes clause for 'ignore(int,int_type)'), second bullet, change
         The last condition will never occur if delim == traits::eof()
    
    to
         The last condition will never occur if
         traits::eq_int_type(delim, traits::eof()).
    
  7. In lib.istream.sentry paragraph 6 (example implementation for the sentry constructor) change
         while ((c = is.rdbuf()->snextc()) != traits::eof()) {
    
    to
         while (!traits::eq_int_type(c = is.rdbuf()->snextc(), traits::eof())) {
    

List of changes to Chapter 21:

  1. In lib.string::find paragraph 1 (effects clause for find()), second bullet, change
         at(xpos+I) == str.at(I) for all elements ...
    
    to
         traits::eq(at(xpos+I), str.at(I)) for all elements ...
    
  2. In lib.string::rfind paragraph 1 (effects clause for rfind()), second bullet, change
         at(xpos+I) == str.at(I) for all elements ...
    
    to
         traits::eq(at(xpos+I), str.at(I)) for all elements ...
    
  3. In lib.string::find.first.of paragraph 1 (effects clause for find_first_of()), second bullet, change
         at(xpos+I) == str.at(I) for all elements ...
    
    to
         traits::eq(at(xpos+I), str.at(I)) for all elements ...
    
  4. In lib.string::find.last.of paragraph 1 (effects clause for find_last_of()), second bullet, change
         at(xpos+I) == str.at(I) for all elements ...
    
    to
         traits::eq(at(xpos+I), str.at(I)) for all elements ...
    
  5. In lib.string::find.first.not.of paragraph 1 (effects clause for find_first_not_of()), second bullet, change
         at(xpos+I) == str.at(I) for all elements ...
    
    to
         traits::eq(at(xpos+I), str.at(I)) for all elements ...
    
  6. In lib.string::find.last.not.of paragraph 1 (effects clause for find_last_not_of()), second bullet, change
         at(xpos+I) == str.at(I) for all elements ...
    
    to
         traits::eq(at(xpos+I), str.at(I)) for all elements ...
    
  7. In lib.string.ios paragraph 5 (effects clause for getline()), second bullet, change
         c == delim for the next available input character c 
    
    to
         traits::eq(c, delim) for the next available input character c 
    

Notes: