Doc. no. N3318=12-0008
Date: 2012-01-16
Project: Programming Language C++
Reply to: Alisdair Meredith <lwgchair@gmail.com>

C++ Standard Library Active Issues List (Revision R77)

Revised 2012-01-16 at 20:01:49 UTC

Reference ISO/IEC IS 14882:2011(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 INCITS PL22.16 and ISO WG21 C++ Standards Committee. Issues represent potential defects in the ISO/IEC IS 14882:2011(E) document.

This document contains only library issues which are actively being considered by the Library Working Group, i.e., issues which have a status of New, Open, Ready, or Review. See Library Defect Reports List for issues considered defects and Library Closed Issues List for issues considered closed.

The issues in these lists are not necessarily formal ISO Defect Reports (DR's). While some issues will eventually be elevated to official Defect Report status, other issues will be disposed of in other ways. See Issue Status.

Prior to Revision 14, library issues lists existed in two slightly different versions; a Committee Version and a Public Version. Beginning with Revision 14 the two versions were combined into a single version.

This document includes [bracketed italicized notes] as a reminder to the LWG of current progress on issues. Such notes are strictly unofficial and should be read with caution as they may be incomplete or incorrect. Be aware that LWG support for a particular resolution can quickly change if new viewpoints or killer examples are presented in subsequent discussions.

For the most current official version of this document see http://www.open-std.org/jtc1/sc22/wg21/. Requests for further information about this document should include the document number above, reference ISO/IEC 14882:2011(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.

How to submit an issue

  1. Mail your issue to the author of this list.
  2. Specify a short descriptive title. If you fail to do so, the subject line of your mail will be used as the issue title.
  3. If the "From" on your email is not the name you wish to appear as issue submitter, then specify issue submitter.
  4. Provide a brief discussion of the problem you wish to correct. Refer to the latest working draft or standard using [section.tag] and paragraph numbers where appropriate.
  5. Provide proposed wording. This should indicate exactly how you want the standard to be changed. General solution statements belong in the discussion area. This area contains very clear and specific directions on how to modify the current draft. If you are not sure how to word a solution, you may omit this part. But your chances of a successful issue greatly increase if you attempt wording.
  6. It is not necessary for you to use html markup. However, if you want to, you can <ins>insert text like this</ins> and <del>delete text like this</del>. The only strict requirement is to communicate clearly to the list maintainer exactly how you want your issue to look.
  7. It is not necessary for you to specify other html font/formatting mark-up, but if you do the list maintainer will attempt to respect your formatting wishes (as described by html markup, or other common idioms).
  8. It is not necessary for you to specify open date or last modified date (the date of your mail will be used).
  9. It is not necessary for you to cross reference other issues, but you can if you like. You do not need to form the hyperlinks when you do, the list maintainer will take care of that.
  10. One issue per email is best.
  11. Between the time you submit the issue, and the next mailing deadline (date at the top of the Revision History), you own this issue. You control the content, the stuff that is right, the stuff that is wrong, the format, the misspellings, etc. You can even make the issue disappear if you want. Just let the list maintainer know how you want it to look, and he will try his best to accommodate you. After the issue appears in an official mailing, you no longer enjoy exclusive ownership of it.

Revision History

Issue Status

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.

Deferred - The LWG has discussed the issue, is not yet ready to move the issue forward, but neither does it deem the issue significant enough to delay publishing a standard or Technical Report. A typical deferred issue would be seeking to clarify wording that might be technically correct, but easily mis-read.

A Proposed Resolution for a deferred issue is still not be construed as the view of LWG. Comments on the current state of discussions are often given at the end of open issues in an italic font. Such comments are for information only and should not be given undue importance.

Dup - The LWG has reached consensus that the issue is a duplicate of another issue, and will not be further dealt with. A Rationale identifies the duplicated issue's issue number.

NAD - The LWG has reached consensus that the issue is not a defect in the Standard.

NAD Editorial - The LWG has reached consensus that the issue can either be handled editorially, or is handled by a paper (usually linked to in the rationale).

NAD Concepts - The LWG has reached consensus that the issue is NAD for now, but represents a real issue when the library is done with language-supported concepts.

NAD Future - In addition to the regular status, the LWG believes that this issue should be revisited at the next revision of the standard.

Review - Exact wording of a Proposed Resolution is now available for review on an issue for which the LWG previously reached informal consensus.

Ready - The LWG has reached consensus that the issue is a defect in the Standard, the Proposed Resolution is correct, and the issue is ready to forward to the full committee for further action as a Defect Report (DR).

Resolved - The LWG has reached consensus that the issue is a defect in the Standard, but the resolution adopted to resolve the issue came via some other mechanism than this issue in the list - typically by applying a formal paper, occasionally as a side effect of consolidating several interacting issue resolutions into a single issue.

DR - (Defect Report) - The full WG21/PL22.16 committee has voted to forward the issue to the Project Editor to be processed as a Potential Defect Report. The Project Editor reviews the issue, and then forwards it to the WG21 Convenor, who returns it to the full committee for final disposition. This issues list accords the status of DR to all these Defect Reports regardless of where they are in that process.

TC1 - (Technical Corrigenda 1) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution as a Technical Corrigenda. Action on this issue is thus complete and no further action is possible under ISO rules.

CD1 - (Committee Draft 2008) - The full WG21/PL22.16 committee has voted to accept the Defect Report's Proposed Resolution into the Fall 2008 Committee Draft.

TRDec - (Decimal TR defect) - The LWG has voted to accept the Defect Report's Proposed Resolution into the Decimal TR. Action on this issue is thus complete and no further action is expected.

WP - (Working Paper) - The proposed resolution has not been accepted as a Technical Corrigendum, but the full WG21/PL22.16 committee has voted to apply the Defect Report's Proposed Resolution to the working paper.

Tentatively - This is a status qualifier. The issue has been reviewed online, or at an unofficial meeting, but not in an official meeting, and some support has been formed for the qualified status. Tentatively qualified issues may be moved to the unqualified status and forwarded to full committee (if Ready) within the same meeting. Unlike Ready issues, Tentatively Ready issues will be reviewed in subcommittee prior to forwarding to full committee. When a status is qualified with Tentatively, the issue is still considered active.

Pending - This is a status qualifier. When prepended to a status this indicates the issue has been processed by the committee, and a decision has been made to move the issue to the associated unqualified status. However for logistical reasons the indicated outcome of the issue has not yet appeared in the latest working paper.

Issues are always given the status of New when they first appear on the issues list. They may progress to Open or Review while the LWG is actively working on them. When the LWG has reached consensus on the disposition of an issue, the status will then change to Dup, NAD, or Ready as appropriate. Once the full J16 committee votes to forward Ready issues to the Project Editor, they are given the status of Defect Report ( DR). These in turn may become the basis for Technical Corrigenda (TC1), or are closed without action other than a Record of Response (Resolved ). The intent of this LWG process is that only issues which are truly defects in the Standard move to the formal ISO DR status.

Active Issues


1169. num_get not fully compatible with strto*

Section: 22.4.2.1.2 [facet.num.get.virtuals] Status: Review Submitter: Cosmin Truta Opened: 2009-07-04 Last modified: 2012-01-14

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

View all issues with Review status.

Discussion:

As specified in the latest draft, N2914, num_get is still not fully compatible with the following C functions: strtoul, strtoull, strtof and strtod.

In C, when conversion of a string to an unsigned integer type falls outside the representable range, strtoul and strtoull return ULONG_MAX and ULLONG_MAX, respectively, regardless whether the input field represents a positive or a negative value. On the other hand, the result of num_get conversion of negative values to unsigned integer types is zero. This raises a compatibility issue.

Moreover, in C, when conversion of a string to a floating-point type falls outside the representable range, strtof, strtod and strtold return ±HUGE_VALF, ±HUGE_VAL and ±HUGE_VALL, respectively. On the other hand, the result of num_get conversion of such out-of-range floating-point values results in the most positive/negative representable value. Although many C library implementations do implement HUGE_VAL (etc.) as the highest representable (which is, usually, the infinity), this isn't required by the C standard. The C library specification makes no statement regarding the value of HUGE_VAL and friends, which potentially raises the same compatibility issue as in the above case of unsigned integers. In addition, neither C nor C++ define symbolic constants for the maximum representable floating-point values (they only do so only for the maximum representable finite floating-point values), which raises a usability issue (it would be hard for the programmer to check the result of num_get against overflow).

As such, we propose to adjust the specification of num_get to closely follow the behavior of all of its underlying C functions.

[ 2010 Rapperswil: ]

Some concern that this is changing the specification for an existing C++03 function, but it was pointed out that this was underspecified as resolved by issue 23. This is clean-up for that issue in turn. Some concern that we are trying to solve the same problem in both clause 22 and 27.

Bill: There's a change here as to whether val is stored to in an error case.

Pablo: Don't think this changes whether val is stored to or not, but changes the value that is stored.

Bill: Remembers having skirmishes with customers and testers as to whether val is stored to, and the resolution was not to store in error cases.

Howard: Believes since C++03 we made a change to always store in overflow.

Everyone took some time to review the issue.

Pablo: C++98 definitely did not store any value during an error condition.

Dietmar: Depends on the question of what is considered an error, and whether overflow is an error or not, which was the crux of LWG 23.

Pablo: Yes, but given the "zero, if the conversion function fails to convert the entire field", we are requiring every error condition to store.

Bill: When did this happen?

Alisdair: One of the last two or three meetings.

Dietmar: To store a value in case of failure is a very bad idea.

Move to Open, needs more study.

[2011-03-24 Madrid meeting]

Move to deferred

[ 2011 Bloomington ]

The proposed wording looks good, no-one sure why this was held back before. Move to Review.

Proposed resolution:

Change 22.4.2.1.2 [facet.num.get.virtuals] as follows:

Stage 3: The sequence of chars accumulated in stage 2 (the field) is converted to a numeric value by the rules of one of the functions declared in the header <cstdlib>:

The numeric value to be stored can be one of:

The resultant numeric value is stored in val. If the conversion function fails to convert the entire field, or if the field represents a value outside the range of representable values, ios_base::failbit is assigned to err.


1175. unordered complexity

Section: 23.2.5 [unord.req] Status: Review Submitter: Pablo Halpern Opened: 2009-07-17 Last modified: 2012-01-14

View all other issues in [unord.req].

View all issues with Review status.

Discussion:

When I look at the unordered_* constructors, I think the complexity is poorly described and does not follow the style of the rest of the standard.

The complexity for the default constructor is specified as constant. Actually, it is proportional to n, but there are no invocations of value_type constructors or other value_type operations.

For the iterator-based constructor the complexity should be:

Complexity: exactly n calls to construct value_type from InputIterator::value_type (where n = distance(f,l)). The number of calls to key_equal::operator() is proportional to n in the average case and n*n in the worst case.

[ 2010 Rapperswil: ]

Concern that the current wording may require O(1) where that cannot be delivered. We need to look at both the clause 23 requirements tables and the constructor description of each unordered container to be sure.

Howard suggests NAD Editorial as we updated the container requirement tables since this issue was written.

Daniel offers to look deeper, and hopefully produce wording addressing any outstanding concerns at the next meeting.

Move to Open.

[2011-02-26: Daniel provides wording]

I strongly suggest to clean-up the differences between requirement tables and individual specifications. In the usual way, the most specific specifications wins, which is in this case the wrong one. In regard to the concern expressed about missing DefaultConstructible requirements of the value type I disagree: The function argument n is no size-control parameter, but only some effective capacity parameter: No elements will be value-initialized by these constructors. The necessary requirement for the value type, EmplaceConstructible into *this, is already listed in Table 103 — Unordered associative container requirements. Another part of the proposed resolution is the fact that there is an inconsistency of the complexity counting when both a range and a bucket count is involved compared to constructions where only bucket counts are provided: E.g. the construction X a(n); has a complexity of n bucket allocations, but this part of the work is omitted for X a(i, j, n);, even though it is considerable larger (in the average case) for n ≫ distance(i, j).

[2011-03-24 Madrid meeting]

Move to deferred

[ 2011 Bloomington ]

The proposed wording looks good. Move to Review.

Proposed resolution:

  1. Modify the following rows in Table 103 — Unordered associative container requirements to add the explicit bucket allocation overhead of some constructions. As editorial recommendation it is suggested not to shorten the sum 𝒪(n) + 𝒪(N) to 𝒪(n + N), because two different work units are involved.

    Table 103 — Unordered associative container requirements (in addition to container)
    Expression Return type Assertion/note pre-/post-condition Complexity
    X(i, j, n, hf, eq)
    X a(i, j, n, hf, eq)
    X
    Effects: Constructs an empty container with at least n
    buckets, using hf as the hash function and eq as the key
    equality predicate, and inserts elements from [i, j) into it.
    Average case 𝒪(n) + 𝒪(N) (N is distance(i, j)),
    worst case 𝒪(n) + 𝒪(N2)
    X(i, j, n, hf)
    X a(i, j, n, hf)
    X
    Effects: Constructs an empty container with at least n
    buckets, using hf as the hash function and key_equal() as the key
    equality predicate, and inserts elements from [i, j) into it.
    Average case 𝒪(n) + 𝒪(N) (N is distance(i, j)),
    worst case 𝒪(n) + 𝒪(N2)
    X(i, j, n)
    X a(i, j, n)
    X
    Effects: Constructs an empty container with at least n
    buckets, using hasher() as the hash function and key_equal() as the key
    equality predicate, and inserts elements from [i, j) into it.
    Average case 𝒪(n) + 𝒪(N) (N is distance(i, j)),
    worst case 𝒪(n) + 𝒪(N2)
  2. Modify 23.5.4.2 [unord.map.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):

    explicit unordered_map(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    

    1 Effects: Constructs an empty unordered_map using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-definedimpldefdefault number of buckets in unordered_map. max_load_factor() returns 1.0.

    2 Complexity: Constant if n is not provided, otherwise linear in n to construct the buckets.

    template <class InputIterator>
    unordered_map(InputIterator f, InputIterator l,
                  size_type n = see below,
                  const hasher& hf = hasher(),
                  const key_equal& eql = key_equal(),
                  const allocator_type& a = allocator_type());
    

    3 Effects: Constructs an empty unordered_map using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-definedimpldefdefault number of buckets in unordered_map. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.

    4 Complexity: Average case linear, worst case quadraticConstant if n is not provided, else linear in n to construct the buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to distance(f, l).

  3. Modify 23.5.5.2 [unord.multimap.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):

    explicit unordered_multimap(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    

    1 Effects: Constructs an empty unordered_multimap using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-definedimpldefdefault number of buckets in unordered_multimap. max_load_factor() returns 1.0.

    2 Complexity: Constant if n is not provided, otherwise linear in n to construct the buckets.

    template <class InputIterator>
    unordered_multimap(InputIterator f, InputIterator l,
                       size_type n = see below,
                       const hasher& hf = hasher(),
                       const key_equal& eql = key_equal(),
                       const allocator_type& a = allocator_type());
    

    3 Effects: Constructs an empty unordered_multimap using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-definedimpldefdefault number of buckets in unordered_multimap. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.

    4 Complexity: Average case linear, worst case quadraticConstant if n is not provided, else linear in n to construct the buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to distance(f, l).

  4. Modify 23.5.6.2 [unord.set.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):

    explicit unordered_set(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    

    1 Effects: Constructs an empty unordered_set using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-definedimpldefdefault number of buckets in unordered_set. max_load_factor() returns 1.0.

    2 Complexity: Constant if n is not provided, otherwise linear in n to construct the buckets.

    template <class InputIterator>
    unordered_set(InputIterator f, InputIterator l,
                  size_type n = see below,
                  const hasher& hf = hasher(),
                  const key_equal& eql = key_equal(),
                  const allocator_type& a = allocator_type());
    

    3 Effects: Constructs an empty unordered_set using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-definedimpldefdefault number of buckets in unordered_set. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.

    4 Complexity: Average case linear, worst case quadraticConstant if n is not provided, else linear in n to construct the buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to distance(f, l).

  5. Modify 23.5.7.2 [unord.multiset.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):

    explicit unordered_multiset(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    

    1 Effects: Constructs an empty unordered_multiset using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-definedimpldefdefault number of buckets in unordered_multiset. max_load_factor() returns 1.0.

    2 Complexity: Constant if n is not provided, otherwise linear in n to construct the buckets.

    template <class InputIterator>
    unordered_multiset(InputIterator f, InputIterator l,
                       size_type n = see below,
                       const hasher& hf = hasher(),
                       const key_equal& eql = key_equal(),
                       const allocator_type& a = allocator_type());
    

    3 Effects: Constructs an empty unordered_multiset using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is implementation-definedimpldefdefault number of buckets in unordered_multiset. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.

    4 Complexity: Average case linear, worst case quadraticConstant if n is not provided, else linear in n to construct the buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to distance(f, l).


1213. Meaning of valid and singular iterator underspecified

Section: 24.2 [iterator.requirements] Status: Deferred Submitter: Daniel Krügler Opened: 2009-09-19 Last modified: 2012-01-14

View other active issues in [iterator.requirements].

View all other issues in [iterator.requirements].

View all issues with Deferred status.

Discussion:

The terms valid iterator and singular aren't properly defined. The fuzziness of those terms became even worse after the resolution of 208 (including further updates by 278). In 24.2 [iterator.requirements] as of N2723 the standard says now:

5 - These values are called past-the-end values. Values of an iterator i for which the expression *i is defined are called dereferenceable. The library never assumes that past-the-end values are dereferenceable. Iterators can also have singular values that are not associated with any container. [...] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value and the assignment of a non-singular value to an iterator that holds a singular value. [...] Dereferenceable values are always non-singular.

10 - An invalid iterator is an iterator that may be singular.

First, issue 208 intentionally removed the earlier constraint that past-the-end values are always non-singular. The reason for this was to support null pointers as past-the-end iterators of e.g. empty sequences. But there seem to exist different views on what a singular (iterator) value is. E.g. according to the SGI definition a null pointer is not a singular value:

Dereferenceable iterators are always nonsingular, but the converse is not true. For example, a null pointer is nonsingular (there are well defined operations involving null pointers) even thought it is not dereferenceable.

and proceeds:

An iterator is valid if it is dereferenceable or past-the-end.

Even if the standard prefers a different meaning of singular here, the change was incomplete, because by restricting feasible expressions of singular iterators to destruction and assignment isn't sufficient for a past-the-end iterator: Of-course it must still be equality-comparable and in general be a readable value.

Second, the standard doesn't clearly say whether a past-the-end value is a valid iterator or not. E.g. 20.6.12 [specialized.algorithms]/1 says:

In all of the following algorithms, the formal template parameter ForwardIterator is required to satisfy the requirements of a forward iterator (24.1.3) [..], and is required to have the property that no exceptions are thrown from [..], or dereference of valid iterators.

The standard should make better clear what "singular pointer" and "valid iterator" means. The fact that the meaning of a valid value has a core language meaning doesn't imply that for an iterator concept the term "valid iterator" has the same meaning.

Let me add a final example: In X [allocator.concepts.members] of N2914 we find:

pointer X::allocate(size_type n);

11 Returns: a pointer to the allocated memory. [Note: if n == 0, the return value is unspecified. —end note]

[..]

void X::deallocate(pointer p, size_type n);

Preconditions: p shall be a non-singular pointer value obtained from a call to allocate() on this allocator or one that compares equal to it.

If singular pointer value would include null pointers this make the preconditions unclear if the pointer value is a result of allocate(0): Since the return value is unspecified, it could be a null pointer. Does that mean that programmers need to check the pointer value for a null value before calling deallocate?

[ 2010-11-09 Daniel comments: ]

A later paper is in preparation.

[ 2010 Batavia: ]

Doesn't need to be resolved for Ox

Proposed resolution:

Consider to await the paper.


1214. Insufficient/inconsistent key immutability requirements for associative containers

Section: 23.2.4 [associative.reqmts] Status: Ready Submitter: Daniel Krügler Opened: 2009-09-20 Last modified: 2012-01-14

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with Ready status.

Discussion:

Scott Meyers' mentions on a recent posting on c.s.c++ some arguments that point to an incomplete resolution of 103 and to an inconsistency of requirements on keys in ordered and unordered associative containers:

1) 103 introduced the term immutable without defining it in a unique manner in 23.2.4 [associative.reqmts]/5:

[..] Keys in an associative container are immutable.

According to conventional dictionaries immutable is an unconditional way of saying that something cannot be changed. So without any further explicit allowance a user always runs into undefined behavior if (s)he attempts to modify such a key. IMO this was not the intend of the committee to resolve 103 in that way because the comments suggest an interpretation that should give any user the freedom to modify the key in an explicit way provided it would not affect the sort order in that container.

2) Another observation was that surprisingly no similar 'safety guards' exists against unintentional key changes for the unordered associative containers, specifically there is no such requirement as in 23.2.4 [associative.reqmts]/6 that "both iterator and const_iterator are constant iterators". But the need for such protection against unintentional changes as well as the constraints in which manner any explicit changes may be performed are both missing and necessary, because such changes could potentially change the equivalence of keys that is measured by the hasher and key_equal.

I suggest to fix the unconditional wording involved with "immutable keys" by at least adding a hint for the reader that users may perform such changes in an explicit manner and to perform similar wording changes as 103 did for the ordered associative containers also for the unordered containers.

[ 2010-03-27 Daniel provides wording. ]

This update attempts to provide normative wording that harmonizes the key and function object constraints of associative and unordered containers.

[ 2010 Batavia: ]

We're uncomfortable with the first agenda item, and we can live with the second agenda item being applied before or after Madrid.

[ 2011 Bloomington ]

Further discussion persuades us this issue is Ready (and so moved). We may need a further issue clarifying the notion of key value vs. key object, as object identity appears to be important to this wording.

Proposed resolution:

  1. Change 23.2.4 [associative.reqmts]/2 as indicated: [This ensures that associative containers make better clear what this "arbitrary" type is, as the unordered containers do in 23.2.5 [unord.req]/3]

    2 Each associative container is parameterized on Key and an ordering relation Compare that induces a strict weak ordering (25.4) on elements of Key. In addition, map and multimap associate an arbitrary mapped typetype T with the Key. The object of type Compare is called the comparison object of a container.

  2. Change 23.2.4 [associative.reqmts]/5 as indicated: [This removes the too strong requirement that keys must not be changed at all and brings this line in sync with 23.2.5 [unord.req]/7. We take care about the real constraints by the remaining suggested changes. The rationale provided by LWG 103 didn't really argue why that addition is necessary, and I believe the remaining additions make it clear that any user changes have strong restrictions]:

    5 For set and multiset the value type is the same as the key type. For map and multimap it is equal to pair<const Key, T>. Keys in an associative container are immutable.

  3. Change 23.2.5 [unord.req]/3+4 as indicated: [The current sentence of p.4 has doesn't say something really new and this whole subclause misses to define the concepts of the container-specific hasher object and predicate object. We introduce the term key equality predicate which is already used in the requirements table. This change does not really correct part of this issue, but is recommended to better clarify the nomenclature and the difference between the function objects and the function object types, which is important, because both can potentially be stateful.]

    3 Each unordered associative container is parameterized by Key, by a function object type Hash that meets the Hash requirements (20.2.4) and acts as a hash function for argument values of type Key, and by a binary predicate Pred that induces an equivalence relation on values of type Key. Additionally, unordered_map and unordered_multimap associate an arbitrary mapped type T with the Key.

    4 The container's object of type Hash - denoted by hash - is called the hash function of the container. The container's object of type Pred - denoted by pred - is called the key equality predicate of the container.A hash function is a function object that takes a single argument of type Key and returns a value of type std::size_t.

  4. Change 23.2.5 [unord.req]/5 as indicated: [This adds a similar safe-guard as the last sentence of 23.2.4 [associative.reqmts]/3]

    5 Two values k1 and k2 of type Key are considered equivalent if the container's key equality predicatekey_equal function object returns true when passed those values. If k1 and k2 are equivalent, the container's hash function shall return the same value for both. [Note: thus, when an unordered associative container is instantiated with a non-default Pred parameter it usually needs a non-default Hash parameter as well. — end note] For any two keys k1 and k2 in the same container, calling pred(k1, k2) shall always return the same value. For any key k in a container, calling hash(k) shall always return the same value.

  5. After 23.2.5 [unord.req]/7 add the following new paragraph: [This ensures the same level of compile-time protection that we already require for associative containers. It is necessary for similar reasons, because any change in the stored key which would change it's equality relation to others or would change it's hash value such that it would no longer fall in the same bucket, would break the container invariants]

    7 For unordered_set and unordered_multiset the value type is the same as the key type. For unordered_map and unordered_multimap it is std::pair<const Key, T>.

    For unordered containers where the value type is the same as the key type, both iterator and const_iterator are constant iterators. It is unspecified whether or not iterator and const_iterator are the same type. [Note: iterator and const_iterator have identical semantics in this case, and iterator is convertible to const_iterator. Users can avoid violating the One Definition Rule by always using const_iterator in their function parameter lists. — end note]


1450. [FCD] Contradiction in regex_constants

Section: 28.5.2 [re.matchflag] Status: Open Submitter: BSI Opened: 2010-08-25 Last modified: 2012-01-14

View all issues with Open status.

Discussion:

Addresses GB-127

The Bitmask Type requirements in 17.5.2.1.3 [bitmask.types] p.3 say that all elements on a bitmask type have distinct values, but 28.5.2 [re.matchflag] defines regex_constants::match_default and regex_constants::format_default as elements of the bitmask type regex_constants::match_flag_type, both with value 0. This is a contradiction.

[ Resolution proposed by ballot comment: ]

One of the bitmask elements should be removed from the declaration and should be defined separately, in the same manner as ios_base::adjustfield, ios_base::basefield and ios_base::floatfield are defined by 27.5.3.1.2 [ios::fmtflags] p.2 and Table 120. These are constants of a bitmask type, but are not distinct elements, they have more than one value set in the bitmask. regex_constants::format_default should be specified as a constant with the same value as regex_constants::match_default.

[ 2010-10-31 Daniel comments: ]

Strictly speaking, a bitmask type cannot have any element of value 0 at all, because any such value would contradict the requirement expressed in 17.5.2.1.3 [bitmask.types] p. 3:

for any pair Ci and Cj, Ci & Ci is nonzero

So, actually both regex_constants::match_default and regex_constants::format_default are only constants of the type regex_constants::match_flag_type, and no bitmask elements.

[ 2010-11-03 Daniel comments and provides a proposed resolution: ]

The proposed resolution is written against N3126 and considered as a further improvement of the fixes suggested by n3110.

Add the following sentence to 28.5.2 [re.matchflag] paragraph 1:

1 The type regex_constants::match_flag_type is an implementation-defined bitmask type (17.5.2.1.3). Matching a regular expression against a sequence of characters [first,last) proceeds according to the rules of the grammar specified for the regular expression object, modified according to the effects listed in Table 136 for any bitmask elements set. Type regex_constants::match_flag_type also defines the constants regex_constants::match_default and regex_constants::format_default.

[ 2011 Bloomington ]

It appears the key problem is the phrasing of the bitmask requirements. Jeremiah supplies updated wording.

Pete Becker has also provided an alternative resolution.

Ammend 17.5.2.1.3 [bitmask.types]:

Change the list of values for "enum bit mask" in p2 from

V0 = 1 << 0, V1 = 1 << 1, V2 = 1 << 2, V3 = 1 << 3, ....

to

V0 = 0, V1 = 1 << 0, V2 = 1 << 1, V3 = 1 << 2, ....

Here, the names C0, C1, etc. represent bitmask elements for this particular bitmask type. All such non-zero elements have distinct values such that, for any pair Ci and Cj where i != j, Ci & Ci is nonzero and Ci & Cj is zero.

Change bullet 3 of paragraph 4:

TheA non-zero value Y is set in the object X if the expression X & Y is nonzero.

Proposed resolution:

Ammend 17.5.2.1.3 [bitmask.types] p3:

Here, the names C0, C1, etc. represent bitmask elements for this particular bitmask type. All such elements have distinct, non-zero values such that, for any pair Ci and Cj where i != j, Ci & Ci is nonzero and Ci & Cj is zero. Additionally, the value 0 is used to represent an empty bitmask, in which no bitmask elements are set.

Add the following sentence to 28.5.2 [re.matchflag] paragraph 1:

1 The type regex_constants::match_flag_type is an implementation-defined bitmask type (17.5.2.1.3). The constants of that type, except for match_default and format_default, are bitmask elements. The match_default and format_default constants are empty bitmasks. Matching a regular expression against a sequence of characters [first,last) proceeds according to the rules of the grammar specified for the regular expression object, modified according to the effects listed in Table 136 for any bitmask elements set.


1526. [FCD] C++ should not impose thread safety requirements on C99 library implementations

Section: 17.6.5.9 [res.on.data.races] Status: Deferred Submitter: BSI Opened: 2011-03-24 Last modified: 2012-01-14

View all issues with Deferred status.

Discussion:

Addresses GB-111

Section 17.6.5.9 [res.on.data.races], Data Race Avoidance, requires the C++ Standard Library to avoid data races that might otherwise result from two threads making calls to C++ Standard Library functions on distinct objects. The C standard library is part of the C++ Standard Library and some C++ Standary library functions (parts of the Localization library, as well as Numeric Conversions in 21.5), are specified to make use of the C standard library. Therefore, the C++ standard indirectly imposes a requirement on the thread safety of the C standard library. However, since the C standard does not address the concept of thread safety conforming C implementations exist that do no provide such guarantees. This conflict needs to be reconciled.

Suggested resolution by national body comment:

remove the requirement to make use of strtol() and sprintf() since these functions depend on the global C locale and thus cannot be made thread safe.

[2011-03-24 Madrid meeting]

Deferred

[ 2011 Bloomington ]

Alisdair: PJ, does this cause a problem in C?

PJ: Every implementation know of is thread safe.

Pete: There a couple of effects that are specified on strtol() and sprintf() which is a problem.

PJ: When C++ talks about C calls it should be "as if" calling the function.

Pete: Culprit is to string stuff. My fault.

PJ: Not your fault. You did what you were told. Distinct resolution to change wording.

Dietmar: What would we break if we change it back?

Pete: Nothing. If implemented on top of thread safe C library you are just fine.

Alisdair: Anyone want to clean up wording and put it back to what Pete gave us?

Alisdair: No volunteers. Do we want to mark as NAD? We could leave it as deferred.

Stefanus: Did original submitter care about this?

Lawrence: There is some work to make local calls thread safe. The resolution would be to call those thread safe version.

Pete: "As if called under single threaded C program"

Action Item (Alisdair): Write wording for this issue.

Rationale:

No consensus to make a change at this time

Proposed resolution:


2003. String exception inconsistency in erase.

Section: 21.4.1 [string.require] Status: Open Submitter: José Daniel García Sánchez Opened: 2010-10-21 Last modified: 2012-01-14

View all other issues in [string.require].

View all issues with Open status.

Discussion:

Clause 21.4.1 [string.require]p3 states:

No erase() or pop_back() member function shall throw any exceptions.

However in 21.4.6.5 [string::erase] p2 the first version of erase has

Throws: out_of_range if pos > size().

[2011-03-24 Madrid meeting]

Beman: Don't want to just change this, can we just say "unless otherwise specified"?

Alisdair: Leave open, but update proposed resolution to say something like "unless otherwise specified".

General agreement that it should be corrected but not a stop-ship.

Action: Update proposed wording for issue 2003 as above, but leave Open.

Proposed resolution:

Update [string.require]p/3:

3 No erase() or pop_back() member function shall throw any exceptions.


2005. unordered_map::insert(T&&) protection should apply to map too

Section: 23.4.4.4 [map.modifiers], 23.4.5.3 [multimap.modifiers], X [unord.map.modifiers], X [unord.multimap.modifiers] Status: Review Submitter: P.J. Plauger Opened: 2010-10-14 Last modified: 2012-01-14

View all issues with Review status.

Discussion:

In [unord.map.modifiers], the signature:

template <class P>
    pair<iterator, bool> insert(P&& obj);

now has an added Remarks paragraph:

Remarks: This signature shall not participate in overload resolution unless P is implicitly convertible to value_type.

The same is true for unordered_multimap.

But neither map nor multimap have this constraint, even though it is a Good Thing(TM) in those cases as well.

[ The submitter suggests: Add the same Remarks clause to [map.modifiers] and [multimap.modifiers]. ]

[ 2010-10-29 Daniel comments: ]

I believe both paragraphs need more cleanup: First, the current Requires element conflict with the Remark; second, it seems to me that the whole single Requires element is intended to be split into a Requires and an Effects element; third, the reference to tuple is incorrect (noticed by Paolo Carlini); fourth, it refers to some non-existing InputIterator parameter relevant for a completely different overload; sixth, the return type of the overload with hint is wrong. The following proposed resolution tries to solve these issues as well and uses similar wording as for the corresponding unordered containers. Unfortunately it has some redundancy over Table 99, but I did not remove the specification because of the more general template parameter P - the Table 99 requirements apply only for an argument identical to value_type.

Daniel's Proposed resolution (not current):

  1. Change 23.4.4.4 [map.modifiers] around p. 1 as indicated:
    template <class P> pair<iterator, bool> insert(P&& x);
    template <class P> pair<iterator, bool> insert(const_iterator position, P&& x);
    

    1 Requires: P shall be convertible to value_type is constructible from std::forward<P>(x)..

    If P is instantiated as a reference type, then the argument x is copied from. Otherwise x is considered to be an rvalue as it is converted to value_type and inserted into the map. Specifically, in such cases CopyConstructible is not required of key_type or mapped_type unless the conversion from P specifically requires it (e.g., if P is a tuple<const key_type, mapped_type>, then key_type must be CopyConstructible). The signature taking InputIterator parameters does not require CopyConstructible of either key_type or mapped_type if the dereferenced InputIterator returns a non-const rvalue pair<key_type,mapped_type>. Otherwise CopyConstructible is required for both key_type and mapped_type.
    ? Effects: Inserts x converted to value_type if and only if there is no element in the container with key equivalent to the key of value_type(x). For the second form, the iterator position is a hint pointing to where the search should start.

    ? Returns: For the first form, the bool component of the returned pair object indicates whether the insertion took place and the iterator component - or for the second form the returned iterator - points to the element with key equivalent to the key of value_type(x).

    ? Complexity: Logarithmic in general, but amortized constant if x is inserted right before position.

    ? Remarks: These signatures shall not participate in overload resolution unless P is implicitly convertible to value_type.

  2. Change 23.4.5.3 [multimap.modifiers] around p. 1 as indicated:
    template <class P> iterator insert(P&& x);
    template <class P> iterator insert(const_iterator position, P&& x);
    

    1 Requires: P shall be convertible to value_type is constructible from std::forward<P>(x).

    If P is instantiated as a reference type, then the argument x is copied from. Otherwise x is considered to be an rvalue as it is converted to value_type and inserted into the map. Specifically, in such cases CopyConstructible is not required of key_type or mapped_type unless the conversion from P specifically requires it (e.g., if P is a tuple<const key_type, mapped_type>, then key_type must be CopyConstructible). The signature taking InputIterator parameters does not require CopyConstructible of either key_type or mapped_type if the dereferenced InputIterator returns a non-const rvalue pair<key_type, mapped_type>. Otherwise CopyConstructible is required for both key_type and mapped_type.
    ? Effects: Inserts x converted to value_type. For the second form, the iterator position is a hint pointing to where the search should start.

    ? Returns: An iterator that points to the element with key equivalent to the key of value_type(x).

    ? Complexity: Logarithmic in general, but amortized constant if x is inserted right before position.

    ? Remarks: These signatures shall not participate in overload resolution unless P is implicitly convertible to value_type.

[ 2010 Batavia: ]

We need is_convertible, not is_constructible, both in ordered and unordered containers.

[ 2011 Bloomington ]

The effects of these inserts can be concisely stated in terms of emplace(). Also, the correct term is "EmplaceConstructible", not "constructible".

New wording by Pablo, eliminating duplicate requirements already implied by the effects clause. Move to Review.

[ 2011-10-02 Daniel comments and refines the proposed wording ]

Unfortunately the template constraints expressed as "P is implicitly convertible to value_type" reject the intended effect to support move-only key types, which was the original intention when the library became move-enabled through the rvalue-reference proposals by Howard (This can clearly be deduced from existing carefully selected wording that emphasizes that CopyConstructible is only required for special situations involving lvalues or const rvalues as arguments). The root of the problem is based on current core rules, where an "implicitly converted" value has copy-initialization semantics. Consider a move-only key type KM, some mapped type T, and a source value p of type P equal to std::pair<KM, T>, this is equivalent to:

std::pair<const KM, T> dest = std::move(p);

Now 8.5 [dcl.init] p16 b6 sb2 says that the effects of this heterogeneous copy-initialization (p has a different type than dest) are as-if a temporary of the target type std::pair<const KM, T> is produced from the rvalue p of type P (which is fine), and this temporary is used to initialize dest. This second step cannot succeed, because we cannot move from const KM to const KM. This means that std::is_convertible<P, std::pair<const KM, T>>::value is false.

But the actual code that is required (with the default allocator) is simply a direct-initialization from P to value_type, so imposing an implicit conversion is more than necessary. Therefore I strongly recommend to reduce the "overload participation" constraint to std::is_constructible<std::pair<const KM, T>, P>::value instead. This change is the only change that has been performed to the previous proposed wording from Pablo shown below.

Proposed resolution:

  1. Change 23.4.4.4 [map.modifiers] around p. 1 as indicated:
    template <class P> pair<iterator, bool> insert(P&& x);
    template <class P> pair<iterator, bool> insert(const_iterator position, P&& x);
    
    1 Requires: P shall be convertible to value_type.

    If P is instantiated as a reference type, then the argument x is copied from. Otherwise x is considered to be an rvalue as it is converted to value_type and inserted into the map. Specifically, in such cases CopyConstructible is not required of key_type or mapped_type unless the conversion from P specifically requires it (e.g., if P is a tuple<const key_type, mapped_type>, then key_type must be CopyConstructible). The signature taking InputIterator parameters does not require CopyConstructible of either key_type or mapped_type if the dereferenced InputIterator returns a non-const rvalue pair<key_type,mapped_type>. Otherwise CopyConstructible is required for both key_type and mapped_type.
    ? Effects: The first form is equivalent to return emplace(std::forward<P>(x)). The second form is equivalent to return emplace_hint(position, std::forward<P>(x)).

    ? Remarks: These signatures shall not participate in overload resolution unless std::is_constructible<value_type, P&&>::value is true.

  2. Change 23.4.5.3 [multimap.modifiers] around p. 1 as indicated:
    template <class P> iterator insert(P&& x);
    template <class P> iterator insert(const_iterator position, P&& x);
    
    1 Requires: P shall be convertible to value_type.

    If P is instantiated as a reference type, then the argument x is copied from. Otherwise x is considered to be an rvalue as it is converted to value_type and inserted into the map. Specifically, in such cases CopyConstructible is not required of key_type or mapped_type unless the conversion from P specifically requires it (e.g., if P is a tuple<const key_type, mapped_type>, then key_type must be CopyConstructible). The signature taking InputIterator parameters does not require CopyConstructible of either key_type or mapped_type if the dereferenced InputIterator returns a non-const rvalue pair<key_type, mapped_type>. Otherwise CopyConstructible is required for both key_type and mapped_type.
    ? Effects: The first form is equivalent to return emplace(std::forward<P>(x)). The second form is equivalent to return emplace_hint(position, std::forward<P>(x)).

    ? Remarks: These signatures shall not participate in overload resolution unless std::is_constructible<value_type, P&&>::value is true.

  3. Change 23.5.4.4 [unord.map.modifers] around p. 1 as indicated:
    template <class P>
    pair<iterator, bool> insert(P&& obj);
    
    1 Requires: value_type is constructible from std::forward<P>(obj).

    2 Effects: equivalent to return emplace(std::forward<P>(obj)). Inserts obj converted to value_type if and only if there is no element in the container with key equivalent to the key of value_type(obj).

    3 Returns: The bool component of the returned pair object indicates whether the insertion took place and the iterator component points to the element with key equivalent to the key of value_type(obj).

    4 Complexity: Average case O(1), worst case O(size()).

    53 Remarks: This signature shall not participate in overload resolution unless P is implicitly convertible to value_typestd::is_constructible<value_type, P&&>::value is true.

    template <class P>
    iterator insert(const_iterator hint, P&& obj);
    
    6 Requires: value_type is constructible from std::forward<P>(obj).

    7? Effects: equivalent to return emplace_hint(hint, std::forward<P>(obj)). Inserts obj converted to value_type if and only if there is no element in the container with key equivalent to the key of value_type(obj). The iterator hint is a hint pointing to where the search should start.

    8 Returns: An iterator that points to the element with key equivalent to the key of value_type(obj).

    9 Complexity: Average case O(1), worst case O(size()).

    10? Remarks: This signature shall not participate in overload resolution unless P is implicitly convertible to value_typestd::is_constructible<value_type, P&&>::value is true.

  4. Change 23.5.5.3 [unord.multimap.modifers] around p. 1 as indicated:
    template <class P>
    iterator insert(P&& obj);
    
    1 Requires: value_type is constructible from std::forward<P>(obj).

    2 Effects: equivalent to return emplace(std::forward<P>(obj)). Inserts obj converted to value_type.

    3 Returns: An iterator that points to the element with key equivalent to the key of value_type(obj).

    4 Complexity: Average case O(1), worst case O(size()).

    53 Remarks: This signature shall not participate in overload resolution unless P is implicitly convertible to value_typestd::is_constructible<value_type, P&&>::value is true.

    template <class P>
    iterator insert(const_iterator hint, P&& obj);
    
    6 Requires: value_type is constructible from std::forward<P>(obj).

    7? Effects: equivalent to return emplace_hint(hint, std::forward<P>(obj)). Inserts obj converted to value_type. The iterator hint is a hint pointing to where the search should start.

    8 Returns: An iterator that points to the element with key equivalent to the key of value_type(obj).

    9 Complexity: Average case O(1), worst case O(size()).

    10? Remarks: This signature shall not participate in overload resolution unless P is implicitly convertible to value_typestd::is_constructible<value_type, P&&>::value is true.


2009. Reporting out-of-bound values on numeric string conversions

Section: 21.5 [string.conversions] Status: Ready Submitter: Alisdair Meredith Opened: 2010-07-19 Last modified: 2012-01-14

View all other issues in [string.conversions].

View all issues with Ready status.

Discussion:

The functions (w)stoi and (w)stof are specified in terms of calling C library APIs for potentially wider types. The integer and floating-point versions have subtly different behaviour when reading values that are too large to convert. The floating point case will throw out_of_bound if the read value is too large to convert to the wider type used in the implementation, but behaviour is undefined if the converted value cannot narrow to a float. The integer case will throw out_of_bounds if the converted value cannot be represented in the narrower type, but throws invalid_argument, rather than out_of_range, if the conversion to the wider type fails due to overflow.

Suggest that the Throws clause for both specifications should be consistent, supporting the same set of fail-modes with the matching set of exceptions.

Proposed resolution:

21.5p3 [string.conversions]

int stoi(const string& str, size_t *idx = 0, int base = 10);
long stol(const string& str, size_t *idx = 0, int base = 10);
unsigned long stoul(const string& str, size_t *idx = 0, int base = 10);
long long stoll(const string& str, size_t *idx = 0, int base = 10);
unsigned long long stoull(const string& str, size_t *idx = 0, int base = 10);

...

3 Throws: invalid_argument if strtol, strtoul, strtoll, or strtoull reports that no conversion could be performed. Throws out_of_range if strtol, strtoul, strtoll or strtoull sets errno to ERANGE, or if the converted value is outside the range of representable values for the return type.

21.5p6 [string.conversions]

float stof(const string& str, size_t *idx = 0);
double stod(const string& str, size_t *idx = 0);
long double stold(const string& str, size_t *idx = 0);

...

6 Throws: invalid_argument if strtod or strtold reports that no conversion could be performed. Throws out_of_range if strtod or strtold sets errno to ERANGE or if the converted value is outside the range of representable values for the return type.


2010. is_* traits for binding operations can't be meaningfully specialized

Section: 20.8.9.1.1 [func.bind.isbind] Status: Ready Submitter: Sean Hunt Opened: 2010-07-19 Last modified: 2012-01-14

View all other issues in [func.bind.isbind].

View all issues with Ready status.

Discussion:

20.8.9.1.1 [func.bind.isbind] says for is_bind_expression:

Users may specialize this template to indicate that a type should be treated as a subexpression in a bind call.

But it also says:

If T is a type returned from bind, is_bind_expression<T> shall be publicly derived from integral_constant<bool, true>, otherwise from integral_constant<bool, false>.

This means that while the user is free to specialize, any specialization would have to be false to avoid violating the second requirement. A similar problem exists for is_placeholder.

[ 2010 Batavia (post meeting session) ]

Alisdair recognises this is clearly a bug introduced by some wording he wrote, the sole purpose of this metafunction is as a customization point for users to write their own bind-expression types that participate in the standard library bind protocol. The consensus was that this should be fixed in Madrid, moved to Open.

[2011-05-13 Jonathan Wakely comments and provides proposed wording]

The requirements are that is_bind_expression<T>::value is true when T is a type returned from bind, false for any other type, except when there's a specialization involving a user-defined type (N.B. 17.6.4.2.1 [namespace.std] means we don't need to say e.g. is_bind_expression<string> is false.)

The obvious way to meet the requirements is for the primary template to derive from integral_constant<bool, false> and for implementations to provide specializations for the unspecified types returned from bind. User-defined specializations can do whatever they like, as long as is_bind_expression::value is sane. There's no reason to forbid users from defining is_bind_expression<user_defined_type>::value=false if that's what they want to do.

Similar reasoning applies to is_placeholder, but a further issue is that 20.8.9.1.1 [func.bind.isbind] contains wording for is_placeholder but contains no definition of it and the sub-clause name only refers to is_bind_expression. The wording below proposes splitting paragraphs 3 and 4 of 20.8.9.1.1 [func.bind.isbind] into a new sub-clause covering is_placeholder.

If the template specializations added by the proposed wording are too vague then they could be preceded by "for exposition only" comments

[2011-05-18 Daniel comments and provides some refinements to the P/R]

Both bind-related type traits should take advantage of the UnaryTypeTrait requirements. Additionally, the updated wording does not imply that the implementation provides several specializations. Wording was used similar to the specification of the uses_allocator type trait (which unfortunately is not expressed in terms of BinaryTypeTrait requirements).

[Bloomington, 2011]

Move to Ready

Proposed resolution:

This wording is relative to the FDIS.

  1. Change 20.8.9.1.1 [func.bind.isbind] to:

    namespace std {
      template<class T> struct is_bind_expression; // see below
        : integral_constant<bool, see below> { };
    }
    

    -1- is_bind_expression can be used to detect function objects generated by bind. bind uses is_bind_expression to detect subexpressions. Users may specialize this template to indicate that a type should be treated as a subexpression in a bind call.

    -2- If T is a type returned from bind, is_bind_expression<T> shall be publicly derived from integral_constant<bool, true>, otherwise from integral_constant<bool, false>Instantiations of the is_bind_expression template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). The implementation shall provide a definition that has a BaseCharacteristic of true_type if T is a type returned from bind, otherwise it shall have a BaseCharacteristic of false_type. A program may specialize this template for a user-defined type T to have a BaseCharacteristic of true_type to indicate that T should be treated as a subexpression in a bind call..

    -3- is_placeholder can be used to detect the standard placeholders _1, _2, and so on. bind uses is_placeholder to detect placeholders. Users may specialize this template to indicate a placeholder type.

    -4- If T is the type of std::placeholders::_J, is_placeholder<T> shall be publicly derived from integral_constant<int, J>, otherwise from integral_constant<int, 0>.

  2. Insert a new sub-clause immediately following sub-clause 20.8.9.1.1 [func.bind.isbind], the suggested sub-clause tag is [func.bind.isplace]:

    20.8.9.1.? Class template is_placeholder [func.bind.isplace]

    namespace std {
      template<class T> struct is_placeholder; // see below
    }
    

    -?- is_placeholder can be used to detect the standard placeholders _1, _2, and so on. bind uses is_placeholder to detect placeholders.

    -?- Instantiations of the is_placeholder template shall meet the UnaryTypeTrait requirements ([meta.rqmts]). The implementation shall provide a definition that has a BaseCharacteristic of integral_constant<int, J> if T is the type of std::placeholders::_J, otherwise it shall have a BaseCharacteristic of integral_constant<int, 0>. A program may specialize this template for a user-defined type T to have a BaseCharacteristic of integral_constant<int, N> with N > 0 to indicate that T should be treated as a placeholder type.


2011. Unexpected output required of strings

Section: 21.4.8.9 [string.io] Status: Review Submitter: James Kanze Opened: 2010-07-23 Last modified: 2012-01-14

View all other issues in [string.io].

View all issues with Review status.

Discussion:

What should the following code output?

#include <string>
#include <iostream>
#include <iomanip>

int main() 
{ 
   std::string test("0X1Y2Z"); 
   std::cout.fill('*'); 
   std::cout.setf(std::ios::internal, std::ios::adjustfield); 
   std::cout << std::setw(8) << test << std::endl; 
} 

I would expect "**0X1Y2Z", and this is what the compilers I have access to (VC++, g++ and Sun CC) do. But according to the standard, it should be "0X**1Y2Z":

21.4.8.9 [string.io]/5:

template<class charT, class traits, class Allocator>
  basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>& os, const basic_string<charT,traits,Allocator>& str);

Effects: Behaves as a formatted output function (27.7.3.6.1 [ostream.formatted.reqmts]). After constructing a sentry object, if this object returns true when converted to a value of type bool, determines padding as described in 22.4.2.2.2 [facet.num.put.virtuals], then inserts the resulting sequence of characters seq as if by calling os.rdbuf()->sputn(seq, n), where n is the larger of os.width() and str.size(); then calls os.width(0).

22.4.2.2.2 [facet.num.put.virtuals]/5:

[…]

Stage 3: A local variable is initialized as

fmtflags adjustfield= (flags & (ios_base::adjustfield));

The location of any padding is determined according to Table 88.

If str.width() is nonzero and the number of charT's in the sequence after stage 2 is less than str.width(), then enough fill characters are added to the sequence at the position indicated for padding to bring the length of the sequence to str.width(). str.width(0) is called.

Table 88 — Fill padding
State Location
adjustfield == ios_base::left pad after
adjustfield == ios_base::right pad before
adjustfield == internal and a sign occurs in the representation pad after the sign
adjustfield == internal and representation after stage 1 began with 0x or 0X pad after x or X
otherwise pad before

Although it's not 100% clear what "the sequence after stage 2" should mean here, when there is no stage 2, the only reasonable assumption is that it is the contents of the string being output. In the above code, the string being output is "0X1Y2Z", which starts with "0X", so the padding should be inserted "after x or X", and not before the string. I believe that this is a defect in the standard, and not in the three compilers I tried.

[ 2010 Batavia (post meeting session) ]

Consensus that all known implementations are consistent, and disagree with the standard. Preference is to fix the standard before implementations start trying to conform to the current spec, as the current implementations have the preferred form. Howard volunteered to drught for Madrid, move to Open.

[2011-03-24 Madrid meeting]

Daniel Krügler volunteered to provide wording, interacting with Dietmar and Bill.

[2011-06-24 Daniel comments and provides wording]

The same problem applies to the output provided by const char* and similar character sequences as of 27.7.3.6.4 [ostream.inserters.character] p. 5. and even for single character output (!) as described in 27.7.3.6.4 [ostream.inserters.character] p. 1, just consider the character value '-' where '-' is the sign character. In this case Table 91 — "Fill padding" requires to pad after the sign, i.e. the output for the program

#include <iostream>
#include <iomanip>

int main() 
{ 
   char c = '-'; 
   std::cout.fill('*'); 
   std::cout.setf(std::ios::internal, std::ios::adjustfield); 
   std::cout << std::setw(2) << c << std::endl; 
} 

According to the current wording this program should output "-*", but all tested implementations output "*-" instead.

I suggest to replace the reference to 22.4.2.2.2 [facet.num.put.virtuals] in all three places. It is not very complicated to describe the padding rules for simple character sequences "inline". A similar approach is used as for the money_put functions.

[ 2011 Bloomington ]

Move to Review, the resolution seems correct but it would be nice if some factoring of the common words were proposed.

Proposed resolution:

The new wording refers to the FDIS numbering.

  1. Change 21.4.8.9 [string.io]/5 as indicated:

    template<class charT, class traits, class Allocator>
      basic_ostream<charT, traits>&
        operator<<(basic_ostream<charT, traits>& os,
                   const basic_string<charT,traits,Allocator>& str);
    

    -5- Effects: Behaves as a formatted output function ([ostream.formatted.reqmts]). After constructing a sentry object, if this object returns true when converted to a value of type bool, determines padding as described in [facet.num.put.virtuals],follows: A charT character sequence is produced, initially consisting of the elements defined by the range [str.begin(), str.end()). If str.size() is less than os.width(), then enough copies of os.fill() are added to this sequence as necessary to pad to a width of os.width() characters. If (os.flags() & ios_base::adjustfield) == ios_base::left is true, the fill characters are placed after the character sequence; otherwise, they are placed before the character sequence. Tthen inserts the resulting sequence of characters seq as if by calling os.rdbuf()->sputn(seq, n), where n is the larger of os.width() and str.size(); then calls os.width(0).

  2. Change 27.7.3.6.4 [ostream.inserters.character]/1 as indicated (An additional editorial fix is suggested for the first prototype declaration):

    template<class charT, class traits>
      basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out,
                                              charT c});
    template<class charT, class traits>
      basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out,
                                              char c);
    // specialization
    template<class traits>
      basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
                                             char c);
    // signed and unsigned
    template<class traits>
      basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
                                             signed char c);
    template<class traits>
      basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
                                             unsigned char c);
    

    -1- Effects: Behaves like a formatted inserter (as described in [ostream.formatted.reqmts]) of out. After a sentry object is constructed it inserts characters. In case c has type char and the character type of the stream is not char, then the character to be inserted is out.widen(c); otherwise the character is c. Padding is determined as described in [facet.num.put.virtuals]follows: A character sequence is produced, initially consisting of the insertion character. If out.width() is greater than one, then enough copies of out.fill() are added to this sequence as necessary to pad to a width of out.width() characters. If (out.flags() & ios_base::adjustfield) == ios_base::left is true, the fill characters are placed after the insertion character; otherwise, they are placed before the insertion character. width(0) is called. The insertion character and any required padding are inserted into out; then calls os.width(0).

  3. Change 27.7.3.6.4 [ostream.inserters.character]/5 as indicated:

    template<class charT, class traits>
      basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out,
                                              const charT* s);
    template<class charT, class traits>
      basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out,
                                              const char* s);
    template<class traits>
      basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
                                             const char* s);
    template<class traits>
      basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
                                             const signed char* s);
    template<class traits>
      basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out,
                                             const unsigned char* s);
    

    […]

    -5- Padding is determined as described in [facet.num.put.virtuals]. The n characters starting at s are widened using out.widen ([basic.ios.members])follows: A character sequence is produced, initially consisting of the elements defined by the n characters starting at s widened using out.widen ([basic.ios.members]). If n is less than out.width(), then enough copies of out.fill() are added to this sequence as necessary to pad to a width of out.width() characters. If (out.flags() & ios_base::adjustfield) == ios_base::left is true, the fill characters are placed after the character sequence; otherwise, they are placed before the character sequence. The widened characters and any required padding are inserted into out. Calls width(0).


2012. Associative maps should insert pair, not tuple

Section: 23.4 [associative] Status: Open Submitter: Paolo Carlini Opened: 2010-10-29 Last modified: 2012-01-14

View all other issues in [associative].

View all issues with Open status.

Discussion:

I'm seeing something strange in the paragraphs 23.4.4.4 [map.modifiers] and 23.4.5.3 [multimap.modifiers]: they both talk about tuple<const key_type, mapped_type> but I think they should be talking about pair<const key_type, mapped_type> because, among other reasons, a tuple is not convertible to a pair. If I replace tuple with pair everything makes sense to me.

The proposed resolution is obvious.

[ 2010-11-07 Daniel comments ]

This is by far not the only necessary fix within both sub-clauses. For details see the 2010-10-29 comment in 2005.

[2011-03-24 Madrid meeting]

Paolo: Don't think we can do it now.

Daniel K: Agrees.

[ 2011 Bloomington ]

Consensus that this issue will be resolved by 2005, but held open until that issue is resolved.

Proposed resolution:

Apply the resolution proposed by the 2010-10-29 comment in 2005.


2013. Do library implementers have the freedom to add constexpr?

Section: 17.6.5.6 [constexpr.functions] Status: Ready Submitter: Matt Austern Opened: 2010-11-12 Last modified: 2012-01-14

View all issues with Ready status.

Discussion:

Suppose that a particular function is not tagged as constexpr in the standard, but that, in some particular implementation, it is possible to write it within the constexpr constraints. If an implementer tags such a function as constexpr, is that a violation of the standard or is it a conforming extension?

There are two questions to consider. First, is this allowed under the as-if rule? Second, if it does not fall under as-if, is there (and should there be) any special license granted to implementers to do this anyway, sort of the way we allow elision of copy constructors even though it is detectable by users?

I believe that this does not fall under "as-if", so implementers probably don't have that freedom today. I suggest changing the WP to grant it. Even if we decide otherwise, however, I suggest that we make it explicit.

[ 2011 Bloomington ]

General surprise this was not already in 'Ready' status, and so moved.

Proposed resolution:

In 17.6.4.6 [constexpr.functions], change paragraph 1 to:

This standard explicitly requires that certain standard library functions are constexpr [dcl.constexpr]. Additionally, an implementation may declare any function to be constexpr if that function's definition satisfies the necessary constraints. Within any header that provides any non-defining declarations of constexpr functions or constructors an implementation shall provide corresponding definitions.


2015. Incorrect pre-conditions for some type traits

Section: 20.9.4 [meta.unary] Status: Ready Submitter: Nikolay Ivchenkov Opened: 2010-11-08 Last modified: 2012-01-14

View all other issues in [meta.unary].

View all issues with Ready status.

Discussion:

According to N3126 ‑ 3.9/9,

"Scalar types, trivial class types (Clause 9), arrays of such types and cv‑qualified versions of these types (3.9.3) are collectively called trivial types."

Thus, an array (possibly of unknown bound) can be trivial type, non‑trivial type, or an array type whose triviality cannot be determined because its element type is incomplete.

According to N3126 ‑ Table 45, preconditions for std::is_trivial are defined as follows:

"T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound"

It seems that "an array of unknown bound" should be changed to "an array of unknown bound of a complete element type". Preconditions for some other templates (e.g., std::is_trivially_copyable, std::is_standard_layout, std::is_pod, and std::is_literal_type) should be changed similarly.

On the other hand, some preconditions look too restrictive. For example, std::is_empty and std::is_polymorphic might accept any incomplete non‑class type.

[2011-02-18: Daniel provides wording proposal]

While reviewing the individual preconditions I could find three different groups of either too weakening or too strengthening constraints:

  1. is_empty/is_polymorphic/is_abstract/has_virtual_destructor:

    These traits can only apply for non‑union class types, otherwise the result must always be false

  2. is_base_of:

    Similar to the previous bullet, but the current wording comes already near to that ideal, it only misses to add the non‑union aspect.

  3. is_trivial/is_trivially_copyable/is_standard_layout/is_pod/is_literal_type:

    These traits always require that std::remove_all_extents<T>::type to be cv void or a complete type.

[Bloomington, 2011]

Move to Ready

Proposed resolution:

  1. Modify the pre-conditions of the following type traits in 20.9.4.3 [meta.unary.prop], Table 48 — Type property predicates:

    Table 48 — Type property predicates
    Template Condition Preconditions
    ...
    template <class T>
    struct is_trivial;
    T is a trivial type (3.9) remove_all_extents<T>::type
    shall be a complete type, or (possibly
    cv-qualified) void, or an array of
    unknown bound
    .
    template <class T>
    struct is_trivially_copyable;
    T is a trivially copyable
    type (3.9)
    remove_all_extents<T>::type
    shall be a complete type, or (possibly
    cv-qualified) void, or an array of
    unknown bound
    .
    template <class T>
    struct is_standard_layout;
    T is a standard-layout
    type (3.9)
    remove_all_extents<T>::type
    shall be a complete type, or (possibly
    cv-qualified) void, or an array of
    unknown bound
    .
    template <class T>
    struct is_pod;
    T is a POD type (3.9) remove_all_extents<T>::type
    shall be a complete type, or (possibly
    cv-qualified) void, or an array of
    unknown bound
    .
    template <class T>
    struct is_literal_type;
    T is a literal type (3.9) remove_all_extents<T>::type
    shall be a complete type, or (possibly
    cv-qualified) void, or an array of
    unknown bound
    .
    template <class T>
    struct is_empty;
    T is a class type, but not a
    union type, with no
    non-static data members
    other than bit-fields of
    length 0, no virtual
    member functions, no
    virtual base classes, and
    no base class B for which
    is_empty<B>::value is
    false.
    T shall be a complete type,
    (possibly cv-qualified) void, or
    an array of unknown bound
    If T
    is a non‑union class type, T
    shall be a complete type
    .
    template <class T>
    struct is_polymorphic;
    T is a polymorphic
    class (10.3)
    T shall be a complete type,
    type, (possibly cv-qualified) void, or
    an array of unknown bound
    If T
    is a non‑union class type, T
    shall be a complete type
    .
    template <class T>
    struct is_abstract;
    T is an abstract
    class (10.4)
    T shall be a complete type,
    type, (possibly cv-qualified) void, or
    an array of unknown bound
    If T
    is a non‑union class type, T
    shall be a complete type
    .
    ...
    template <class T>
    struct has_virtual_destructor;
    T has a virtual
    destructor (12.4)
    T shall be a complete type,
    (possibly cv-qualified) void, or
    an array of unknown bound
    If T
    is a non‑union class type, T
    shall be a complete type
    .
  2. Modify the pre-conditions of the following type traits in 20.9.6 [meta.rel], Table 50 — Type relationship predicates:

    Table 50 — Type relationship predicates
    Template Condition Comments
    ...
    template <class Base, class
    Derived>
    struct is_base_of;
    Base is a base class of
    Derived (10) without
    regard to cv-qualifiers
    or Base and Derived
    are not unions and
    name the same class
    type without regard to
    cv-qualifiers
    If Base and Derived are
    non‑union class types
    and are different types
    (ignoring possible cv-qualifiers)
    then Derived shall be a complete
    type. [ Note: Base classes that
    are private, protected, or
    ambigious are, nonetheless, base
    classes. — end note ]
    ...

2016. Allocators must be no-throw swappable

Section: 17.6.3.5 [allocator.requirements] Status: Open Submitter: Daniel Krügler Opened: 2010-11-17 Last modified: 2012-01-14

View other active issues in [allocator.requirements].

View all other issues in [allocator.requirements].

View all issues with Open status.

Discussion:

During the Batavia meeting it turned out that there is a definition hole for types satisfying the Allocators requirements: The problem became obvious when it was discussed whether all swap functions of Containers with internal data handles can be safely tagged with noexcept or not. While it is correct that the implicit swap function of an allocator is required to be a no-throw operation (because move/copy-constructors and assignment operators are required to be no-throw functions), there are no such requirements for specialized swap overloads for a particular allocator.

But this requirement is essential because the Containers are required to support swappable Allocators, when the value allocator_traits<>::propagate_on_container_swap evaluates to true.

[2011-02-10 Alberto, Daniel, and Pablo collaborated on the proposed wording]

The proposed resolution (based on N3225) attempts to solve the following problems:

  1. Table 44 — Allocator requirements, expression rows X::propagate_on_container_copy_assignment, X::propagate_on_container_move_assignment, and X::propagate_on_container_swap only describe operations, but no requirements. In fact, if and only if these compile-time predicates evaluate to true, the additional requirements CopyAssignable, no-throw MoveAssignable, and no-throw lvalue Swappable, respectively, are imposed on the allocator types.
  2. 23.2.1 [container.requirements.general] p. 9 misses to refer to the correct swap conditions: The current wording does not relate to 17.6.3.2 [swappable.requirements] as it should and omits to mention that lvalues shall be swapped. Additional there is one situation described twice in p. 8 and p. 9 (undefined behaviour unless a.get_allocator() == b.get_allocator() or allocator_traits<allocator_type>::propagate_on_container_swap::value == true), which should be cleaned up.

[2011-04-08 Pablo comments]

I'm implementing a version of list now and I actually do find it impossible to write an exception-safe assignment operator unless I can assume that allocator assignment does not throw. (The problem is that I use a sentinel node and I need to allocate a new sentinel using the new allocator without destroying the old one -- then swap the allocator and sentinel pointer in atomically, without risk of an exception leaving one inconsistent with the other.

Please update the proposed resolution to add the nothrow requirement to copy-assignment.

Proposed resolution:

  1. Adapt the following three rows from Table 44 — Allocator requirements:

    Table 44 — Allocator requirements
    Expression Return type Assertion/note
    pre-/post-condition
    Default
    X::propagate_on_container_copy_assignment Identical to or derived from true_type
    or false_type
    true_type only if an allocator of type X should be copied
    when the client container is copy-assigned. See Note B, below.
    false_type
    X::propagate_on_container_move_assignment Identical to or derived from true_type
    or false_type
    true_type only if an allocator of type X should be moved
    when the client container is move-assigned. See Note B, below.
    false_type
    X::propagate_on_container_swap Identical to or derived from true_type
    or false_type
    true_type only if an allocator of type X should be swapped
    when the client container is swapped. See Note B, below.
    false_type
  2. Following 17.6.3.5 [allocator.requirements] p. 3 insert a new normative paragraph:

    Note B: If X::propagate_on_container_copy_assignment::value is true, X shall satisfy the CopyAssignable requirements (Table 39 [copyassignable]) and the copy operation shall not throw exceptions. If X::propagate_on_container_move_assignment::value is true, X shall satisfy the MoveAssignable requirements (Table 38 [moveassignable]) and the move operation shall not throw exceptions. If X::propagate_on_container_swap::value is true, lvalues of X shall be swappable (17.6.3.2 [swappable.requirements]) and the swap operation shall not throw exceptions.

  3. Modify 23.2.1 [container.requirements.general] p. 8 and p. 9 as indicated:

    8 - [..] The allocator may be replaced only via assignment or swap(). Allocator replacement is performed by copy assignment, move assignment, or swapping of the allocator only if allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value, allocator_traits<allocator_type>::propagate_on_container_move_assignment::value, or allocator_traits<allocator_type>::propagate_on_container_swap::value is true within the implementation of the corresponding container operation. The behavior of a call to a container's swap function is undefined unless the objects being swapped have allocators that compare equal or allocator_traits<allocator_type>::propagate_on_container_swap::value is true. In all container types defined in this Clause, the member get_allocator() returns a copy of the allocator used to construct the container or, if that allocator has been replaced, a copy of the most recent replacement.

    9 - The expression a.swap(b), for containers a and b of a standard container type other than array, shall exchange the values of a and b without invoking any move, copy, or swap operations on the individual container elements. Lvalues of aAny Compare, Pred, or Hash objects belonging to a and b shall be swappable and shall be exchanged by unqualified calls to non-member calling swap as described in 17.6.3.2 [swappable.requirements]. If allocator_traits<allocator_type>::propagate_on_container_swap::value is true, then lvalues of allocator_type shall be swappable and the allocators of a and b shall also be exchanged using a an unqualified call to non-member swap call as described in 17.6.3.2 [swappable.requirements]. Otherwise, theythe allocators shall not be swapped, and the behavior is undefined unless a.get_allocator() == b.get_allocator(). Every iterator referring to an element in one container before the swap shall refer to the same element in the other container after the swap. It is unspecified whether an iterator with value a.end() before the swap will have value b.end() after the swap.


2018. regex_traits::isctype Returns clause is wrong

Section: 28.7 [re.traits] Status: Open Submitter: Jonathan Wakely Opened: 2010-11-16 Last modified: 2012-01-14

View all other issues in [re.traits].

View all issues with Open status.

Discussion:

28.7 [re.traits] p. 12 says:

returns true if f bitwise or'ed with the result of calling lookup_classname with an iterator pair that designates the character sequence "w" is not equal to 0 and c == '_'

If the bitmask value corresponding to "w" has a non-zero value (which it must do) then the bitwise or with any value is also non-zero, and so isctype('_', f) returns true for any f. Obviously this is wrong, since '_' is not in every ctype category.

There's a similar problem with the following phrases discussing the "blank" char class.

[2011-05-06: Jonathan Wakely comments and provides suggested wording]

DR 2019 added isblank support to <locale> which simplifies the definition of regex_traits::isctype by removing the special case for the "blank" class.

My suggestion for 2018 is to add a new table replacing the lists of recognized names in the Remarks clause of regex_traits::lookup_classname. I then refer to that table in the Returns clause of regex_traits::isctype to expand on the "in an unspecified manner" wording which is too vague. The conversion can now be described using the "is set" term defined by 17.5.2.1.3 [bitmask.types] and the new table to convey the intented relationship between e.g. [[:digit:]] and ctype_base::digit, which is not actually stated in the FDIS.

The effects of isctype can then most easily be described in code, given an "exposition only" function prototype to do the not-quite-so-unspecified conversion from char_class_type to ctype_base::mask.

The core of LWG 2018 is the "bitwise or'ed" wording which gives the wrong result, always evaluating to true for all values of f. That is replaced by the condition (f&x) == x where x is the result of calling lookup_classname with "w". I believe that's necessary, because the "w" class could be implemented by an internal "underscore" class i.e. x = _Alnum|_Underscore in which case (f&x) != 0 would give the wrong result when f==_Alnum.

The proposed resolution also makes use of ctype::widen which addresses the problem that the current wording only talks about "w" and '_' which assumes charT is char. There's still room for improvement here: the regex grammar in 28.13 [re.grammar] says that the class names in the table should always be recognized, implying that e.g. U"digit" should be recognized by regex_traits<char32_t>, but the specification of regex_traits::lookup_classname doesn't cover that, only mentioning char and wchar_t. Maybe the table should not distinguish narrow and wide strings, but should just have one column and add wording to say that regex_traits widens the name as if by using use_facet<ctype<charT>>::widen().

Another possible improvement would be to allow additional implementation-defined extensions in isctype. An implementation is allowed to support additional class names in lookup_classname, e.g. [[:octdigit:]] for [0-7] or [[:bindigit:]] for [01], but the current definition of isctype provides no way to use them unless ctype_base::mask also supports them.

[2011-05-10: Alberto and Daniel perform minor fixes in the P/R]

[ 2011 Bloomington ]

Consensus that this looks to be a correct solution, and the presentation as a table is a big improvement.

Concern that the middle section wording is a little muddled and confusing, Stefanus volunteered to reword.

Proposed resolution:

This wording is relative to the FDIS.

  1. Modify 28.7 [re.traits] p. 10 as indicated:

    template <class ForwardIterator>
      char_class_type lookup_classname(
        ForwardIterator first, ForwardIterator last, bool icase = false) const;
    

    -9- Returns: an unspecified value that represents the character classification named by the character sequence designated by the iterator range [first,last). If the parameter icase is true then the returned mask identifies the character classification without regard to the case of the characters being matched, otherwise it does honor the case of the characters being matched.(footnote 335) The value returned shall be independent of the case of the characters in the character sequence. If the name is not recognized then returns a value that compares equal to 0.

    -10- Remarks: For regex_traits<char>, at least the names "d", "w", "s", "alnum", "alpha", "blank", "cntrl", "digit", "graph", "lower", "print", "punct", "space", "upper" and "xdigit"narrow character names in Table X shall be recognized. For regex_traits<wchar_t>, at least the names L"d", L"w", L"s", L"alnum", L"alpha", L"blank", L"cntrl", L"digit", L"graph", L"lower", L"print", L"punct", L"space", L"upper" and L"xdigit"wide character names in Table X shall be recognized.

  2. Modify 28.7 [re.traits] p. 12 as indicated:

    bool isctype(charT c, char_class_type f) const;
    

    -11- Effects: Determines if the character c is a member of the character classification represented by f.

    -12- Returns: Converts f into a value m of type std::ctype_base::mask in an unspecified manner, and returns true if use_facet<ctype<charT> >(getloc()).is(m, c) is true. Otherwise returns true if f bitwise or'ed with the result of calling lookup_classname with an iterator pair that designates the character sequence "w" is not equal to 0 and c == '_', or if f bitwise or'ed with the result of calling lookup_classname with an iterator pair that designates the character sequence "blank" is not equal to 0 and c is one of an implementation-defined subset of the characters for which isspace(c, getloc()) returns true, otherwise returns false.except that when f represents membership of a character class named in Table X, the corresponding ctype_base::mask value shall be set in m. Given the function prototype

    template<class C>
       ctype_base::mask convert(typename regex_traits<C>::char_class_type);
    

    the result is determined as if by

    
    ctype_base::mask m = convert<charT>(f);
    const ctype<charT>& ct = use_facet<ctype<charT> >(getloc());
    if (ct.is(m, c))
      return true;
    charT w[1] = { ct.widen('w') };
    char_class_type x = lookup_classname(w, w+1);
    if ((f&x) == x && c == ct.widen('_'))
      return true;
    return false;
    

    [Example:

    
    regex_traits<char> t;
    string d("d");
    string u("upper");
    regex_traits<char>::char_class_type f;
    f = t.lookup_classname(d.begin(), d.end());
    f |= t.lookup_classname(u.begin(), u.end());
    ctype_base::mask m = convert<char>(f); // m == ctype_base::digit|ctype_base::upper
    

    end example]

    [Example:

    
    regex_traits<char> t;
    string w("w");
    regex_traits<char>::char_class_type f;
    f = t.lookup_classname(w.begin(), w.end());
    t.isctype('A', f); // returns true
    t.isctype('_', f); // returns true
    t.isctype(' ', f); // returns false
    

    end example]

  3. At the end of [re.traits] add a new Table X — Character class names and corresponding ctype masks:

    Table X — Character class names and corresponding ctype masks
    Narrow character name Wide character name Corresponding ctype_base::mask value
    "alnum" L"alnum" ctype_base::alnum
    "alpha" L"alpha" ctype_base::alpha
    "blank" L"blank" ctype_base::blank
    "cntrl" L"cntrl" ctype_base::cntrl
    "digit" L"digit" ctype_base::digit
    "d" L"d" ctype_base::digit
    "graph" L"graph" ctype_base::graph
    "lower" L"lower" ctype_base::lower
    "print" L"print" ctype_base::print
    "punct" L"punct" ctype_base::punct
    "space" L"space" ctype_base::space
    "s" L"s" ctype_base::space
    "upper" L"upper" ctype_base::upper
    "w" L"w" ctype_base::alnum
    "xdigit" L"xdigit" ctype_base::xdigit

2021. Further incorrect usages of result_of

Section: 20.8.9.1.2 [func.bind.bind], 30.6.1 [futures.overview], 30.6.8 [futures.async] Status: Ready Submitter: Daniel Krügler Opened: 2010-12-07 Last modified: 2012-01-14

View all other issues in [func.bind.bind].

View all issues with Ready status.

Discussion:

Issue 2017 points out some incorrect usages of result_of in the declaration of the function call operator overload of reference_wrapper, but there are more such specification defects:

  1. According to 20.8.9.1.2 [func.bind.bind] p. 3:

    [..] The effect of g(u1, u2, ..., uM) shall be INVOKE(fd, v1, v2, ..., vN, result_of<FD cv (V1, V2, ..., VN)>::type) [..]

    but fd is defined as "an lvalue of type FD constructed from std::forward<F>(f)". This means that the above usage must refer to result_of<FD cv & (V1, V2, ..., VN)> instead.

  2. Similar in 20.8.9.1.2 [func.bind.bind] p. 10 bullet 2 we have:

    if the value of is_bind_expression<TiD>::value is true, the argument is tid(std::forward<Uj>(uj)...) and its type Vi is result_of<TiD cv (Uj...)>::type

    Again, tid is defined as "lvalue of type TiD constructed from std::forward<Ti>(ti)". This means that the above usage must refer to result_of<TiD cv & (Uj...)> instead. We also have similar defect as in 2017 in regard to the argument types, this leads us to the further corrected form result_of<TiD cv & (Uj&&...)>. This is not the end: Since the Vi are similar sensitive to the argument problem, the last part must say:

    "[..] its type Vi is result_of<TiD cv & (Uj&&...)>::type &&"

    (The bound arguments Vi can never be void types, therefore we don't need to use the more defensive std::add_rvalue_reference type trait)

  3. The function template async is declared as follows (the other overload has the same problem):

    template <class F, class... Args>
    future<typename result_of<F(Args...)>::type>
    async(F&& f, Args&&... args);
    

    This usage has the some same problems as we have found in reference_wrapper (2017) and more: According to the specification in 30.6.8 [futures.async] the effective result type is that of the call of

    INVOKE(decay_copy(std::forward<F>(f)), decay_copy(std::forward<Args>(args))...)
    

    First, decay_copy potentially modifies the effective types to decay<F>::type and decay<Args>::type.... Second, the current specification is not really clear, what the value category of callable type or the arguments shall be: According to the second bullet of 30.6.8 [futures.async] p. 3:

    Invocation of the deferred function evaluates INVOKE(g, xyz) where g is the stored value of decay_copy(std::forward<F>(f)) and xyz is the stored copy of decay_copy(std::forward<Args>(args))....

    This seems to imply that lvalues are provided in contrast to the direct call expression of 30.6.8 [futures.async] p. 2 which implies rvalues instead. The specification needs to be clarified.

[2011-06-13: Daniel comments and refines the proposed wording changes]

The feedback obtained following message c++std-lib-30745 and follow-ups point to the intention, that the implied provision of lvalues due to named variables in async should be provided as rvalues to support move-only types, but the functor type should be forwarded as lvalue in bind.

If bind were newly invented, the value strategy could be improved, because now we have a preference of ref & qualified function call operator overloads. But such a change seems to be too late now. User-code that needs to bind a callable object with an ref && qualified function call operator (or conversion function to function pointer) needs to use a corresponding wrapper similar to reference_wrapper that forwards the reference as rvalue-reference instead.

The wording has been adapted to honor these observations and to fit to FDIS numbering as well.

[Bloomington, 2011]

Move to Ready

Proposed resolution:

The suggested wording changes are against the FDIS.

  1. Change 20.8.9.1.2 [func.bind.bind] p. 3 as indicated:

    template<class F, class... BoundArgs>
      unspecified bind(F&& f, BoundArgs&&... bound_args);
    

    -2- Requires: is_constructible<FD, F>::value shall be true. For each Ti in BoundArgs, is_constructible<TiD, Ti>::value shall be true. INVOKE(fd, w1, w2, ..., wN) (20.8.2) shall be a valid expression for some values w1, w2, ..., wN, where N == sizeof...(bound_args).

    -3- Returns: A forwarding call wrapper g with a weak result type (20.8.2). The effect of g(u1, u2, ..., uM) shall be INVOKE(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN), result_of<FD cv & (V1, V2, ..., VN)>::type), where cv represents the cv-qualifiers of g and the values and types of the bound arguments v1, v2, ..., vN are determined as specified below. […]

  2. Change 20.8.9.1.2 [func.bind.bind] p. 7 as indicated:

    template<class R, class F, class... BoundArgs>
       unspecified bind(F&& f, BoundArgs&&... bound_args);
    

    -6- Requires: is_constructible<FD, F>::value shall be true. For each Ti in BoundArgs, is_constructible<TiD, Ti>::value shall be true. INVOKE(fd, w1, w2, ..., wN) shall be a valid expression for some values w1, w2, ..., wN, where N == sizeof...(bound_args).

    -7- Returns: A forwarding call wrapper g with a nested type result_type defined as a synonym for R. The effect of g(u1, u2, ..., uM) shall be INVOKE(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN), R), where the values and types of the bound arguments v1, v2, ..., vN are determined as specified below. […]

  3. Change 20.8.9.1.2 [func.bind.bind] p. 10 as indicated:

    -10- The values of the bound arguments v1, v2, ..., vN and their corresponding types V1, V2, ..., VN depend on the types TiD derived from the call to bind and the cv-qualifiers cv of the call wrapper g as follows:

    • if TiD is reference_wrapper<T>, the argument is tid.get() and its type Vi is T&;
    • if the value of is_bind_expression<TiD>::value is true, the argument is tid(std::forward<Uj>(uj)...) and its type Vi is result_of<TiD cv & (Uj&&...)>::type&&;
    • if the value j of is_placeholder<TiD>::value is not zero, the argument is std::forward<Uj>(uj) and its type Vi is Uj&&;
    • otherwise, the value is tid and its type Vi is TiD cv &.
  4. This resolution assumes that the wording of 30.6.8 [futures.async] is intended to provide rvalues as arguments of INVOKE.

    Change the function signatures in header <future> synopsis 30.6.1 [futures.overview] p. 1 and in 30.6.8 [futures.async] p. 1 as indicated:

    template <class F, class... Args>
    future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
    async(F&& f, Args&&... args);
    template <class F, class... Args>
    future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type>
    async(launch policy, F&& f, Args&&... args);
    
  5. Change 30.6.8 [futures.async] as indicated: (Remark: There is also a tiny editorial correction in p. 4 that completes one :: scope specifier)

    -3- Effects: […]

    • […]
    • if policy & launch::deferred is non-zero — Stores DECAY_COPY(std::forward<F>(f)) and DECAY_COPY(std::forward<Args>(args))... in the shared state. These copies of f and args constitute a deferred function. Invocation of the deferred function evaluates INVOKE(std::move(g), std::move(xyz)) where g is the stored value of DECAY_COPY(std::forward<F>(f)) and xyz is the stored copy of DECAY_COPY(std::forward<Args>(args)).... The shared state is not made ready until the function has completed. The first call to a non-timed waiting function (30.6.4) on an asynchronous return object referring to this shared state shall invoke the deferred function in the thread that called the waiting function. Once evaluation of INVOKE(std::move(g), std::move(xyz)) begins, the function is no longer considered deferred. [ Note: If this policy is specified together with other policies, such as when using a policy value of launch::async | launch::deferred, implementations should defer invocation or the selection of the policy when no more concurrency can be effectively exploited. — end note ]

    -4- Returns: an object of type future<typename result_of<typename decay<F>::type(typename decay<Args>::type...)>::type> that refers to the associated asynchronous state created by this call to async.


2028. messages_base::catalog overspecified

Section: 22.4.7.1 [locale.messages] Status: Ready Submitter: Howard Hinnant Opened: 2011-02-14 Last modified: 2012-01-14

View all issues with Ready status.

Discussion:

In 22.4.7.1 [locale.messages], messages_base::catalog is specified to be a typedef to int. This type is subsequently used to open, access and close catalogs.

However, an OS may have catalog/messaging services that are indexed and managed by types other than int. For example POSIX, publishes the following messaging API:

typedef unspecified nl_catd;

nl_catd catopen(const char* name , int oflag);
char*   catgets(nl_catd catd, int set_id, int msg_id, const char* s);
int     catclose(nl_catd catd);

I.e., the catalog is managed with an unspecified type, not necessarily an int. Mac OS uses a void* for nl_catd (which is conforming to the POSIX standard). The current messages_base spec effectively outlaws using the built-in OS messaging service supplied for this very purpose!

[2011-02-24: Chris Jefferson updates the proposed wording, changing unspecified to unspecified signed integral type]

[2011-03-02: Daniel updates the proposed wording, changing unspecified signed integral type to unspecified signed integer type (We don't want to allow for bool or char)]

[2011-03-24 Madrid meeting]

Consensus that this resolution is the direction we would like to see.

Proposed resolution:

  1. Modify 22.4.7.1 [locale.messages]:

    namespace std {
      class messages_base {
      public:
        typedef intunspecified signed integer type catalog;
      };
      ...
    }
    

2033. Preconditions of reserve, shrink_to_fit, and resize functions

Section: 23.3.6.3 [vector.capacity], 23.3.3.3 [deque.capacity] Status: Ready Submitter: Nikolay Ivchenkov Opened: 2011-02-20 Last modified: 2012-01-14

View other active issues in [vector.capacity].

View all other issues in [vector.capacity].

View all issues with Ready status.

Discussion:

I have several questions with regard to the working paper N3225 (C++0x working draft):

  1. Where the working draft specifies preconditions for shrink_to_fit member function of std::vector and std::deque?

  2. Where the working draft specifies preconditions for 'void reserve(size_type n)' member function of std::vector?

  3. Does a call to 'void resize(size_type sz)' of std::vector require the element type to be DefaultConstructible? If yes, why such requirement is not listed in the Requires paragraph?

  4. Does a call to 'void resize(size_type sz)' of std::vector require the element type to be MoveAssignable because the call erase(begin() + sz, end()) mentioned in the Effects paragraph would require the element type to be MoveAssignable?

  5. Why CopyInsertable requirement is used for 'void resize(size_type sz)' of std::vector instead of MoveInsertable requirement?

[2011-06-12: Daniel comments and provides wording]

According to my understanding of the mental model of vector (and to some parts for deque) the some requirements are missing in the standard as response to above questions:

  1. The preconditions of shrink_to_fit for both std::vector and std::deque should impose the MoveInsertable requirements. The reason for this is, that these containers can host move-only types. For a container type X the C++03 idiom X(*this).swap(*this) imposes the CopyInsertable requirements which would make the function call ill-formed, which looks like an unacceptable restriction to me. Assuming the committee decides to support the move-only case, further wording has to be added for the situation where such a move-only type could throw an exception, because this can leave the object in an unspecified state. This seems consistent with the requirements of reserve, which seems like a very similar function to me (for vector). And this brings us smoothly to the following bullet:
  2. I agree that we are currently missing to specify the preconditions of the reserve function. My interpretation of the mental model of this function is that it should work for move-only types, which seems to be supported by the wording used in 23.3.6.3 [vector.capacity] p2:

    […] If an exception is thrown other than by the move constructor of a non-CopyInsertable type, there are no effects.

    Given this statement, the appropriate requirement is MoveInsertable into the vector.

  3. I agree that vector::resize(size_type) misses to list the DefaultConstructible requirements.
  4. Unfortunately the current specification in terms of erase implies the MoveAssignable requirements. I don't think that this implication is intended. This function requires "append" and "pop-back" effects, respectively, where the former can be realized in terms of MoveInsertable requirements. The same fix in regard to using pop_back instead of erase is necessary for the two argument overload of resize as well (no MoveAssignable is required).
  5. The CopyInsertable requirement is incorrect and should be MoveInsertable instead.

In addition to above mentioned items, the proposed resolution adds a linear complexity bound for shrink_to_fit and attempts to resolve the related issue 2066.

[ 2011 Bloomington ]

Move to Ready.

Note for editor: we do not normally refer to 'linear time' for complexity requirements, but there is agreement that any clean-up of such wording is editorial.

Proposed resolution:

This wording is relative to the FDIS.

  1. Edit 23.3.3.3 [deque.capacity] as indicated [Remark: The suggested change of p4 is not redundant, because CopyInsertable is not necessarily a refinement of MoveInsertable in contrast to the fact that CopyConstructible is a refinement of MoveConstructible]:

    void resize(size_type sz);
    

    -1- Effects: If sz <= size(), equivalent to erase(begin() + sz, end());calling pop_back() size() - sz times. If size() < sz, appends sz - size() value-initialized elements to the sequence.

    -2- Requires: T shall be MoveInsertable into *this and DefaultConstructible.

    void resize(size_type sz, const T& c);
    

    -3- Effects: If sz <= size(), equivalent to calling pop_back() size() - sz times. If size() < sz, appends sz - size() copies of c to the sequence.

    if (sz > size())
      insert(end(), sz-size(), c);
    else if (sz < size())
      erase(begin()+sz, end());
    else
      ; // do nothing
    

    -4- Requires: T shall be MoveInsertable into *this and CopyInsertable into *this.

    void shrink_to_fit();
    

    -?- Requires: T shall be MoveInsertable into *this.

    -?- Complexity: Takes at most linear time in the size of the sequence.

    -5- Remarks: shrink_to_fit is a non-binding request to reduce memory use but does not change the size of the sequence. [ Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ]

  2. Edit 23.3.6.3 [vector.capacity] as indicated including edits that also resolve 2066 [Remark: The combined listing of MoveInsertable and CopyInsertable before p12 is not redundant, because CopyInsertable is not necessarily a refinement of MoveInsertable in contrast to the fact that CopyConstructible is a refinement of MoveConstructible]:

    […]

    void reserve(size_type n);
    

    -?- Requires: T shall be MoveInsertable into *this.

    -2- Effects: A directive that informs a vector of a planned change in size, so that it can manage the storage allocation accordingly. After reserve(), capacity() is greater or equal to the argument of reserve if reallocation happens; and equal to the previous value of capacity() otherwise. Reallocation happens at this point if and only if the current capacity is less than the argument of reserve(). If an exception is thrown other than by the move constructor of a non-CopyInsertable type, there are no effects.

    -3- Complexity: It does not change the size of the sequence and takes at most linear time in the size of the sequence.

    -4- Throws: length_error if n > max_size().[footnote 266]

    -5- Remarks: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. It is guaranteed that no reallocation takes place during insertions that happen after a call to reserve() until the time when an insertion would make the size of the vector greater than the value of capacity().

    void shrink_to_fit();
    

    -?- Requires: T shall be MoveInsertable into *this.

    -?- Complexity: Takes at most linear time in the size of the sequence.

    -6- Remarks: shrink_to_fit is a non-binding request to reduce capacity() to size(). [ Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ] If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.

    […]

    void resize(size_type sz);
    

    -9- Effects: If sz <= size(), equivalent to erase(begin() + sz, end());calling pop_back() size() - sz times. If size() < sz, appends sz - size() value-initialized elements to the sequence.

    -10- Requires: T shall be CopyMoveInsertable into *this and DefaultConstructible.

    -??- Remarks: If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.

    void resize(size_type sz, const T& c);
    

    -11- Effects: If sz <= size(), equivalent to calling pop_back() size() - sz times. If size() < sz, appends sz - size() copies of c to the sequence.

    if (sz > size())
      insert(end(), sz-size(), c);
    else if (sz < size())
      erase(begin()+sz, end());
    else
      ; // do nothing
    

    -??- Requires: T shall be MoveInsertable into *this and CopyInsertable into *this.

    -12- RequiresRemarks: If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.


2035. Output iterator requirements are broken

Section: 24.2.4 [output.iterators] Status: Open Submitter: Daniel Krügler Opened: 2011-02-27 Last modified: 2012-01-14

View other active issues in [output.iterators].

View all other issues in [output.iterators].

View all issues with Open status.

Discussion:

During the Pittsburgh meeting the proposal N3066 became accepted because it fixed several severe issues related to the iterator specification. But the current working draft (N3225) does not reflect all these changes. Since I'm unaware whether every correction can be done editorial, this issue is submitted to take care of that. To give one example: All expressions of Table 108 — "Output iterator requirements" have a post-condition that the iterator is incrementable. This is impossible, because it would exclude any finite sequence that is accessed by an output iterator, such as a pointer to a C array. The N3066 wording changes did not have these effects.

[2011-03-01: Daniel comments:]

This issue has some overlap with the issue 2038 and I would prefer if we could solve both at one location. I suggest the following approach:

  1. The terms dereferencable and incrementable could be defined in a more general way not restricted to iterators (similar to the concepts HasDereference and HasPreincrement from working draft N2914). But on the other hand, all current usages of dereferencable and incrementable are involved with types that satisfy iterator requirements. Thus, I believe that it is sufficient for C++0x to add corresponding definitions to 24.2.1 [iterator.requirements.general] and to let all previous usages of these terms refer to this sub-clause. Since the same problem occurs with the past-the-end iterator, this proposal suggest providing similar references to usages that precede its definition as well.

  2. We also need to ensure that all iterator expressions get either an operational semantics in terms of others or we need to add missing pre- and post-conditions. E.g. we have the following ones without semantics:

    *r++ = o // output iterator
    *r--     // bidirectional iterator
    

    According to the SGI specification these correspond to

    { *r = o; ++r; }                         // output iterator
    { reference tmp = *r; --r; return tmp; } // bidirectional iterator
    

    respectively. Please note especially the latter expression for bidirectional iterator. It fixes a problem that we have for forward iterator as well: Both these iterator categories provide stronger guarantees than input iterator, because the result of the dereference operation is reference, and not only convertible to the value type (The exact form from the SGI documentation does not correctly refer to reference).

[2011-03-14: Daniel comments and updates the suggested wording]

In addition to the before mentioned necessary changes there is another one need, which became obvious due to issue 2042: forward_list<>::before_begin() returns an iterator value which is not dereferencable, but obviously the intention is that it should be incrementable. This leads to the conclusion that imposing dereferencable as a requirement for the expressions ++r is wrong: We only need the iterator to be incrementable. A similar conclusion applies to the expression --r of bidirectional iterators.

[ 2011 Bloomington ]

Consensus this is the correct direction, but there are (potentially) missing incrementable preconditions on some table rows, and the Remarks on when an output iterator becomes dereferencable are probably better handled outside the table, in a manner similar to the way we word for input iterators.

There was some concern about redundant pre-conditions when the operational semantic is defined in terms of operations that have preconditions, and a similar level of concern over dropping such redundancies vs. applying a consistent level of redundant specification in all the iterator tables. Wording clean-up in either direction would be welcome.

[2011-08-18: Daniel adapts the proposed resolution to honor the Bloomington request]

There is only a small number of further changes suggested to get rid of superfluous requirements and essentially non-normative assertions. Operations should not have extra pre-conditions, if defined by "in-terms-of" semantics, see e.g. a != b or a->m for Table 107. Further, some remarks, that do not impose anything or say nothing new have been removed, because I could not find anything helpful they provide. E.g. consider the remarks for Table 108 for the operations dereference-assignment and preincrement: They don't provide additional information say nothing surprising. With the new pre-conditions and post-conditions it is implied what the remarks intend to say.

[ 2011-11-03: Some observations from Alexander Stepanov via c++std-lib-31405 ]

The following sentence is dropped from the standard section on OutputIterators:

"In particular, the following two conditions should hold: first, any iterator value should be assigned through before it is incremented (this is, for an output iterator i, i++; i++; is not a valid code sequence); second, any value of an output iterator may have at most one active copy at any given time (for example, i = j; *++i = a; *j = b; is not a valid code sequence)."

[ 2011-11-04: Daniel comments and improves the wording ]

In regard to the first part of the comment, the intention of the newly proposed wording was to make clear that for the expression

*r = o

we have the precondition dereferenceable and the post-condition incrementable. And for the expression

++r

we have the precondition incrementable and the post-condition dereferenceable or past-the-end. This should not allow for a sequence like i++; i++; but I agree that it doesn't exactly say that.

In regard to the second point: To make this point clearer, I suggest to add a similar additional wording as we already have for input iterator to the "Assertion/note" column of the expression ++r:

"Post: any copies of the previous value of r are no longer required to be dereferenceable or incrementable."

The proposed has been updated to honor the observations of Alexander Stepanov.

Proposed resolution:

  1. Add a reference to 24.2.1 [iterator.requirements.general] to the following parts of the library preceding Clause 24 Iterators library: (I stopped from 23.2.5 [unord.req] on, because the remaining references are the concrete containers)

    1. 17.6.3.2 [swappable.requirements] p5:

      -5- A type X satisfying any of the iterator requirements (24.2) is ValueSwappable if, for any dereferenceable (24.2.1 [iterator.requirements.general]) object x of type X, *x is swappable.

    2. 17.6.3.5 [allocator.requirements], Table 27 — "Descriptive variable definitions", row with the expression c:

      a dereferenceable (24.2.1 [iterator.requirements.general]) pointer of type C*

    3. 20.6.3.2 [pointer.traits.functions]:

      Returns: The first template function returns a dereferenceable (24.2.1 [iterator.requirements.general]) pointer to r obtained by calling Ptr::pointer_to(r); […]

    4. 21.4.3 [string.iterators] p. 2:

      Returns: An iterator which is the past-the-end value (24.2.1 [iterator.requirements.general]).

    5. 22.4.5.1.2 [locale.time.get.virtuals] p. 11:

      iter_type do_get(iter_type s, iter_type end, ios_base& f,
        ios_base::iostate& err, tm *t, char format, char modifier) const;
      

      Requires: t shall be dereferenceable (24.2.1 [iterator.requirements.general]).

    6. 23.2.1 [container.requirements.general] p. 6:

      […] end() returns an iterator which is the past-the-end (24.2.1 [iterator.requirements.general]) value for the container. […]

    7. 23.2.3 [sequence.reqmts] p. 3:

      […] q denotes a valid dereferenceable (24.2.1 [iterator.requirements.general]) const iterator to a, […]

    8. 23.2.4 [associative.reqmts] p. 8 (I omit intentionally one further reference in the same sub-clause):

      […] q denotes a valid dereferenceable (24.2.1 [iterator.requirements.general]) const iterator to a, […]

    9. 23.2.5 [unord.req] p. 10 (I omit intentionally one further reference in the same sub-clause):

      […] q and q1 are valid dereferenceable (24.2.1 [iterator.requirements.general]) const iterators to a, […]

  2. Edit 24.2.1 [iterator.requirements.general] p. 5 as indicated (The intent is to properly define incrementable and to ensure some further library guarantee related to past-the-end iterator values):

    -5- Just as a regular pointer to an array guarantees that there is a pointer value pointing past the last element of the array, so for any iterator type there is an iterator value that points past the last element of a corresponding sequence. These values are called past-the-end values. Values of an iterator i for which the expression *i is defined are called dereferenceable. Values of an iterator i for which the expression ++i is defined are called incrementable. The library never assumes that past-the-end values are dereferenceable or incrementable. Iterators can also have singular values that are not associated with any sequence. […]

  3. Modify the column contents of Table 106 — "Iterator requirements", 24.2.2 [iterator.iterators], as indicated:

    Table 106 — Iterator requirements
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    *r reference   pre: r is dereferenceable.
    ++r X&   pre: r is incrementable.
  4. Modify the column contents of Table 107 — "Input iterator requirements", 24.2.3 [input.iterators], as indicated [Rationale: The wording changes attempt to define a minimal "independent" set of operations, namely *a and ++r, and to specify the semantics of the remaining ones. This approach seems to be in agreement with the original SGI specificationend rationale]:

    Table 107 — Input iterator requirements (in addition to Iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    a != b contextually
    convertible to bool
    !(a == b) pre: (a, b) is in the domain
    of ==.
    *a convertible to T   pre: a is dereferenceable.
    The expression
    (void)*a, *a is equivalent
    to *a.
    If a == b and (a,b) is in
    the domain of == then *a is
    equivalent to *b.
    a->m   (*a).m pre: a is dereferenceable.
    ++r X&   pre: r is dereferenceableincrementable.
    post: r is dereferenceable or
    r is past-the-end.
    post: any copies of the
    previous value of r are no
    longer required either to be
    dereferenceable, incrementable,
    or to be in the domain of ==.
    (void)r++   (void)++r equivalent to (void)++r
    *r++ convertible to T { T tmp = *r;
    ++r;
    return tmp; }
     
  5. Modify the column contents of Table 108 — "Output iterator requirements", 24.2.4 [output.iterators], as indicated [Rationale: The wording changes attempt to define a minimal "independent" set of operations, namely *r = o and ++r, and to specify the semantics of the remaining ones. This approach seems to be in agreement with the original SGI specificationend rationale]:

    Table 108 — Output iterator requirements (in addition to Iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    *r = o result is not used   pre: r is dereferenceable.
    Remark: After this operation
    r is not required to be
    dereferenceable and any copies of
    the previous value of r are no
    longer required to be dereferenceable
    or incrementable.

    post: r is incrementable.
    ++r X&   pre: r is incrementable.
    &r == &++r.
    Remark: After this operation
    r is not required to be
    dereferenceable.
    Remark: After this operation
    r is not required to be
    incrementable and any copies of
    the previous value of r are no
    longer required to be dereferenceable
    or incrementable.

    post: r is dereferenceable
    or r is past-the-end
    incrementable.
    r++ convertible to const X& { X tmp = r;
    ++r;
    return tmp; }
    Remark: After this operation
    r is not required to be
    dereferenceable.
    post: r is incrementable.
    *r++ = o result is not used { *r = o; ++r; } Remark: After this operation
    r is not required to be
    dereferenceable.
    post: r is incrementable.
  6. Modify the column contents of Table 109 — "Forward iterator requirements", 24.2.5 [forward.iterators], as indicated [Rationale: Since the return type of the expression *r++ is now guaranteed to be type reference, the implied operational semantics from input iterator based on value copies is wrong — end rationale]

    Table 109 — Forward iterator requirements (in addition to input iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    r++ convertible to const X& { X tmp = r;
    ++r;
    return tmp; }
     
    *r++ reference { reference tmp = *r;
    ++r;
    return tmp; }
     
  7. Modify the column contents of Table 110 — "Bidirectional iterator requirements", 24.2.6 [bidirectional.iterators], as indicated:

    Table 110 — Bidirectional iterator requirements (in addition to forward iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    --r X&   pre: there exists s such that
    r == ++s.
    post: r is dereferenceableincrementable.
    --(++r) == r.
    --r == --s implies r == s.
    &r == &--r.
    r-- convertible to const X& { X tmp = r;
    --r;
    return tmp; }
     
    *r-- reference { reference tmp = *r;
    --r;
    return tmp; }
     

2038. Missing definition for incrementable iterator

Section: 24.2.4 [output.iterators] Status: Open Submitter: Pete Becker Opened: 2011-02-27 Last modified: 2012-01-14

View other active issues in [output.iterators].

View all other issues in [output.iterators].

View all issues with Open status.

Discussion:

In comp.lang.c++, Vicente Botet raises the following questions:

"In "24.2.4 Output iterators" there are 3 uses of incrementable. I've not found the definition. Could some one point me where it is defined?

Something similar occurs with dereferenceable. While the definition is given in "24.2.1 In general" it is used several times before.

Shouldn't these definitions be moved to some previous section?"

He's right: both terms are used without being properly defined.

There is no definition of "incrementable".

While there is a definition of "dereferenceable", it is, in fact, a definition of "dereferenceable iterator". "dereferenceable" is used throughout Clause 23 (Containers) before its definition in Clause 24. In almost all cases it's referring to iterators, but in 17.6.3.2 [swappable.requirements] there is a mention of "dereferenceable object"; in 17.6.3.5 [allocator.requirements] the table of Descriptive variable definitions refers to a "dereferenceable pointer"; 20.6.3.2 [pointer.traits.functions] refers to a "dereferenceable pointer"; in 22.4.5.1.2 [locale.time.get.virtuals]/11 (do_get) there is a requirement that a pointer "shall be dereferenceable". In those specific cases it is not defined.

[2011-03-02: Daniel comments:]

I believe that the currently proposed resolution of issue 2035 solves this issue as well.

[ 2011 Bloomington ]

Agree with Daniel, this will be handled by the resolution of 2035.

Proposed resolution:


2039. Issues with std::reverse and std::copy_if

Section: 25.3.1 [alg.copy], 25.3.10 [alg.reverse] Status: Ready Submitter: Nikolay Ivchenkov Opened: 2011-03-02 Last modified: 2012-01-14

View all issues with Ready status.

Discussion:

  1. In the description of std::reverse

    Effects: For each non-negative integer i <= (last - first)/2, applies iter_swap to all pairs of iterators first + i, (last - i) - 1.

    should be changed to

    Effects: For each non-negative integer i < (last - first)/2, applies iter_swap to all pairs of iterators first + i, (last - i) - 1.

    Here i shall be strictly less than (last - first)/2.

  2. In the description of std::copy_if Returns paragraph is missing.

[2011-03-02: Daniel drafts wording]

Proposed resolution:

  1. Modify 25.3.10 [alg.reverse] p. 1 as indicated:

    1 Effects: For each non-negative integer i <= (last - first)/2, applies iter_swap to all pairs of iterators first + i, (last - i) - 1.

  2. Add the following Returns element after 25.3.1 [alg.copy] p. 9:

    template<class InputIterator, class OutputIterator, class Predicate>
    OutputIterator copy_if(InputIterator first, InputIterator last,
       OutputIterator result, Predicate pred);
    

    8 Requires: The ranges [first,last) and [result,result + (last - first)) shall not overlap.

    9 Effects: Copies all of the elements referred to by the iterator i in the range [first,last) for which pred(*i) is true.

    ?? Returns: The end of the resulting range.

    10 Complexity: Exactly last - first applications of the corresponding predicate.

    11 Remarks: Stable.


2044. No definition of "Stable" for copy algorithms

Section: 17.6.5.7 [algorithm.stable] Status: Ready Submitter: Pablo Halpern Opened: 2011-03-24 Last modified: 2012-01-14

View all issues with Ready status.

Discussion:

17.6.5.7 [algorithm.stable] specified the meaning of "stable" when applied to the different types of algorithms. The second bullet says:

— For the remove algorithms the relative order of the elements that are not removed is preserved.

There is no description of what "stable" means for copy algorithms, even though the term is applied to copy_if (and perhaps others now or in the future). Thus, copy_if is using the term without a precise definition.

[Bloomington, 2011]

Move to Ready

Proposed resolution:

This wording is relative to the FDIS.

  1. In 17.6.5.7 [algorithm.stable] p. 1 change as indicated:

    When the requirements for an algorithm state that it is “stable” without further elaboration, it means:

    • For the sort algorithms the relative order of equivalent elements is preserved.
    • For the remove and copy algorithms the relative order of the elements that are not removed is preserved.
    • For the merge algorithms, for equivalent elements in the original two ranges, the elements from the first range precede the elements from the second range.

2045. forward_list::merge and forward_list::splice_after with unequal allocators

Section: 23.3.4.6 [forwardlist.ops] Status: Ready Submitter: Pablo Halpern Opened: 2011-03-24 Last modified: 2012-01-14

View other active issues in [forwardlist.ops].

View all other issues in [forwardlist.ops].

View all issues with Ready status.

Discussion:

See also: 1215

list::merge and list::splice have the requirement that the two lists being merged or spliced must use the same allocator. Otherwise, moving list nodes from one container to the other would corrupt the data structure. The same requirement is needed for forward_list::merge and forward_list::splice_after.

[ 2011 Bloomington ]

Move to Ready.

Proposed resolution:

This wording is relative to the FDIS.

  1. In 23.3.4.6 [forwardlist.ops] p. 1 change as indicated:

    void splice_after(const_iterator position, forward_list<T,Allocator>& x);
    void splice_after(const_iterator position, forward_list<T,Allocator>&& x);
    

    1 - Requires: position is before_begin() or is a dereferenceable iterator in the range [begin(),end()). get_allocator() == x.get_allocator(). &x != this.

  2. In 23.3.4.6 [forwardlist.ops] p. 5 change as indicated:

    void splice_after(const_iterator position, forward_list<T,Allocator>& x, const_iterator i);
    void splice_after(const_iterator position, forward_list<T,Allocator>&& x, const_iterator i);
    

    5 - Requires: position is before_begin() or is a dereferenceable iterator in the range [begin(),end()). The iterator following i is a dereferenceable iterator in x. get_allocator() == x.get_allocator().

  3. In 23.3.4.6 [forwardlist.ops] p. 9 change as indicated:

    void splice_after(const_iterator position, forward_list<T,Allocator>& x, 
                      const_iterator first, const_iterator last);
    void splice_after(const_iterator position, forward_list<T,Allocator>&& x, 
                      const_iterator first, const_iterator last);
    

    9 - Requires: position is before_begin() or is a dereferenceable iterator in the range [begin(),end()). (first,last) is a valid range in x, and all iterators in the range (first,last) are dereferenceable. position is not an iterator in the range (first,last). get_allocator() == x.get_allocator().

  4. In 23.3.4.6 [forwardlist.ops] p. 18 change as indicated:

    void merge(forward_list<T,Allocator>& x);
    void merge(forward_list<T,Allocator>&& x);
    template <class Compare> void merge(forward_list<T,Allocator>& x, Compare comp);
    template <class Compare> void merge(forward_list<T,Allocator>&& x, Compare comp);
    

    18 - Requires: comp defines a strict weak ordering ([alg.sorting]), and *this and x are both sorted according to this ordering. get_allocator() == x.get_allocator().


2047. Incorrect "mixed" move-assignment semantics of unique_ptr

Section: 20.7.1.2.3 [unique.ptr.single.asgn] Status: Ready Submitter: Daniel Krügler Opened: 2011-04-16 Last modified: 2012-01-14

View all other issues in [unique.ptr.single.asgn].

View all issues with Ready status.

Discussion:

The semantics described in 20.7.1.2.3 [unique.ptr.single.asgn] p. 6

Effects: Transfers ownership from u to *this as if […] followed by an assignment from std::forward<D>(u.get_deleter()).

contradicts to the pre-conditions described in p. 4:

Requires: If E is not a reference type, assignment of the deleter from an rvalue of type E shall be well-formed and shall not throw an exception. Otherwise, E is a reference type and assignment of the deleter from an lvalue of type E shall be well-formed and shall not throw an exception.

Either the pre-conditions are incorrect or the semantics should be an assignment from std::forward<E>(u.get_deleter()), instead.

It turns out that this contradiction is due to an incorrect transcription from the proposed resolution of 983 to the finally accepted proposal n3073 (see bullet 12) as confirmed by Howard Hinnant, thus the type argument provided to std::forward must be fixed as indicated.

[Bloomington, 2011]

Move to Ready

Proposed resolution:

This wording is relative to the FDIS.

  1. Edit 20.7.1.2.3 [unique.ptr.single.asgn] p. 6 as indicated:

    template <class U, class E> unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;
    

    4 - Requires: If E is not a reference type, assignment of the deleter from an rvalue of type E shall be well-formed and shall not throw an exception. Otherwise, E is a reference type and assignment of the deleter from an lvalue of type E shall be well-formed and shall not throw an exception.

    5 - Remarks: This operator shall not participate in overload resolution unless:

    • unique_ptr<U, E>::pointer is implicitly convertible to pointer and
    • U is not an array type.

    6 - Effects: Transfers ownership from u to *this as if by calling reset(u.release()) followed by an assignment from std::forward<DE>(u.get_deleter()).

    7 - Returns: *this.


2048. Unnecessary mem_fn overloads

Section: 20.8 [function.objects], 20.8.10 [func.memfn] Status: Review Submitter: Jonathan Wakely Opened: 2011-04-18 Last modified: 2012-01-14

View all other issues in [function.objects].

View all issues with Review status.

Discussion:

The mem_fn overloads for member functions are redundant and misleading and should be removed from the post-C++11 WP.

I believe the history of the overloads is as follows:

In TR1 and in C++0x prior to the N2798 draft, mem_fn was specified by a single signature:

template<class R, class T> unspecified mem_fn(R T::* pm);

and was accompanied by the remark "Implementations may implement mem_fn as a set of overloaded function templates." This remark predates variadic templates and was presumably to allow implementations to provide overloads for a limited number of function parameters, to meet the implementation-defined limit on numbers of template parameters.

N2770 "Concepts for the C++0x Standard Library: Utilities" added separate overloads for pointers to member functions, apparently so that function parameters would require the CopyConstructible concept (those overloads first appeared in N2322.) The overloads failed to account for varargs member functions (i.e. those declared with an ellipsis in the parameter-declaration-clause) e.g.

struct S {
 int f(int, ...);
};

Syntactically such a function would be handled by the original mem_fn(R T::* pm) signature, the only minor drawback being that there would be no CopyConstructible requirement on the parameter list. (Core DR 547 clarifies that partial specializations can be written to match cv-qualified and ref-qualified functions to support the case where R T::* matches a pointer to member function type.)

LWG issue 920 pointed out that additional overloads were missing for member functions with ref-qualifiers. These were not strictly necessary, because such functions are covered by the mem_fn(R T::* pm) signature.

Concepts were removed from the draft and N3000 restored the original single signature and accompanying remark.

LWG 1230 was opened to strike the remark again and to add an overload for member functions (this overload was unnecessary for syntactic reasons and insufficient as it didn't handle member functions with cv-qualifiers and/or ref-qualifiers.)

920 (and 1230) were resolved by restoring a full set of (non-concept-enabled) overloads for member functions with cv-qualifiers and ref-qualifiers, but as in the concept-enabled draft there were no overloads for member functions with an ellipsis in the parameter-declaration-clause. This is what is present in the FDIS.

Following the thread beginning with message c++std-lib-30675, it is my understanding that all the mem_fn overloads for member functions are unnecessary and were only ever added to allow concept requirements. I'm not aware of any reason implementations cannot implement mem_fn as a single function template. Without concepts the overloads are redundant, and the absence of overloads for varargs functions can be interpreted to imply that varargs functions are not intended to work with mem_fn. Clarifying the intent by adding overloads for varargs functions would expand the list of 12 redundant overloads to 24, it would be much simpler to remove the 12 redundant overloads entirely.

[Bloomington, 2011]

Move to Review.

The issue and resolution appear to be correct, but there is some concern that the wording of INVOKE may be different depending on whether you pass a pointer-to-member-data or pointer-to-member-function. That might make the current wording necessary after all, and then we might need to add the missing elipsis overloads.

There was some concern that the Remark confirming implementors had freedom to implement this as a set of overloaded functions may need to be restored if we delete the specification for these overloads.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change the <functional> synopsis 20.8 [function.objects] p. 2 as follows:

    namespace std {
      […]
      // [func.memfn], member function adaptors:
      template<class R, class T> unspecified mem_fn(R T::*);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...));
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) volatile);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const volatile);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) &);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const &);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) volatile &);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const volatile &);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) &&);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const &&);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) volatile &&);
      template<class R, class T, class... Args>
      unspecified mem_fn(R (T::*)(Args...) const volatile &&);
    
      […]
    }
    
  2. Change 20.8.10 [func.memfn] as follows:

    template<class R, class T> unspecified mem_fn(R T::*);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...));
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) volatile);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const volatile);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) &);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const &);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) volatile &);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const volatile &);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) &&);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const &&);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) volatile &&);
    template<class R, class T, class... Args>
    unspecified mem_fn(R (T::*)(Args...) const volatile &&);
    

2049. is_destructible is underspecified

Section: 20.9.4.3 [meta.unary.prop] Status: Review Submitter: Daniel Krügler Opened: 2011-04-18 Last modified: 2012-01-14

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with Review status.

Discussion:

The conditions for the type trait is_destructible to be true are described in Table 49 — Type property predicates:

For a complete type T and given
template <class U> struct test { U u; };,
test<T>::~test() is not deleted.

This specification does not say what the result would be for function types or for abstract types:

  1. For an abstract type X the instantiation test<X> is already ill-formed, so we cannot say anything about whether the destructor would be deleted or not.
  2. In regard to function types, there exists a special rule in the core language, 14.3.1 [temp.arg.type] p. 3, which excludes member functions to be declared via the type of the template parameter:

    If a declaration acquires a function type through a type dependent on a template-parameter and this causes a declaration that does not use the syntactic form of a function declarator to have function type, the program is ill-formed.

    [ Example:

    template<class T> struct A {
      static T t;
    };
    typedef int function();
    A<function> a; // ill-formed: would declare A<function>::t
                   // as a static member function
    

    end example ]

    which has the same consequence as for abstract types, namely that the corresponding instantiation of test is already ill-formed and we cannot say anything about the destructor.

To solve this problem, I suggest to specify function types as trivially and nothrowing destructible, because above mentioned rule is very special for templates. For non-templates, a typedef can be used to introduce a member as member function as clarified in 8.3.5 [dcl.fct] p. 10.

For abstract types, two different suggestions have been brought to my attention: Either declare them as unconditionally non-destructible or check whether the expression

std::declval<T&>().~T()

is well-formed in an unevaluated context. The first solution is very easy to specify, but the second version has the advantage for providing more information to user-code. This information could be quite useful, if generic code is supposed to invoke the destructor of a reference to a base class indirectly via a delete expression, as suggested by Howard Hinnant:

template <class T>
my_pointer<T>::~my_pointer() noexcept(is_nothrow_destructible<T>::value)
{
   delete ptr_;
}

Additional to the is_destructible traits, its derived forms is_trivially_destructible and is_nothrow_destructible are similarly affected, because their wording refers to "the indicated destructor" and probably need to be adapted as well.

[ 2011 Bloomington ]

After discussion about to to handle the exceptional cases of reference types, function types (available by defererencing a function pointer) and void types, Howard supplied proposed wording.

[ 2011-08-20 Daniel comments and provides alternatives wording ]

The currently proposed wording would have the consequence that every array type is not destructible, because the pseudo-destructor requires a scalar type with the effect that the expression

std::declval<T&>().~T()

is not well-formed for e.g. T equal to int[3]. The intuitive solution to fix this problem would be to adapt the object type case to refer to the expression

std::declval<U&>().~U()

with U equal to remove_all_extents<T>::type, but that would have the effect that arrays of unknown bounds would be destructible, if the element type is destructible, which was not the case before (This was intentionally covered by the special "For a complete type T" rule in the FDIS).

Suggestion: Use the following definition instead:

Let U be remove_all_extents<T>::type.
For incomplete types and function types, is_destructible<T>::value is false.
For object types, if the expression std::declval<U&>().~U() is well-formed
when treated as an unevaluated operand (Clause 5), then is_destructible<T>::value
is true, otherwise it is false.
For reference types, is_destructible<T>::value is true.

This wording also harmonizes with the "unevaluated operand" phrase used in other places, there does not exist the definition of an "unevaluated context"

Note: In the actually proposed wording this wording has been slightly reordered with the same effects.

Howard's (old) proposed resolution:

Update 20.9.4.3 [meta.unary.prop], table 49:

template <class T> struct is_destructible; For a complete type T and given template <class U> struct test { U u; };, test<T>::~test() is not deleted.
For object types, if the expression: std::declval<T&>().~T() is well-formed in an unevaluated context then is_destructible<T>::value is true, otherwise it is false.
For void types, is_destructible<T>::value is false.
For reference types, is_destructible<T>::value is true.
For function types, is_destructible<T>::value is false.
T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.

Proposed resolution:

Update 20.9.4.3 [meta.unary.prop], table 49:

template <class T> struct is_destructible; For a complete type T and given template <class U> struct test { U u; };, test<T>::~test() is not deleted.
For reference types, is_destructible<T>::value is true.
For incomplete types and function types, is_destructible<T>::value is false.
For object types and given U equal to remove_all_extents<T>::type,
if the expression std::declval<U&>().~U() is well-formed when treated as an
unevaluated operand (Clause 5 [expr]), then is_destructible<T>::value is true,
otherwise it is false.
T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.

2050. Unordered associative containers do not use allocator_traits to define member types

Section: 23.5 [unord] Status: Ready Submitter: Tom Zieberman Opened: 2011-04-29 Last modified: 2012-01-14

View all other issues in [unord].

View all issues with Ready status.

Discussion:

The unordered associative containers define their member types reference, const_reference, pointer, const_pointer in terms of their template parameter Allocator (via allocator_type typedef). As a consequence, only the allocator types, that provide sufficient typedefs, are usable as allocators for unordered associative containers, while other containers do not have this deficiency. In addition to that, the definitions of said typedefs are different from ones used in the other containers. This is counterintuitive and introduces a certain level of confusion. These issues can be fixed by defining pointer and const_pointer typedefs in terms of allocator_traits<Allocator> and by defining reference and const_reference in terms of value_type as is done in the other containers.

[ 2011 Bloomington. ]

Move to Ready.

Proposed resolution:

This wording is relative to the FDIS.


2052. Mixup between mapped_type and value_type for associative containers

Section: 23.2.4 [associative.reqmts] Status: Open Submitter: Marc Glisse Opened: 2011-05-04 Last modified: 2012-01-14

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with Open status.

Discussion:

(this is basically reopening the first part of issue 2006, as discussed in the thread starting at c++std-lib-30698 )

Section 23.2.4 [associative.reqmts]

In Table 102, several uses of T (which means mapped_type here) should be value_type instead. This is almost editorial. For instance:

a_uniq.emplace(args)

Requires: T shall be EmplaceConstructible into X from args.

Effects: Inserts a T object t constructed with std::forward<Args>(args)... if and only if there is no element in the container with key equivalent to the key of t. The bool component of the returned pair is true if and only if the insertion takes place, and the iterator component of the pair points to the element with key equivalent to the key of t.

[ 2011 Bloomington ]

Not even an exhaustive list of problem locations. No reason to doubt issue.

Pablo agrees to provide wording.

[ 2011-09-04 Pablo Halpern provides improved wording ]

Proposed resolution:

In both section 23.2.4 [associative.reqmts] Table 102 and 23.2.5 [unord.req], Table 103, make the following text replacements:

Original text, in FDIS Replacement text
T is CopyInsertable into X and CopyAssignable. value_type is CopyInsertable into X, key_type is CopyAssignable, and mapped_type is CopyAssignable (for containers having a mapped_type)
T is CopyInsertable value_type is CopyInsertable
T shall be CopyInsertable value_type shall be CopyInsertable
T shall be MoveInsertable value_type shall be MoveInsertable
T shall be EmplaceConstructible value_type shall be EmplaceConstructible
T object value_type object

[ Notes to the editor: The above are carefully selected phrases that can be used for global search-and-replace within the specified sections without accidentally making changes to correct uses T. ]


2053. Errors in regex bitmask types

Section: 28.5 [re.const] Status: Ready Submitter: Jonathan Wakely Opened: 2011-05-09 Last modified: 2012-01-14

View all issues with Ready status.

Discussion:

When N3110 was applied to the WP some redundant "static" keywords were added and one form of initializer which isn't valid for enumeration types was replaced with another form of invalid initializer.

[ 2011 Bloomington. ]

Move to Ready.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change 28.5.1 [re.synopt] as indicated:

    namespace std {
      namespace regex_constants {
        typedef T1 syntax_option_type;
        static constexpr syntax_option_type icase = unspecified ;
        static constexpr syntax_option_type nosubs = unspecified ;
        static constexpr syntax_option_type optimize = unspecified ;
        static constexpr syntax_option_type collate = unspecified ;
        static constexpr syntax_option_type ECMAScript = unspecified ;
        static constexpr syntax_option_type basic = unspecified ;
        static constexpr syntax_option_type extended = unspecified ;
        static constexpr syntax_option_type awk = unspecified ;
        static constexpr syntax_option_type grep = unspecified ;
        static constexpr syntax_option_type egrep = unspecified ;
      }
    }
    
  2. Change 28.5.2 [re.matchflag] as indicated:

    namespace std {
      namespace regex_constants {
        typedef T2 match_flag_type;
        static constexpr match_flag_type match_default = 0{};
        static constexpr match_flag_type match_not_bol = unspecified ;
        static constexpr match_flag_type match_not_eol = unspecified ;
        static constexpr match_flag_type match_not_bow = unspecified ;
        static constexpr match_flag_type match_not_eow = unspecified ;
        static constexpr match_flag_type match_any = unspecified ;
        static constexpr match_flag_type match_not_null = unspecified ;
        static constexpr match_flag_type match_continuous = unspecified ;
        static constexpr match_flag_type match_prev_avail = unspecified ;
        static constexpr match_flag_type format_default = 0{};
        static constexpr match_flag_type format_sed = unspecified ;
        static constexpr match_flag_type format_no_copy = unspecified ;
        static constexpr match_flag_type format_first_only = unspecified ;
      }
    }
    
  3. Change 28.5.3 [re.err] as indicated:

    namespace std {
      namespace regex_constants {
        typedef T3 error_type;
        static constexpr error_type error_collate = unspecified ;
        static constexpr error_type error_ctype = unspecified ;
        static constexpr error_type error_escape = unspecified ;
        static constexpr error_type error_backref = unspecified ;
        static constexpr error_type error_brack = unspecified ;
        static constexpr error_type error_paren = unspecified ;
        static constexpr error_type error_brace = unspecified ;
        static constexpr error_type error_badbrace = unspecified ;
        static constexpr error_type error_range = unspecified ;
        static constexpr error_type error_space = unspecified ;
        static constexpr error_type error_badrepeat = unspecified ;
        static constexpr error_type error_complexity = unspecified ;
        static constexpr error_type error_stack = unspecified ;
      }
    }
    

2054. time_point constructors need to be constexpr

Section: 20.11.6 [time.point] Status: Open Submitter: Anthony Williams Opened: 2011-05-13 Last modified: 2012-01-14

View all issues with Open status.

Discussion:

In 20.11.6 [time.point], time_point::min() and time_point::max() are listed as constexpr. However, time_point has no constexpr constructors, so is not a literal type, and so these functions cannot be constexpr without adding a constexpr constructor for implementation purposes.

Proposed resolution: Add constexpr to the constructors of time_point. The effects of the constructor template basically imply that the member function time_since_epoch() is intended to be constexpr as well.

Proposed resolution:

This wording is relative to the FDIS.

  1. Alter the class template definition in 20.11.6 [time.point] as follows:

    template <class Clock, class Duration = typename Clock::duration>
    class time_point {
      […]
    public:
      // 20.11.6.1, construct:
      constexpr time_point(); // has value epoch
      constexpr explicit time_point(const duration& d); // same as time_point() + d
      template <class Duration2>
        constexpr time_point(const time_point<clock, Duration2>& t);
    
      // 20.11.6.2, observer:
      constexpr duration time_since_epoch() const;
      […]
    };
    
  2. Alter the declarations in 20.11.6.1 [time.point.cons]:

    constexpr time_point();
    

    -1- Effects: Constructs an object of type time_point, initializing d_ with duration::zero(). Such a time_point object represents the epoch.

    constexpr explicit time_point(const duration& d);
    

    -2- Effects: Constructs an object of type time_point, initializing d_ with d. Such a time_point object represents the epoch + d.

    template <class Duration2>
      constexpr time_point(const time_point<clock, Duration2>& t);
    

    -3- Remarks: This constructor shall not participate in overload resolution unless Duration2 is implicitly convertible to duration.

    -4- Effects: Constructs an object of type time_point, initializing d_ with t.time_since_epoch().

  3. Alter the declaration in 20.11.6.2 [time.point.observer]:

    constexpr duration time_since_epoch() const;
    

    -1- Returns: d_.


2056. future_errc enums start with value 0 (invalid value for broken_promise)

Section: 30.6.1 [futures.overview] Status: Review Submitter: Nicolai Josuttis Opened: 2011-05-18 Last modified: 2012-01-14

View other active issues in [futures.overview].

View all other issues in [futures.overview].

View all issues with Review status.

Discussion:

In 30.6.1 [futures.overview] enum class future_errc is defined as follows:

enum class future_errc {
  broken_promise,
  future_already_retrieved,
  promise_already_satisfied,
  no_state
};

With this declaration broken_promise has value 0, which means that for a future_error f with this code

f.code().operator bool()

yields false, which makes no sense. 0 has to be reserved for "no error". So, the enums defined here have to start with 1.

Howard, Anthony, and Jonathan have no objections.

[Discussion in Bloomington 2011-08-16]

Previous resolution:

This wording is relative to the FDIS.

  1. In 30.6.1 [futures.overview], header <future> synopsis, fix the declaration of future_errc as follows:

    namespace std {
      enum class future_errc {
        broken_promise,
        future_already_retrieved = 1,
        promise_already_satisfied,
        no_state,
        broken_promise
      };
      […]
    }
    

Is this resolution overspecified? These seem to be all implementation-defined. How do users add new values and not conflict with established error codes?

PJP proxy says: over-specified. boo.

Other error codes: look for is_error_code_enum specializations. Only one exists io_errc

Peter: I don't see any other parts of the standard that specify error codes where we have to do something similar.

Suggest that for every place where we add an error code, the following:

  1. no zero values
  2. all implementation defined values, so future_already_retrieved = implementation_defined
  3. values are distinct

Proposed resolution:

This wording is relative to the FDIS.

In 30.6.1 [futures.overview], header <future> synopsis, fix the declaration of future_errc as follows:

namespace std {
  enum class future_errc {
    broken_promise = implementation defined,
    future_already_retrieved = implementation defined,
    promise_already_satisfied = implementation defined,
    no_state = implementation defined
  };
  […]
}

In 30.6.1 [futures.overview], header <future> synopsis, add a paragraph after paragraph 2 as follows:

The enum values of future_errc are distinct and not zero.

2057. time_point + duration semantics should be made constexpr conforming

Section: 20.11.6.5 [time.point.nonmember] Status: Open Submitter: Daniel Krügler Opened: 2011-05-21 Last modified: 2012-01-14

View all issues with Open status.

Discussion:

It has been observed by LWG 2054 that the specification of some time_point member functions already imply that time_point needs to be a literal type and suggests to specify the constructors and the member function time_since_epoch() as constexpr functions at the minimum necessary. Adding further constexpr specifier to other operations should clearly be allowed and should probably be done as well. But to allow for further constexpr functions in the future requires that their semantics is compatible to operations allowed in constexpr functions. This is already fine for all operations, except this binary plus operator:

template <class Clock, class Duration1, class Rep2, class Period2>
time_point<Clock, typename common_type<Duration1, duration<Rep2, Period2>>::type>
operator+(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs);

-1- Returns: CT(lhs) += rhs, where CT is the type of the return value.

for similar reasons as those mentioned in 2020. The semantics should be fixed to allow for making them constexpr. This issue should also be considered as a placeholder for a request to make the remaining time_point operations similarly constexpr as had been done for duration.

Proposed resolution:

This wording is relative to the FDIS.

  1. In 20.11.6.5 [time.point.nonmember], p.1 change the Returns element semantics as indicated:

    template <class Clock, class Duration1, class Rep2, class Period2>
    time_point<Clock, typename common_type<Duration1, duration<Rep2, Period2>>::type>
    operator+(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs);
    

    -1- Returns: CT(lhs) += rhsCT(lhs.time_since_epoch() + rhs), where CT is the type of the return value.


2058. valarray and begin/end

Section: 26.6 [numarray] Status: Review Submitter: Gabriel Dos Reis Opened: 2011-05-17 Last modified: 2012-01-14

View all other issues in [numarray].

View all issues with Review status.

Discussion:

It was just brought to my attention that the pair of functions begin/end were added to valarray component. Those additions strike me as counter to the long standing agreement that valarray<T> is not yet another container. Valarray values are in general supposed to be treated as a whole, and as such has a loose specification allowing expression template techniques.

The addition of these functions sound to me as making it much harder (or close to impossible) to effectively use expression templates as implementation techniques, for no clear benefits.

My recommendation would be to drop begin/end - or at least for the const valarray<T>& version. I strongly believe those are defects.

[This issue was discussed on the library reflector starting from c++std-lib-30761. Some of the key conclusions of this discussion were:]

  1. The begin/end members were added to allow valarray to participate in the new range-based for-loop by n2930 and not to make them container-like.
  2. It is currently underspecified when the iterator values returned from begin/end become invalidated. To fix this, these invalidation rules need at least to reflect the invalidation rules of the references returned by the operator[] overloads of valarray (26.6.2.4 [valarray.access]).
  3. A further problem is that the requirements expressed in 26.6.1 [valarray.syn] p.3-5 enforce an implementation to provide further overloads of begin/end, if the replacement type technique is used (which was clearly part of the design of valarray). Providing such additional overloads would also lead to life-time problems in examples like begin(x + y) where x and y are expressions involving valarray objects. To fix this, the begin/end overloads could be explicitly excluded from the general statements of 26.6.1 [valarray.syn] p.3-5. This would make it unspecified whether the expression begin(x + y) would be well-formed, portable code would need to write this as begin(std::valarray<T>(x + y)).

[ 2011 Bloomington ]

The intent of these overloads is entirely to support the new for syntax, and not to create new containers.

Stefanus provides suggested wording.

Proposed resolution:

In 26.6.1 [valarray.syn]/4, make the following insertion:

4 Implementations introducing such replacement types shall provide additional functions and operators as follows:

In 26.6.10 [valarray.range], make the following insertion:

1 In the begin and end function templates that follow, unspecified1 is a type that meets the requirements of a mutable random access iterator (24.2.7) whose value_type is the template parameter T and whose reference type is T&. unspecified2 is a type that meets the requirements of a constant random access iterator (24.2.7) whose value_type is the template parameter T and whose reference type is const T&.

2 The iterators returned by begin and end for an array are guaranteed to be valid until the member function resize(size_t, T) (26.6.2.8 [valarray.members]) is called for that array or until the lifetime of that array ends, whichever happens first.


2059. C++0x ambiguity problem with map::erase

Section: 23.4.4 [map] Status: Review Submitter: Christopher Jefferson Opened: 2011-05-18 Last modified: 2012-01-14

View all other issues in [map].

View all issues with Review status.

Discussion:

map::erase (and several related methods) took an iterator in C++03, but take a const_iterator in C++0x. This breaks code where the map's key_type has a constructor which accepts an iterator (for example a template constructor), as the compiler cannot choose between erase(const key_type&) and erase(const_iterator).

#include <map>

struct X
{
  template<typename T>
  X(T&) {}
};

bool operator<(const X&, const X&) { return false; }

void erasor(std::map<X,int>& s, X x)
{
  std::map<X,int>::iterator it = s.find(x);
  if (it != s.end())
    s.erase(it);
}

[ 2011 Bloomington ]

This issue affects only associative container erase calls, and is not more general, as these are the only functions that are also overloaded on another single arguement that might cause confusion - the erase by key method. The complete resolution should simply restore the iterator overload in addition to the const_iterator overload for all eight associative containers.

Proposed wording supplied by Alan Talbot, and moved to Review.

Proposed resolution:

Editorial note: The following things are different between 23.2.4 [associative.reqmts] p.8 and 23.2.5 [unord.req] p.10. These should probably be reconciled.

  1. First uses the convention "denotes"; second uses the convention "is".
  2. First redundantly says: "If no such element exists, returns a.end()." in erase table entry, second does not.

23.2.4 [associative.reqmts] Associative containers

8 In Table 102, X denotes an associative container class, a denotes a value of X, a_uniq denotes a value of X when X supports unique keys, a_eq denotes a value of X when X supports multiple keys, u denotes an identifier, i and j satisfy input iterator requirements and refer to elements implicitly convertible to value_type, [i,j) denotes a valid range, p denotes a valid const iterator to a, q denotes a valid dereferenceable const iterator to a, r denotes a valid dereferenceable iterator to a, [q1, q2) denotes a valid range of const iterators in a, il designates an object of type initializer_list<value_type>, t denotes a value of X::value_type, k denotes a value of X::key_type and c denotes a value of type X::key_compare. A denotes the storage allocator used by X, if any, or std::allocator<X::value_type> otherwise, and m denotes an allocator of a type convertible to A.

23.2.4 [associative.reqmts] Associative containers Table 102

Add row:

a.erase(r) iterator erases the element pointed to by r. Returns an iterator pointing to the element immediately following r prior to the element being erased. If no such element exists, returns a.end(). amortized constant

23.2.5 [unord.req] Unordered associative containers

10 In table 103: X is an unordered associative container class, a is an object of type X, b is a possibly const object of type X, a_uniq is an object of type X when X supports unique keys, a_eq is an object of type X when X supports equivalent keys, i and j are input iterators that refer to value_type, [i, j) is a valid range, p and q2 are valid const iterators to a, q and q1 are valid dereferenceable const iterators to a, r is a valid dereferenceable iterator to a, [q1,q2) is a valid range in a, il designates an object of type initializer_list<value_type>, t is a value of type X::value_type, k is a value of type key_type, hf is a possibly const value of type hasher, eq is a possibly const value of type key_equal, n is a value of type size_type, and z is a value of type float.

23.2.5 [unord.req] Unordered associative containers Table 103

Add row:

a.erase(r) iterator Erases the element pointed to by r. Returns the iterator immediately following r prior to the erasure. Average case O(1), worst case O(a.size()).

23.4.4.1 [map.overview] Class template map overview p. 2

iterator erase(iterator position);
iterator erase(const_iterator position);
size_type erase(const key_type& x);
iterator erase(const_iterator first, const_iterator last);

23.4.5.1 [multimap.overview] Class template multimap overview p. 2

iterator erase(iterator position);
iterator erase(const_iterator position);
size_type erase(const key_type& x);
iterator erase(const_iterator first, const_iterator last);

23.4.6.1 [set.overview] Class template set overview p. 2

iterator erase(iterator position);
iterator erase(const_iterator position);
size_type erase(const key_type& x);
iterator erase(const_iterator first, const_iterator last);

23.4.7.1 [multiset.overview] Class template multiset overview

iterator erase(iterator position);
iterator erase(const_iterator position);
size_type erase(const key_type& x);
iterator erase(const_iterator first, const_iterator last);

23.5.4.1 [unord.map.overview] Class template unordered_map overview p. 3

iterator erase(iterator position);
iterator erase(const_iterator position);
size_type erase(const key_type& x);
iterator erase(const_iterator first, const_iterator last);

23.5.5.1 [unord.multimap.overview] Class template unordered_multimap overview p. 3

iterator erase(iterator position);
iterator erase(const_iterator position);
size_type erase(const key_type& x);
iterator erase(const_iterator first, const_iterator last);

23.5.6.1 [unord.set.overview] Class template unordered_set overview p. 3

iterator erase(iterator position);
iterator erase(const_iterator position);
size_type erase(const key_type& x);
iterator erase(const_iterator first, const_iterator last);

23.5.7.1 [unord.multiset.overview] Class template unordered_multiset overview p. 3

iterator erase(iterator position);
iterator erase(const_iterator position);
size_type erase(const key_type& x);
iterator erase(const_iterator first, const_iterator last);

[diff.cpp03.containers] C.2.12 Clause 23: containers library

23.2.3, 23.2.4

Change: Signature changes: from iterator to const_iterator parameters

Rationale: Overspecification. Effects: The signatures of the following member functions changed from taking an iterator to taking a const_iterator:

Valid C++ 2003 code that uses these functions may fail to compile with this International Standard.


2061. make_move_iterator and arrays

Section: 24.3 [iterator.synopsis], 24.5.3 [move.iterators] Status: Ready Submitter: Marc Glisse Opened: 2011-05-28 Last modified: 2012-01-14

View all other issues in [iterator.synopsis].

View all issues with Ready status.

Discussion:

The standard library always passes template iterators by value and never by reference, which has the nice effect that an array decays to a pointer. There is one exception: make_move_iterator.

#include <iterator>
int main(){
  int a[]={1,2,3,4};
  std::make_move_iterator(a+4);
  std::make_move_iterator(a); // fails here
}

[ 2011 Bloomington. ]

Move to Ready.

Proposed resolution:

This wording is relative to the FDIS.

  1. Modify the header <iterator> synopsis in 24.3 [iterator.synopsis]:

    namespace std {
      […]
      template <class Iterator>
      move_iterator<Iterator> make_move_iterator(const Iterator&Iterator i);
    
      […]
    }
    
  2. Modify the class template move_iterator synopsis in 24.5.3.1 [move.iterator]:

    namespace std {
      […]
      template <class Iterator>
      move_iterator<Iterator> make_move_iterator(const Iterator&Iterator i);
    }
    
  3. Modify 24.5.3.3.14 [move.iter.nonmember]:

    template <class Iterator>
    move_iterator<Iterator> make_move_iterator(const Iterator&Iterator i);
    

    -3- Returns: move_iterator<Iterator>(i).


2062. Effect contradictions w/o no-throw guarantee of std::function swaps

Section: 20.8.11.2 [func.wrap.func], 20.8.11.2.2 [func.wrap.func.mod] Status: Open Submitter: Daniel Krügler Opened: 2011-05-28 Last modified: 2012-01-14

View all other issues in [func.wrap.func].

View all issues with Open status.

Discussion:

Howard Hinnant observed in reflector message c++std-lib-30841 that 20.8.11.2 [func.wrap.func] makes the member swap noexcept, even though the non-member swap is not noexcept.

The latter was an outcome of the discussions during the Batavia meeting and the Madrid meeting involving LWG 1349, which seems to indicate that the remaining noexcept specifier at the member swap is incorrect and should be removed.

But if we allow for a potentially throwing member swap of std::function, this causes another conflict with the exception specification for the following member function:

template<class F> function& operator=(reference_wrapper<F> f) noexcept;

Effects: function(f).swap(*this);

Note that in this example the sub-expression function(f) does not cause any problems, because of the nothrow-guarantee given in 20.8.11.2.1 [func.wrap.func.con] p. 10. The problem is located in the usage of the swap which could potentially throw given the general latitude.

So, either the Madrid meeting decision need to be revised (and both member and free swap of std::function should be noexcept), or this function needs to be adapted as well, e.g. by taking the exception-specification away or by changing the semantics.

One argument for "swap-may-throw" would be to allow for small-object optimization techniques where the copy of the target may throw. But given the fact that the swap function has been guaranteed to be "Throws: Nothing" from TR1 on, it seems to me that that there would still be opportunities to perform small-object optimizations just restricted to the set of target copies that cannot throw.

In my opinion member swap of std::function has always been intended to be no-throw, because otherwise there would be no good technical reason to specify the effects of several member functions in terms of the "construct-swap" idiom (There are three functions that are defined this way), which provides the strong exception safety in this case. I suggest to enforce that both member swap and non-member swap of std::function are nothrow functions as it had been guaranteed since TR1 on.

[ 2011 Bloomington ]

Dietmar: May not be swappable in the first place.

Alisdair: This is wide contact. Then we should be taking noexcept off instead of putting it on. This is preferred resolution.

Pablo: This is bigger issue. Specification of assignment in terms of swap is suspect to begin with. It is over specification. How this was applied to string is a better example to work from.

Pablo: Two problems: inconsistency that should be fixed (neither should have noexcept), the other issues is that assignment should not be specified in terms of swap. There are cases where assignment should succeed where swap would fail. This is easier with string as it should follow container rules.

Action Item (Alisdair): There are a few more issues found to file.

Dave: This is because of allocators? The allocator makes this not work.

Howard: There is a type erased allocator in shared_ptr. There is a noexcept allocator in shared_ptr.

Pablo: shared_ptr is a different case. There are shared semantics and the allocator does move around. A function does not have shared semantics.

Alisdair: Function objects think they have unique ownership.

Howard: In function we specify semantics with copy construction and swap.

Action Item (Pablo): Write this up better (why assignment should not be defined in terms of swap)

Howard: Not having trouble making function constructor no throw.

Dietmar: Function must allocate memory.

Howard: Does not put stuff that will throw on copy or swap in small object optimization. Put those on heap. Storing allocator, but has to be no throw copy constructable.

Pablo: Are you allowed to or required to swap or move allocators in case or swap or move.

Dave: An allocator that is type erased should be different...

Pablo: it is

Dave: Do you need to know something about allocator types? But only at construction time.

Pablo: You could have allocators that are different types.

Dave: Swap is two ended operation.

Pablo: Opinion is that both have to say propagate on swap for them to swap.

John: It is not arbitrary. If one person says no. No is no.

Howard: Find noexcept swap to be very useful. Would like to move in that direction and bring container design along.

Dave: If you have something were allocator must not propagate you can detect that at construction time.

...

Pablo: Need to leave this open and discuss in smaller group.

Alisdair: Tried to add boost::any as TR2 proposal and ran into this issue. Only the first place where we run into issues with type erased allocators. Suggest we move it to open.

Action Item: Move to open.

Action Item (Pablo works with Howard and Daniel): Address the more fundamental issue (which may be multiple issues) and write up findings.

[ Original resolution: ]

This wording is relative to the FDIS.

  1. Modify the header <functional> synopsis in 20.8 [function.objects] as indicated:

    namespace std {
      […]
    
      template<class R, class... ArgTypes>
      void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
    
      […]
    }
    
  2. Modify the class template function synopsis in 20.8.11.2 [func.wrap.func] as indicated:

    namespace std {
      […]
    
      // [func.wrap.func.alg], specialized algorithms:
      template<class R, class... ArgTypes>
      void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
    
      […]
    }
    
  3. Modify 20.8.11.2.7 [func.wrap.func.alg] as indicated:

    template<class R, class... ArgTypes>
    void swap(function<R(ArgTypes...)>& f1, function<R(ArgTypes...)>& f2) noexcept;
    

    -1- Effects: f1.swap(f2);

Proposed resolution:


2063. Contradictory requirements for string move assignment

Section: 21.4 [basic.string] Status: Open Submitter: Howard Hinnant Opened: 2011-05-29 Last modified: 2012-01-14

View other active issues in [basic.string].

View all other issues in [basic.string].

View all issues with Open status.

Discussion:

21.4.1 [string.require]/p4 says that basic_string is an "allocator-aware" container and behaves as described in 23.2.1 [container.requirements.general].

23.2.1 [container.requirements.general] describes move assignment in p7 and Table 99.

If allocator_traits<allocator_type>::propagate_on_container_move_assignment::value is false, and if the allocators stored in the lhs and rhs sides are not equal, then move assigning a string has the same semantics as copy assigning a string as far as resources are concerned (resources can not be transferred). And in this event, the lhs may have to acquire resources to gain sufficient capacity to store a copy of the rhs.

However 21.4.2 [string.cons]/p22 says:

basic_string<charT,traits,Allocator>&
operator=(basic_string<charT,traits,Allocator>&& str) noexcept;

Effects: If *this and str are not the same object, modifies *this as shown in Table 71. [Note: A valid implementation is swap(str). — end note ]

These two specifications for basic_string::operator=(basic_string&&) are in conflict with each other. It is not possible to implement a basic_string which satisfies both requirements.

Additionally assign from an rvalue basic_string is defined as:

basic_string& assign(basic_string&& str) noexcept;

Effects: The function replaces the string controlled by *this with a string of length str.size() whose elements are a copy of the string controlled by str. [ Note: A valid implementation is swap(str). — end note ]

It seems contradictory that this member can be sensitive to propagate_on_container_swap instead of propagate_on_container_move_assignment. Indeed, there is a very subtle chance for undefined behavior here: If the implementation implements this in terms of swap, and if propagate_on_container_swap is false, and if the two allocators are unequal, the behavior is undefined, and will likely lead to memory corruption. That's a lot to go wrong under a member named "assign".

[ 2011 Bloomington ]

Alisdair: Can this be conditional noexcept?

Pablo: We said we were not going to put in many conditional noexcepts. Problem is not allocator, but non-normative definition. It says swap is a valid operation which it is not.

Dave: Move assignment is not a critical method.

Alisdair: Was confusing assignment and construction.

Dave: Move construction is critical for efficiency.

Kyle: Is it possible to test for noexcept.

Alisdair: Yes, query the noexcept operator.

Alisdair: Agreed there is a problem that we cannot unconditionally mark these operations as noexcpet.

Pablo: How come swap is not defined in alloc

Alisdair: It is in utility.

Pablo: Swap has a conditional noexcept. Is no throw move constructable, is no throw move assignable.

Pablo: Not critical for strings or containers.

Kyle: Why?

Pablo: They do not use the default swap.

Dave: Important for deduction in other types.

Alisdair: Would change the policy we adopted during FDIS mode.

Pablo: Keep it simple and get some vendor experience.

Alisdair: Is this wording correct? Concerned with bullet 2.

Pablo: Where does it reference containers section.

Alisdair: String is a container.

Alisdair: We should not remove redundancy piecemeal.

Pablo: I agree. This is a deviation from rest of string. Missing forward reference to containers section.

Pablo: To fix section 2. Only the note needs to be removed. The rest needs to be a forward reference to containers.

Alisdair: That is a new issue.

Pablo: Not really. Talking about adding one sentence, saying that basic string is a container.

Dave: That is not just a forward reference, it is a semantic change.

PJ: We intended to make it look like a container, but it did not satisfy all the requirements.

Pablo: Clause 1 is correct. Clause 2 is removing note and noexcept (do not remove the rest). Clause 3 is correct.

Alisdair: Not sure data() is correct (in clause 2).

Conclusion: Move to open, Alisdair and Pablo volunteered to provide wording

[ originally proposed wording: ]

This wording is relative to the FDIS.

  1. Modify the class template basic_string synopsis in 21.4 [basic.string]:

    namespace std {
      template<class charT, class traits = char_traits<charT>,
        class Allocator = allocator<charT> >
      class basic_string {
      public:
        […]
        basic_string& operator=(basic_string&& str) noexcept;
        […]
        basic_string& assign(basic_string&& str) noexcept;
        […]
      };
    }
    
  2. Remove the definition of the basic_string move assignment operator from 21.4.2 [string.cons] entirely, including Table 71 — operator=(const basic_string<charT, traits, Allocator>&&). This is consistent with how we define move assignment for the containers in Clause 23:

    basic_string<charT,traits,Allocator>&
    operator=(basic_string<charT,traits,Allocator>&& str) noexcept;
    

    -22- Effects: If *this and str are not the same object, modifies *this as shown in Table 71. [ Note: A valid implementation is swap(str). — end note ]

    -23- If *this and str are the same object, the member has no effect.

    -24- Returns: *this

    Table 71 — operator=(const basic_string<charT, traits, Allocator>&&)
    Element Value
    data() points at the array whose first element was pointed at by str.data()
    size() previous value of str.size()
    capacity() a value at least as large as size()
  3. Modify the paragraphs prior to 21.4.6.3 [string::assign] p.3 as indicated (The first insertion recommends a separate paragraph number for the indicated paragraph):

    basic_string& assign(basic_string&& str) noexcept;
    

    -?- Effects: Equivalent to *this = std::move(str). The function replaces the string controlled by *this with a string of length str.size() whose elements are a copy of the string controlled by str. [ Note: A valid implementation is swap(str). — end note ]

    -3- Returns: *this

Proposed resolution:


2064. More noexcept issues in basic_string

Section: 21.4 [basic.string] Status: Ready Submitter: Howard Hinnant Opened: 2011-05-29 Last modified: 2012-01-14

View other active issues in [basic.string].

View all other issues in [basic.string].

View all issues with Ready status.

Discussion:

The following inconsistencies regarding noexcept for basic_string are noted.

Member swap is not marked noexcept:

void swap(basic_string& str);

But the global swap is marked noexcept:

template<class charT, class traits, class Allocator>
void swap(basic_string<charT,traits,Allocator>& lhs,
          basic_string<charT,traits,Allocator>& rhs) noexcept;

But only in the definition, not in the synopsis.

All comparison operators are marked noexcept in their definitions, but not in the synopsis.

The compare function that takes a pointer:

int compare(const charT *s) const;

is not marked noexcept. But some of the comparison functions which are marked noexcept (only in their definition) are specified to call the throwing compare operator:

template<class charT, class traits, class Allocator>
bool operator==(const basic_string<charT,traits,Allocator>& lhs,
                const charT* rhs) noexcept;

Returns: lhs.compare(rhs) == 0.

All functions with a narrow contract should not be declared as noexcept according to the guidelines presented in n3279. Among these narrow contract functions are the swap functions (23.2.1 [container.requirements.general] p. 8) and functions with non-NULL const charT* parameters.

[2011-06-08 Daniel provides wording]

[Bloomington, 2011]

Move to Ready

Proposed resolution:

This wording is relative to the FDIS. Both move-assignment operator and the moving assign function are not touched by this issue, because they are handled separately by issue 2063.

  1. Modify the header <string> synopsis in 21.3 [string.classes] as indicated (Rationale: Adding noexcept to these specific overloads is in sync with applying the same rule to specific overloads of the member functions find, compare, etc. This approach deviates from that taken in n3279, but seems more consistent given similar application for comparable member functions):

    #include <initializer_list>
    
    namespace std {
    
      […]
      template<class charT, class traits, class Allocator>
        bool operator==(const basic_string<charT,traits,Allocator>& lhs,
                        const basic_string<charT,traits,Allocator>& rhs) noexcept;
      […]
      template<class charT, class traits, class Allocator>
        bool operator!=(const basic_string<charT,traits,Allocator>& lhs,
                        const basic_string<charT,traits,Allocator>& rhs) noexcept;
      […]
    
      template<class charT, class traits, class Allocator>
        bool operator<(const basic_string<charT,traits,Allocator>& lhs,
                       const basic_string<charT,traits,Allocator>& rhs) noexcept;
      […]
      template<class charT, class traits, class Allocator>
        bool operator>(const basic_string<charT,traits,Allocator>& lhs,
                       const basic_string<charT,traits,Allocator>& rhs) noexcept;
      […]
    
      template<class charT, class traits, class Allocator>
        bool operator<=(const basic_string<charT,traits,Allocator>& lhs,
                        const basic_string<charT,traits,Allocator>& rhs) noexcept;
      […]
      template<class charT, class traits, class Allocator>
        bool operator>=(const basic_string<charT,traits,Allocator>& lhs,
                        const basic_string<charT,traits,Allocator>& rhs) noexcept;
      […]
    }
    
  2. Modify the class template basic_string synopsis in 21.4 [basic.string] as indicated (Remark 1: The noexcept at the move-constructor is fine, because even for a small-object optimization there is no problem here, because basic_string::value_type is required to be a non-array POD as of 21.1 [strings.general] p1, Remark 2: This proposal removes the noexcept at single character overloads of find, rfind, etc. because they are defined in terms of potentially allocating functions. It seems like an additional issue to me to change the semantics in terms of non-allocating functions and adding noexcept instead):

    namespace std {
      template<class charT, class traits = char_traits<charT>,
        class Allocator = allocator<charT> >
      class basic_string {
      public:
        […]
        // [string.ops], string operations:
        […]
        size_type find (charT c, size_type pos = 0) const noexcept;
        […]
        size_type rfind(charT c, size_type pos = npos) const noexcept;
        […]
        size_type find_first_of(charT c, size_type pos = 0) const noexcept;
        […]
        size_type find_last_of (charT c, size_type pos = npos) const noexcept;
        […]
        size_type find_first_not_of(charT c, size_type pos = 0) const noexcept;
        […]
        size_type find_last_not_of (charT c, size_type pos = npos) const noexcept;
        […]
      };
    }
    
  3. Modify 21.4.7.2 [string::find] before p5 and before p7 as indicated:

    size_type find(const charT* s, size_type pos = 0) const noexcept;
    […]
    size_type find(charT c, size_type pos = 0) const noexcept;
    

    -7- Returns: find(basic_string<charT,traits,Allocator>(1,c), pos).

  4. Modify 21.4.7.3 [string::rfind] before p7 as indicated:

    size_type rfind(charT c, size_type pos = npos) const noexcept;
    

    -7- Returns: rfind(basic_string<charT,traits,Allocator>(1,c),pos).

  5. Modify 21.4.7.4 [string::find.first.of] before p7 as indicated:

    size_type find_first_of(charT c, size_type pos = 0) const noexcept;
    

    -7- Returns: find_first_of(basic_string<charT,traits,Allocator>(1,c), pos).

  6. Modify 21.4.7.5 [string::find.last.of] before p7 as indicated:

    size_type find_last_of(charT c, size_type pos = npos) const noexcept;
    

    -7- Returns: find_last_of(basic_string<charT,traits,Allocator>(1,c),pos).

  7. Modify 21.4.7.6 [string::find.first.not.of] before p7 as indicated:

    size_type find_first_not_of(charT c, size_type pos = 0) const noexcept;
    

    -7- Returns: find_first_not_of(basic_string(1, c), pos).

  8. Modify 21.4.7.7 [string::find.last.not.of] before p7 as indicated:

    size_type find_last_not_of(charT c, size_type pos = npos) const noexcept;
    

    -7- Returns: find_last_not_of(basic_string(1, c), pos).

  9. Modify 21.4.8.2 [string::operator==] before p2+p3 as indicated:

    template<class charT, class traits, class Allocator>
    bool operator==(const charT* lhs,
                    const basic_string<charT,traits,Allocator>& rhs) noexcept;
    
    […]
    				
    template<class charT, class traits, class Allocator>
    bool operator==(const basic_string<charT,traits,Allocator>& lhs,
                    const charT* rhs) noexcept;
    
  10. Modify 21.4.8.3 [string::op!=] before p2+p3 as indicated:

    template<class charT, class traits, class Allocator>
    bool operator!=(const charT* lhs,
                    const basic_string<charT,traits,Allocator>& rhs) noexcept;
    
    […]
    				
    template<class charT, class traits, class Allocator>
    bool operator!=(const basic_string<charT,traits,Allocator>& lhs,
                    const charT* rhs) noexcept;
    
  11. Modify 21.4.8.4 [string::op<] before p2+p3 as indicated:

    template<class charT, class traits, class Allocator>
    bool operator<(const charT* lhs,
                   const basic_string<charT,traits,Allocator>& rhs) noexcept;
    
    […]
    				
    template<class charT, class traits, class Allocator>
    bool operator<(const basic_string<charT,traits,Allocator>& lhs,
                   const charT* rhs) noexcept;
    
  12. Modify 21.4.8.5 [string::op>] before p2+p3 as indicated:

    template<class charT, class traits, class Allocator>
    bool operator>(const charT* lhs,
                   const basic_string<charT,traits,Allocator>& rhs) noexcept;
    
    […]
    				
    template<class charT, class traits, class Allocator>
    bool operator>(const basic_string<charT,traits,Allocator>& lhs,
                   const charT* rhs) noexcept;
    
  13. Modify 21.4.8.6 [string::op<=] before p2+p3 as indicated:

    template<class charT, class traits, class Allocator>
    bool operator<=(const charT* lhs,
                    const basic_string<charT,traits,Allocator>& rhs) noexcept;
    
    […]
    				
    template<class charT, class traits, class Allocator>
    bool operator<=(const basic_string<charT,traits,Allocator>& lhs,
                    const charT* rhs) noexcept;
    
  14. Modify 21.4.8.7 [string::op>=] before p2+p3 as indicated:

    template<class charT, class traits, class Allocator>
    bool operator>=(const charT* lhs,
                    const basic_string<charT,traits,Allocator>& rhs) noexcept;
    
    […]
    				
    template<class charT, class traits, class Allocator>
    bool operator>=(const basic_string<charT,traits,Allocator>& lhs,
                    const charT* rhs) noexcept;
    
  15. Modify 21.4.8.8 [string.special] as indicated (Remark: The change of the semantics guarantees as of 17.5.1.4 [structure.specifications] p4 that the "Throws: Nothing" element of member swap is implied):

    template<class charT, class traits, class Allocator>
      void swap(basic_string<charT,traits,Allocator>& lhs,
        basic_string<charT,traits,Allocator>& rhs) noexcept;
    

    -1- Effects: Equivalent to lhs.swap(rhs);


2065. Minimal allocator interface

Section: 17.6.3.5 [allocator.requirements] Status: Ready Submitter: Jonathan Wakely Opened: 2011-06-06 Last modified: 2012-01-14

View other active issues in [allocator.requirements].

View all other issues in [allocator.requirements].

View all issues with Ready status.

Discussion:

The example in 17.6.3.5 [allocator.requirements] says SimpleAllocator satisfies the requirements of Table 28 — Allocator requirements, but it doesn't support comparison for equality/inequality.

[Bloomington, 2011]

Move to Ready

Proposed resolution:

This wording is relative to the FDIS.

  1. Modify the example in 17.6.3.5 [allocator.requirements] p5 as indicated:

    -5- […]

    [ Example: the following is an allocator class template supporting the minimal interface that satisfies the requirements of Table 28:

    template <class Tp>
    struct SimpleAllocator {
      typedef Tp value_type;
      SimpleAllocator(ctor args);
      template <class T> SimpleAllocator(const SimpleAllocator<T>& other);
      Tp *allocate(std::size_t n);
      void deallocate(Tp *p, std::size_t n);
    };
    
    template <class T, class U>
    bool operator==(const SimpleAllocator<T>&, const SimpleAllocator<U>&);
    template <class T, class U>
    bool operator!=(const SimpleAllocator<T>&, const SimpleAllocator<U>&);
    

    end example ]


2066. Missing specification of vector::resize(size_type)

Section: 23.3.6.3 [vector.capacity] Status: Tentatively Resolved Submitter: Rani Sharoni Opened: 2011-03-29 Last modified: 2012-01-14

View other active issues in [vector.capacity].

View all other issues in [vector.capacity].

Discussion:

In C++1x (N3090) there are two version of vector::resize — 23.3.6.3 [vector.capacity]:

void resize(size_type sz);
void resize(size_type sz, const T& c);

The text in 23.3.6.3 [vector.capacity]/12 only mentions "no effects on throw" for the two args version of resize:

Requires: If an exception is thrown other than by the move constructor of a non-CopyConstructible T there are no effects.

This seems like unintentional oversight since resize(size) is semantically the same as resize(size, T()). Additionally, the C++03 standard only specify single version of resize with default for the second argument - 23.2.4:

void resize(size_type sz, T c = T());

Therefore not requiring same guarantees for both version of resize is in fact a regression.

[2011-06-12: Daniel comments]

The proposed resolution for issue 2033 should solve this issue as well.

[ 2011 Bloomington ]

This issue will be resolved by issue 2033, and closed when this issue is applied.

Proposed resolution:

Apply the proposed resolution of issue 2033


2067. packaged_task should have deleted copy c'tor with const parameter

Section: 30.6.9 [futures.task] Status: Ready Submitter: Daniel Krügler Opened: 2011-06-16 Last modified: 2012-01-14

View all other issues in [futures.task].

View all issues with Ready status.

Discussion:

Class template packaged_task is a move-only type with the following form of the deleted copy operations:

packaged_task(packaged_task&) = delete;
packaged_task& operator=(packaged_task&) = delete;

Note that the argument types are non-const. This does not look like a typo to me, this form seems to exist from the very first proposing paper on N2276. Using either of form of the copy-constructor did not make much difference before the introduction of defaulted special member functions, but it makes now an observable difference. This was brought to my attention by a question on a German C++ newsgroup where the question was raised why the following code does not compile on a recent gcc:

#include <utility>
#include <future>
#include <iostream>
#include <thread>

int main(){
  std::packaged_task<void()> someTask([]{ std::cout << std::this_thread::get_id() << std::endl; });
  std::thread someThread(std::move(someTask)); // Error here
  // Remainder omitted
}

It turned out that the error was produced by the instantiation of some return type of std::bind which used a defaulted copy-constructor, which leads to a const declaration conflict with [class.copy] p8.

Some aspects of this problem are possibly core-language related, but I consider it more than a service to programmers, if the library would declare the usual form of the copy operations (i.e. those with const first parameter type) as deleted for packaged_task to prevent such problems.

A similar problem exists for class template basic_ostream in 27.7.3.1 [ostream]:

namespace std {
  template <class charT, class traits = char_traits<charT> >
  class basic_ostream : virtual public basic_ios<charT,traits> {
    […]

    // 27.7.3.3 Assign/swap
    basic_ostream& operator=(basic_ostream& rhs) = delete;
    basic_ostream& operator=(const basic_ostream&& rhs);
    void swap(basic_ostream& rhs);
};

albeit this could be considered as an editorial swap of copy and move assignment operator, I suggest to fix this as part of this issue as well.

[ 2011 Bloomington. ]

Move to Ready.

Proposed resolution:

This wording is relative to the FDIS.

  1. Modify the class template basic_ostream synopsis in 27.7.3.1 [ostream] as indicated (Note: The prototype signature of the move assignment operator in 27.7.3.3 [ostream.assign] is fine):

    namespace std {
      template <class charT, class traits = char_traits<charT> >
      class basic_ostream : virtual public basic_ios<charT,traits> {
        […]
    
        // 27.7.3.3 Assign/swap
        basic_ostream& operator=(const basic_ostream& rhs) = delete;
        basic_ostream& operator=(const basic_ostream&& rhs);
        void swap(basic_ostream& rhs);
    };
    
  2. Modify the class template packaged_task synopsis in 30.6.9 [futures.task] p2 as indicated:

    namespace std {
      template<class> class packaged_task; // undefined
    
      template<class R, class... ArgTypes>
      class packaged_task<R(ArgTypes...)> {
      public:
        […]
      
        // no copy
        packaged_task(const packaged_task&) = delete;
        packaged_task& operator=(const packaged_task&) = delete;
        
        […]
      };
      […]
    }
    

2069. Inconsistent exception spec for basic_string move constructor

Section: 21.4.2 [string.cons] Status: Ready Submitter: Bo Persson Opened: 2011-07-01 Last modified: 2012-01-14

View all issues with Ready status.

Discussion:

Sub-clause 21.4.2 [string.cons] contains these constructors in paragraphs 2 and 3:

basic_string(const basic_string<charT,traits,Allocator>& str);
basic_string(basic_string<charT,traits,Allocator>&& str) noexcept;

[…]

-3- Throws: The second form throws nothing if the allocator's move constructor throws nothing.

How can it ever throw anything if it is marked noexcept?

[2011-07-11: Daniel comments and suggests wording changes]

Further, according to paragraph 18 of the same sub-clause:

basic_string(const basic_string& str, const Allocator& alloc);
basic_string(basic_string&& str, const Allocator& alloc);

[…]

-18- Throws: The second form throws nothing if alloc == str.get_allocator() unless the copy constructor for Allocator throws.

The constraint "unless the copy constructor for Allocator throws" is redundant, because according to Table 28 — Allocator requirements, the expressions

X a1(a);
X a(b);

impose the requirement: "Shall not exit via an exception".

[ 2011 Bloomington. ]

Move to Ready.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change 21.4.2 [string.cons] p3 as indicated (This move constructor has a wide contract and is therefore safely marked as noexcept):

    basic_string(const basic_string<charT,traits,Allocator>& str);
    basic_string(basic_string<charT,traits,Allocator>&& str) noexcept;
    

    -2- Effects: Constructs an object of class basic_string as indicated in Table 64. In the second form, str is left in a valid state with an unspecified value.

    -3- Throws: The second form throws nothing if the allocator's move constructor throws nothing.

  2. Change 21.4.2 [string.cons] p18 as indicated (This move-like constructor may throw, if the allocators don't compare equal, but not because of a potentially throwing allocator copy constructor, only because the allocation attempt may fail and throw an exception):

    basic_string(const basic_string& str, const Allocator& alloc);
    basic_string(basic_string&& str, const Allocator& alloc);
    

    […]

    -18- Throws: The second form throws nothing if alloc == str.get_allocator() unless the copy constructor for Allocator throws.


2070. allocate_shared should use allocator_traits<A>::construct

Section: 20.7.2.2.6 [util.smartptr.shared.create] Status: Open Submitter: Jonathan Wakely Opened: 2011-07-11 Last modified: 2012-01-14

View all issues with Open status.

Discussion:

20.7.2.2.6 [util.smartptr.shared.create] says:

-2- Effects: Allocates memory suitable for an object of type T and constructs an object in that memory via the placement new expression ::new (pv) T(std::forward<Args>(args)...). The template allocate_shared uses a copy of a to allocate memory. If an exception is thrown, the functions have no effect.

This explicitly requires placement new rather than using allocator_traits<A>::construct(a, (T*)pv, std::forward<Args>(args)...) In most cases that would result in the same placement new expression, but would allow more control over how the object is constructed e.g. using scoped_allocator_adaptor to do uses-allocator construction, or using an allocator declared as a friend to construct objects with no public constructors.

[ 2011-08-16 Bloomington: ]

Agreed to fix in principle, but believe that make_shared and allocate_shared have now diverged enough that their descriptions should be separated. Pablo and Stefanus to provide revised wording.

Daniel's (old) proposed resolution:

This wording is relative to the FDIS.

  1. Change the following paragraphs of 20.7.2.2.6 [util.smartptr.shared.create] as indicated (The suggested removal of the last sentence of p1 is not strictly required to resolve this issue, but is still recommended, because it does not say anything new but may give the impression that it says something new):

    template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);
    template<class T, class A, class... Args>
      shared_ptr<T> allocate_shared(const A& a, Args&&... args);
    

    -1- Requires: For the template make_shared, tThe expression ::new (pv) T(std::forward<Args>(args)...), where pv has type void* and points to storage suitable to hold an object of type T, shall be well formed. For the template allocate_shared, the expression allocator_traits<A>::construct(a, pt, std::forward<Args>(args)...), where pt has type T* and points to storage suitable to hold an object of type T, shall be well formed. A shall be an allocator ([allocator.requirements]). The copy constructor and destructor of A shall not throw exceptions.

    -2- Effects: Allocates memory suitable for an object of type T and constructs an object in that memory. The template make_shared constructs the object via the placement new expression ::new (pv) T(std::forward<Args>(args)...). The template allocate_shared uses a copy of a to allocate memory and constructs the object by calling allocator_traits<A>::construct(a, pt, std::forward<Args>(args)...). If an exception is thrown, the functions have no effect.

    -3- Returns: A shared_ptr instance that stores and owns the address of the newly constructed object of type T.

    -4- Postconditions: get() != 0 && use_count() == 1

    -5- Throws: bad_alloc, or, for the template make_shared, an exception thrown from the constructor of T, or, for the template allocate_shared, an exception thrown from A::allocate or from allocator_traits<A>::constructfrom the constructor of T.

    -6- Remarks: Implementations are encouraged, but not required, to perform no more than one memory allocation. [ Note: This provides efficiency equivalent to an intrusive smart pointer. — end note ]

    -7- [ Note: These functions will typically allocate more memory than sizeof(T) to allow for internal bookkeeping structures such as the reference counts. — end note ]

[2011-12-04: Jonathan and Daniel improve wording]

See also c++std-lib-31796

Proposed resolution:

This wording is relative to the FDIS.

  1. Change the following paragraphs of 20.7.2.2.6 [util.smartptr.shared.create] as indicated:

    template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);
    template<class T, class A, class... Args>
      shared_ptr<T> allocate_shared(const A& a, Args&&... args);
    

    -1- Requires: The expression ::new (pv) T(std::forward<Args>(args)...), where pv has type void* and points to storage suitable to hold an object of type T, shall be well formed. A shall be an allocator (17.6.3.5 [allocator.requirements]). The copy constructor and destructor of A shall not throw exceptions.

    -2- Effects: Equivalent to

     
    return allocate_shared<T>(allocator<T>(), std::forward<Args>(args)...);
    

    Allocates memory suitable for an object of type T and constructs an object in that memory via the placement new expression ::new (pv) T(std::forward<Args>(args)...). The template allocate_shared uses a copy of a to allocate memory. If an exception is thrown, the functions have no effect.

    -?- Remarks: An implementation may meet the effects (and the implied guarantees) without creating the allocator object [Note: That is, user-provided specializations of std::allocator may not be instantiated, the expressions ::new (pv) T(std::forward<Args>(args)...) and pv->~T() may be evaluated directly — end note].

    -3- Returns: A shared_ptr instance that stores and owns the address of the newly constructed object of type T.

    -4- Postconditions: get() != 0 && use_count() == 1

    -5- Throws: bad_alloc, or an exception thrown from A::allocate or from the constructor of T.

    -6- Remarks: Implementations are encouraged, but not required, to perform no more than one memory allocation. [Note: This provides efficiency equivalent to an intrusive smart pointer. — end note]

    -7- [Note: These functions will typically allocate more memory than sizeof(T) to allow for internal bookkeeping structures such as the reference counts. — end note]

  2. Add the following set of new paragraphs immediately following the previous paragraph 7 of 20.7.2.2.6 [util.smartptr.shared.create]:

    template<class T, class A, class... Args>
      shared_ptr<T> allocate_shared(const A& a, Args&&... args);
    

    -?- Requires: The expressions allocator_traits<A>::construct(b, pt, std::forward<Args>(args)...) and allocator_traits<A>::destroy(b, pt) shall be well-formed and well-defined, where b has type A and is a copy of a and where pt has type T* and points to storage suitable to hold an object of type T. A shall meet the allocator requirements (17.6.3.5 [allocator.requirements]).

    -?- Effects: Uses an object a2 of type allocator_traits<A>::rebind_alloc<unspecified> that compares equal to a to allocate memory suitable for an object of type T. Uses a copy b of type A from a to construct an object of type T in that memory by calling allocator_traits<A>::construct(b, pt, std::forward<Args>(args)...). If an exception is thrown, the function has no effect.

    -?- Returns: A shared_ptr instance that stores and owns the address of the newly constructed object of type T. When ownership is given up, the effects are as follows: Uses a copy b2 of type A from a to destruct an object of type T by calling allocator_traits<A>::destroy(b2, pt2) where pt2 has type T* and refers to the newly constructed object. Then uses an object of type allocator_traits<A>::rebind_alloc<unspecified> that compares equal to a to deallocate the allocated memory.

    -?- Postconditions: get() != 0 && use_count() == 1

    -?- Throws: Nothing unless memory allocation or allocator_traits<A>::construct throws an exception.

    -?- Remarks: Implementations are encouraged, but not required, to perform no more than one memory allocation. [Note: Such an implementation provides efficiency equivalent to an intrusive smart pointer. — end note]

    -?- [Note: This function will typically allocate more memory than sizeof(T) to allow for internal bookkeeping structures such as the reference counts. — end note]


2071. std::valarray move-assignment

Section: 26.6.2.3 [valarray.assign] Status: New Submitter: Paolo Carlini Opened: 2011-05-05 Last modified: 2012-01-14

View all other issues in [valarray.assign].

View all issues with New status.

Discussion:

Yesterday I noticed that the language we have in the FDIS about std::valarray move assignment is inconsistent with the resolution of LWG 675. Indeed, we guarantee constant complexity (vs linear complexity). We also want it to be noexcept, that is more subtle, but again it's at variance with all the containers.

Also, even if we suppose that LWG 675 applies only to the containers proper, I don't think the current "as if by calling resize(v.size())" is internally consistent with the noexcept requirement.

So, what do we really want for std::valarray? Shall we maybe just strike or fix the as-if, consider it some sort of pasto from the copy-assignment text, thus keep the noexcept and constant complexity requirements (essentially the whole operation would boild down to a swap of POD data members). Or LWG 675 should be explicitly extended to std::valarray too? In that case both noexcept and constant complexity would go, I think, and the operation would boil down to the moral equivalent of clear() (which doesn't really exist in this case) + swap?

Howard: I agree the current wording is incorrect. The complexity should be linear in size() (not v.size()) because the first thing this operator needs to do is resize(0) (or clear() as you put it).

I think we can keep the noexcept.

As for proper wording, here's a first suggestion:

Effects: *this obtains the value of v. The value of v after the assignment is not specified.

Complexity: linear.

See also reflector discussion startin with c++std-lib-30690.

Proposed resolution:

This wording is relative to the FDIS.

In 26.6.2.3 [valarray.assign] update as follows:

valarray<T>& operator=(valarray<T>&& v) noexcept;

3 Effects: *this obtains the value of v. If the length of v is not equal to the length of *this, resizes *this to make the two arrays the same length, as if by calling resize(v.size()), before performing the assignment.The value of v after the assignment is not specified.

4 Complexity: ConstantLinear.


2072. Unclear wording about capacity of temporary buffers

Section: 20.6.11 [temporary.buffer] Status: New Submitter: Kazutoshi Satoda Opened: 2011-08-10 Last modified: 2012-01-14

View all other issues in [temporary.buffer].

View all issues with New status.

Discussion:

According to 20.6.11 [temporary.buffer] p1+2:

template <class T>
pair<T*, ptrdiff_t> get_temporary_buffer(ptrdiff_t n) noexcept;

-1- Effects: Obtains a pointer to storage sufficient to store up to n adjacent T objects. It is implementation-defined whether over-aligned types are supported (3.11).

-2- Returns: A pair containing the buffer's address and capacity (in the units of sizeof(T)), or a pair of 0 values if no storage can be obtained or if n <= 0.

I read this as prohibiting to return a buffer of which capacity is less than n, because such a buffer is not sufficient to store n objects.

The corresponding description in SGI STL is clear on this point, but I think it is a bit too verbose:

(for the return value, a pair P) [...] the buffer pointed to by P.first is large enough to hold P.second objects of type T. P.second is greater than or equal to 0, and less than or equal to len.

There seems to be two different targets of the "up to n" modification: The capacity of obtained buffer, and the actual number that the caller will store into the buffer.

First I read as the latter, and got surprised seeing that libstdc++ implementation can return a smaller buffer. I started searching about get_temporary_buffer(). After reading a quote from TC++PL at stackoverflow, I realized that the former is intended.

Such misinterpretation seems common:

Proposed resolution:


2073. Library exceptions that take string arguments

Section: 19.2 [std.exceptions], 19.5.6 [syserr.syserr], 27.5.3.1.1 [ios::failure] Status: New Submitter: Eelis van der Weegen Opened: 2011-08-16 Last modified: 2012-01-14

View all other issues in [std.exceptions].

View all issues with New status.

Discussion:

This is an extension issue for LWG to add constructor overloads that take a string by an rvalue reference in order to move the string into the exception.

Proposed resolution:


2074. Off by one error in std::reverse_copy

Section: 25.3.10 [alg.reverse] Status: New Submitter: Peter Miller Opened: 2011-08-17 Last modified: 2012-01-14

View all other issues in [alg.reverse].

View all issues with New status.

Discussion:

The output of the program below should be:

"three two one null \n"

But when std::reverse_copy is implemented as described in N3291 25.3.10 [alg.reverse] it's:

"null three two one \n"

because there's an off by one error in 25.3.10 [alg.reverse]/4; the definition should read:

*(result + (last - first) - 1 - i) = *(first + i)

Test program:

#include <algorithm>
#include <iostream>

template <typename BiIterator, typename OutIterator>
auto
reverse_copy_as_described_in_N3291(
  BiIterator first, BiIterator last, OutIterator result )
-> OutIterator
{
  // 25.3.10/4 [alg.reverse]:
  // "...such that for any non-negative integer i < (last - first)..."
  for ( unsigned i = 0; i < ( last - first ); ++i )
    // "...the following assignment takes place:"
    *(result + (last - first) - i) = *(first + i);

  // 25.3.10/6
  return result + (last - first);
}

int main()
{
  using std::begin;
  using std::end;
  using std::cout;

  static const char*const in[3]  { "one", "two", "three" };
  const char*             out[4] { "null", "null", "null", "null" };

  reverse_copy_as_described_in_N3291( begin( in ), end( in ), out );

  for ( auto s : out )
    cout << s << ' ';

  cout < std::endl;

  return 0;
}

Proposed resolution:

This wording is relative to the FDIS.

Change 25.3.10 [alg.reverse] p4 as follows:

template<class BidirectionalIterator, class OutputIterator>
  OutputIterator
    reverse_copy(BidirectionalIterator first,
                 BidirectionalIterator last, OutputIterator result);

-4- Effects: Copies the range [first,last) to the range [result,result+(last-first)) such that for any non-negative integer i < (last - first) the following assignment takes place: *(result + (last - first) - 1 - i) = *(first + i).

-5- Requires: The ranges [first,last) and [result,result+(last-first)) shall not overlap.

-6- Returns: result + (last - first).

-7- Complexity: Exactly last - first assignments.


2075. Progress guarantees, lock-free property, and scheduling assumptions

Section: 1.10 [intro.multithread], 29.4 [atomics.lockfree], 29.6.5 [atomics.types.operations.req] Status: New Submitter: Torvald Riegel Opened: 2011-08-18 Last modified: 2012-01-14

View all issues with New status.

Discussion:

According to 1.10 [intro.multithread] p2:

"Implementations should ensure that all unblocked threads eventually make progress."

29.4 [atomics.lockfree] p2 declares the lock-free property for a particular object. However, "lock-free" is never defined, and in discussions that I had with committee members it seemed as if the standard's lock-free would be different from what lock-free means in other communities (eg, research, text books on concurrent programming, etc.).

Following 29.6.5 [atomics.types.operations.req] p7 is_lock_free() returns "true if the object is lock-free". What is returned if the object is only sometimes lock-free?

Basically, I would like to see clarifications for the progress guarantees so that users know what they can expect from implementations (and what they cannot expect!), and to give implementors a clearer understanding of which user expectations they have to implement.

  1. Elaborate on the intentions of the progress guarantee in 1.10 [intro.multithread] p2. As I don't know about your intentions, it's hard to suggest a resolution.

  2. Define the lock-free property. The definition should probably include the following points:

  3. Add a note explaining that compare-exchange-weak is not necessarily lock-free (but is nonblocking)? Or is it indeed intended to be lock-free (only allowed to fail spuriously but guaranteed to not fail eventually)? Implementing the latter might be a challenge on LL-SC machines or lead to space overheads I suppose, see the cacheline sharing example above.

[2011-12-01: Hans comments]

1.10 [intro.multithread] p2 was an intentional compromise, and it was understood at the time that it was not a precise statement. The wording was introduced by N3209, which discusses some of the issues. There were additional reflector discussions.

This is somewhat separable from the question of what lock-free means, which is probably a more promising question to focus on.

Proposed resolution:


2076. Bad CopyConstructible requirement in set constructors

Section: 23.4.6.2 [set.cons] Status: New Submitter: Jens Maurer Opened: 2011-08-20 Last modified: 2012-01-14

View all issues with New status.

Discussion:

23.4.6.2 [set.cons] paragraph 4 says:

Requires: If the iterator’s dereference operator returns an lvalue or a non-const rvalue, then Key shall be CopyConstructible.

I'm confused why a "non-const rvalue" for the return value of the iterator would require CopyConstructible; isn't that exactly the situation when you'd want to apply the move constructor?

The corresponding requirement for multimap seems better in that regard ([multimap.cons] paragraph 3):

Requires: If the iterator’s dereference operator returns an lvalue or a const rvalue pair<key_type, mapped_type>, then both key_type and mapped_type shall be CopyConstructible.

Obviously, if I have a const rvalue, I can't apply the move constructor (which will likely attempt modify its argument).

Dave Abrahams:

I think you are right. Proposed resolution: drop "non-" from 23.4.6.2 [set.cons] paragraph 3.

Proposed resolution:

This wording is relative to the FDIS.

Change 23.4.6.2 [set.cons] p3 as follows:

template <class InputIterator>
  set(InputIterator first, InputIterator last,
    const Compare& comp = Compare(), const Allocator& = Allocator());

-3- Effects: Constructs an empty set using the specified comparison object and allocator, and inserts elements from the range [first,last).

-4- Requires: If the iterator’s dereference operator returns an lvalue or a non-const rvalue, then Key shall be CopyConstructible.

-5- Complexity: Linear in N if the range [first,last) is already sorted using comp and otherwise N logN, where N is last - first.


2077. Further incomplete constraints for type traits

Section: 20.9.4.3 [meta.unary.prop] Status: New Submitter: Daniel Krügler Opened: 2011-08-20 Last modified: 2012-01-14

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with New status.

Discussion:

The currently agreed on proposed wording for 2015 using remove_all_extents<T>::type instead of the "an array of unknown bound" terminology in the precondition should be extended to some further entries especially in Table 49, notably the is_*constructible, is_*assignable, and is_*destructible entries. To prevent ODR violations, incomplete element types of arrays must be excluded for value-initialization and destruction for example. Construction and assignment has to be honored, when we have array-to-pointer conversions or pointer conversions of incomplete pointees in effect.

Proposed resolution:


2078. Throw specification of async() incomplete

Section: 30.6.8 [futures.async] Status: New Submitter: Nicolai Josuttis Opened: 2011-08-29 Last modified: 2012-01-14

View other active issues in [futures.async].

View all other issues in [futures.async].

View all issues with New status.

Discussion:

The current throw specification of async() does state:

-6- Throws: system_error if policy is launch::async and the implementation is unable to start a new thread.

First it seems not clear whether this only applies if policy equals launch::async of if the async launch mode flag is set (if policy|launch::async!=0)

In the discussion Lawrence Crowl also wrote:

More generally, I think what we want to say is that if the implementation cannot successfully execute on one of the policies allowed, then it must choose another. The principle would apply to implementation-defined policies as well.

Peter Sommerlad:

Should not throw. That was the intent. "is async" meat exactly.

Proposed resolution:


2079. Required pow() overloads

Section: 26.8 [c.math] Status: New Submitter: Steve Clamage Opened: 2011-08-29 Last modified: 2012-01-14

View other active issues in [c.math].

View all other issues in [c.math].

View all issues with New status.

Discussion:

LWG issue 550 removed the functions:

float       pow(float, int);
double      pow(double, int);
long double pow(long double, int);

from header <cmath>. This change does not seem to be mentioned in Annex C, C.2.14.

Howard:

N3290 26.8 [c.math]/p11 says:

Moreover, there shall be additional overloads sufficient to ensure:

  1. If any argument corresponding to a double parameter has type long double, then all arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any argument corresponding to a double parameter has type double or an integer type, then all arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arguments corresponding to double parameters are effectively cast to float.

From C99 7.12.7.4 we have:

double pow(double, double);

26.8 [c.math]/p11/b2 says that if the client calls pow(2.0f, 2), then the int for second argument causes the following effective call to be made:

pow(static_cast<double>(2.0f), static_cast<double>(2)) -> double

The first sentence of p11 implies that this is done by supplying the following additional overload:

double pow(float, int);

If the client calls pow(2.0, 2), then the same reasoning (b2 again) implies the following additional overload:

double pow(double, int);

If the client calls pow(2.0l, 2), then b1 implies the following additional overload:

long double pow(long double, int);

In all, p11 implies hundreds (perhaps thousands?) of extra overloads. All but one of which is a superset of the overloads required by C++98/03 (that one being pow(float, int) which had its return type changed from float to double).

In practice, at least some vendors implement p11 by using templated overloads as opposed to ordinary overloads.

Steve Clamage:

Thanks. I didn't see that those extra overloads were actually implied by p11, despite the first sentence. Without examples, the point is a bit subtle (at least for me).

Proposed resolution:


2080. Specify when once_flag becomes invalid

Section: 30.4.4 [thread.once] Status: New Submitter: Nicolai Josuttis Opened: 2011-08-30 Last modified: 2012-01-14

View all issues with New status.

Discussion:

In function call_once 30.4.4.2 [thread.once.callonce] paragraph 4 and 5 specify for call_once():

Throws: system_error when an exception is required (30.2.2 [thread.req.exception]), or any exception thrown by func.

Error conditions:

However, nowhere in 30.4.4 [thread.once] is specified, when a once-flag becomes invalid.

As far as I know this happens if the flag is used for different functions. So we either have to have to insert a sentence/paragraph in

30.4.4.2 Function call_once [thread.once.callonce]

or

30.4.4 Call once [thread.once]

explaining when a once_flag becomes invalidated or we should state as error condition something like:

Anthony Williams:

A once_flag is invalidated if you destroy it (e.g. it is an automatic object, or heap allocated and deleted, etc.)

If the library can detect that this is the case then it will throw this exception. If it cannot detect such a case then it will never be thrown.

Jonathan Wakely:

I have also wondered how that error can happen in C++, where the type system will reject a non-callable type being passed to call_once() and should prevent a once_flag being used after its destructor runs.

If a once_flag is used after its destructor runs then it is indeed undefined behaviour, so implementations are already free to throw any exception (or set fire to a printer) without the standard saying so.

My assumption was that it's an artefact of basing the API on pthreads, which says:

The pthread_once() function may fail if:

[EINVAL] If either once_control or init_routine is invalid.

Pete Becker:

Yes, probably. We had to clean up several UNIXisms that were in the original design.

Proposed resolution:


2081. Allocator requirements should include CopyConstructible

Section: 17.6.3.5 [allocator.requirements] Status: New Submitter: Jonathan Wakely Opened: 2011-08-30 Last modified: 2012-01-14

View other active issues in [allocator.requirements].

View all other issues in [allocator.requirements].

View all issues with New status.

Discussion:

As discussed in c++std-lib-31054 and c++std-lib-31059, the Allocator requirements implicitly require CopyConstructible because a.select_on_container_copy_construction() and container.get_allocator() both return a copy by value, but the requirement is not stated explicitly anywhere.

In order to clarify that allocators cannot have 'explicit' copy constructors, the requirements should include CopyConstructible.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change Table 28 — Allocator requirements in 17.6.3.5 [allocator.requirements]:

    Table 28 — Allocator requirements
    Expression Return type Assertion/note pre-/post-condition Default
    X a1(a);
    X a1 = a;
    Shall not exit via an exception.
    post: a1 == a
    X a1(move(a));
    X a1 = move(a);
    Shall not exit via an exception.
    post: a1 equals the prior value
    of a.
  2. Change 17.6.3.5 [allocator.requirements] paragraph 4:

    An allocator type X shall satisfy the requirements of CopyConstructible (17.6.3.1 [utility.arg.requirements]). The X::pointer, X::const_pointer, X::void_pointer, and X::const_void_pointer types shall satisfy the requirements of NullablePointer (17.6.3.3 [nullablepointer.requirements]). No constructor, comparison operator, copy operation, move operation, or swap operation on these types shall exit via an exception. X::pointer and X::const_pointer shall also satisfy the requirements for a random access iterator (24.2 [iterator.requirements]).


2082. Misleading complexity requirements in <algorithm>

Section: 25 [algorithms] Status: New Submitter: Nicolai Josuttis Opened: 2011-09-02 Last modified: 2012-01-14

View all other issues in [algorithms].

View all issues with New status.

Discussion:

The partition_point() algorithm is specified with:

Complexity: O(log(last - first)) applications of pred.

While this is correct, it gives the impression that this is a logarithmic algorithm. But unless random access iterators are used it is not logarithmic because for advancing the iterator we have last-first steps, which means that the complexity becomes linear here.

Shouldn't we clarify the complexity here to something like:

Complexity: logarithmic for random-access iterators and linear otherwise (in any case O(log(last - first)) applications of pred).

Or should we even require O(log(last - first) for random-access iterators only because for other iterators just iterating over all elements, while calling pred for each element, might often be faster.

Daniel Krügler:

I agree that especially this description is a bit misleading. I'm not convinced that this is a real defect, because the whole bunch of templates within <algorithm> document the complexity solely of applications* of predicates, assignment, or swaps, but never the complexity of traversal operations (e.g. increment or iterator equality tests). This means, the standard is consistent for this function template, even though it could say a bit more.

I would like to see a wording improvement, but I would rather think that the complexity of the predicate should be mentioned first (as in other algorithms) and that a non-normative note could be added for specifically this algorithm to point out that this does not imply a logarithmic traversal complexity. The note could give more details, by explicity pointing out the linear traversal complexity for non-random-Access iterators.

Howard Hinnant:

If we are going to make such improvements, they should be made across the board in <algorithm>, not to just partition_point. For example all 4 algorithms in 25.4.3 [alg.binary.search] have the same issue, and have since C++98.

stable_partition and inplace_merge should be inspected as well.

Perhaps a new paragraph in 25.1 [algorithms.general], similar to p12 would be a better place to address this issue.

Proposed resolution:


2083. const-qualification on weak_ptr::owner_before

Section: 20.7.2.3 [util.smartptr.weak], 20.7.2.3.5 [util.smartptr.weak.obs] Status: New Submitter: Ai Azuma Opened: 2011-09-06 Last modified: 2012-01-14

View all other issues in [util.smartptr.weak].

View all issues with New status.

Discussion:

Is there any reason why weak_ptr::owner_before member function templates are not const-qualified?

Daniel Krügler:

I don't think so. To the contrary, without these to be const member function templates, the semantics of the specializations owner_less<weak_ptr<T>> and owner_less<shared_ptr<T>> described in 20.7.2.3.7 [util.smartptr.ownerless] is unclear.

It is amusing to note that this miss has remain undetected from the accepted paper n2637 on. For the suggested wording changes see below.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change the class template weak_ptr synopsis in 20.7.2.3 [util.smartptr.weak] as indicated:

    namespace std {
      template<class T> class weak_ptr {
      public:
        typedef T element_type;
        […]
        template<class U> bool owner_before(shared_ptr<U> const& b) const;
        template<class U> bool owner_before(weak_ptr<U> const& b) const;
      };
      […]
    }
    
  2. Change the prototypes in 20.7.2.3.5 [util.smartptr.weak.obs] before p6 as indicated:

    template<class U> bool owner_before(shared_ptr<U> const& b) const;
    template<class U> bool owner_before(weak_ptr<U> const& b) const;
    

2085. Wrong description of effect 1 of basic_istream::ignore

Section: 27.7.2.3 [istream.unformatted] Status: New Submitter: Krzysztof Zelechowski Opened: 2011-09-11 Last modified: 2012-01-14

View all other issues in [istream.unformatted].

View all issues with New status.

Discussion:

27.7.2.3 [istream.unformatted] in N3242 currently has the following to say about the semantics of basic_istream::ignore:

[..]. Characters are extracted until any of the following occurs:

This statement, apart from being slightly ungrammatical, indicates that if (n == numeric_limits<streamsize>::max()), the method returns without extracting any characters.

The description intends to describe the observable behaviour of an implementation in terms of logical assertions. Logical assertions are not "bullets" that can be "entered" but need not; they are predicates that can evaluate to true or false.

The description contains two predicates, either of them causes extraction to terminate. In the incriminated case, the first predicate is evaluates to true because its premise is false, therefore no characters will be extracted.

The intended semantics would be described by the following statement:

[..]. Characters are extracted until any of the following occurs:

Proposed resolution:

This wording is relative to the FDIS.

Change 27.7.2.3 [istream.unformatted] p25 as indicated:

basic_istream<charT,traits>&
  ignore(streamsize n = 1, int_type delim = traits::eof());

-25- Effects: Behaves as an unformatted input function (as described in 27.7.2.3 [istream.unformatted], paragraph 1). After constructing a sentry object, extracts characters and discards them. Characters are extracted until any of the following occurs:


2086. Overly generic type support for math functions

Section: 26.8 [c.math] Status: New Submitter: Daniel Krügler Opened: 2011-09-22 Last modified: 2012-01-14

View other active issues in [c.math].

View all other issues in [c.math].

View all issues with New status.

Discussion:

26.8 [c.math] ends with a description of a rule set for "sufficient overloads" in p11:

Moreover, there shall be additional overloads sufficient to ensure:

  1. If any argument corresponding to a double parameter has type long double, then all arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any argument corresponding to a double parameter has type double or an integer type, then all arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arguments corresponding to double parameters are effectively cast to float.

My impression is that this rule set is probably more generic as intended, my assumption is that it is written to mimic the C99/C1x rule set in 7.25 p2+3 in the "C++" way:

-2- Of the <math.h> and <complex.h> functions without an f (float) or l (long double) suffix, several have one or more parameters whose corresponding real type is double. For each such function, except modf, there is a corresponding type-generic macro. (footnote 313) The parameters whose corresponding real type is double in the function synopsis are generic parameters. Use of the macro invokes a function whose corresponding real type and type domain are determined by the arguments for the generic parameters. (footnote 314)

-3- Use of the macro invokes a function whose generic parameters have the corresponding real type determined as follows:

where footnote 314 clarifies the intent:

If the type of the argument is not compatible with the type of the parameter for the selected function, the behavior is undefined.

The combination of the usage of the unspecific term "cast" with otherwise no further constraints (note that C constraints the valid set to types that C++ describes as arithmetic types, but see below for one important difference) has the effect that it requires the following examples to be well-formed and well-defined:

#include <cmath>

enum class Ec { };

struct S { explicit operator long double(); };

void test(Ec e, S s) {
 std::sqrt(e); // OK, behaves like std::sqrt((float) e);
 std::sqrt(s); // OK, behaves like std::sqrt((float) s);
}

GCC 4.7 does not accept any of these examples.

I found another example where the C++ rule differs from the C set, but in this case I'm not so sure, which direction C++ should follow. The difference is located in the fact, that in C enumerated types are integer types as described in 6.2.5 p17 (see e.g. n1569 or n1256):

"The type char, the signed and unsigned integer types, and the enumerated types are collectively called integer types. The integer and real floating types are collectively called real types."

This indicates that in C the following code

#include <math.h>

enum E { e };

void test(void) {
  sqrt(e); // OK, behaves like sqrt((double) e);
}

seems to be well-defined and e is cast to double, but in C++ referring to

#include <cmath>

enum E { e };

void test() {
  std::sqrt(e); // OK, behaves like sqrt((float) e);
}

is also well-defined (because of our lack of constraints) but we must skip bullet 2 (because E is not an integer type) and effectively cast e to float. Accepting this, we would introduce a silent, but observable runtime difference for C and C++.

GCC 4.7 does not accept this example, but causes an ambiguity error among the three floating point overloads of sqrt.

My current suggestion to fix these problems would be to constrain the valid argument types of these functions to arithmetic types.

Howard provided wording to solve the issue.

Proposed resolution:

This wording is relative to the FDIS.

Change 26.8 [c.math] p11 as indicated:

Moreover, there shall be additional overloads sufficient to ensure:

  1. If any arithmetic argument corresponding to a double parameter has type long double, then all arithmetic arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any arithmetic argument corresponding to a double parameter has type double or an integer type, then all arithmetic arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arithmetic arguments corresponding to double parameters are effectively cast tohave type float.

2087. iostream_category() and noexcept

Section: 27.5 [iostreams.base] Status: New Submitter: Nicolai Josuttis Opened: 2011-09-22 Last modified: 2012-01-14

View all other issues in [iostreams.base].

View all issues with New status.

Discussion:

In <system_error> we have:

const error_category& generic_category() noexcept;
const error_category& system_category() noexcept;

In <future> we have:

const error_category& future_category() noexcept;

But in <ios> we have:

const error_category& iostream_category();

Is there any reason that iostream_category() is not declared with noexcept or is this an oversight?

Daniel:

This looks like an oversight to me. We made the above mentioned changes as part of noexcept-ifying the thread library but iostream_category() was skipped, so it seems to be forgotten. There should be no reason, why it cannot be noexcept. When doing so, we should also make these functions noexcept (similar to corresponding overloads):

error_code make_error_code(io_errc e);
error_condition make_error_condition(io_errc e);

Suggested wording provided by Daniel.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change 27.5.1 [iostreams.base.overview], header <ios> synopsis as indicated:

    #include <iosfwd>
    namespace std {
      […]
      error_code make_error_code(io_errc e) noexcept;
      error_condition make_error_condition(io_errc e) noexcept;
      const error_category& iostream_category() noexcept;
    }
    
  2. Change the prototype declarations in 27.5.6.5 [error.reporting] as indicated:

    error_code make_error_code(io_errc e) noexcept;
    

    -1- Returns: error_code(static_cast<int>(e), iostream_category()).

    error_condition make_error_condition(io_errc e) noexcept;
    

    -2- Returns: error_condition(static_cast<int>(e), iostream_category()).

    const error_category& iostream_category() noexcept;
    

    -3- Returns: A reference to an object of a type derived from class error_category.

    -4- The object’s default_error_condition and equivalent virtual functions shall behave as specified for the class error_category. The object’s name virtual function shall return a pointer to the string "iostream".


2088. std::terminate problem

Section: 18.8.3 [exception.terminate] Status: New Submitter: Daniel Krügler Opened: 2011-09-25 Last modified: 2012-01-14

View all issues with New status.

Discussion:

Andrzej Krzemienski reported the following on comp.std.c++:

In N3290, which is to become the official standard, in 18.8.3.4 [terminate], paragraph 1 reads

Remarks: Called by the implementation when exception handling must be abandoned for any of several reasons (15.5.1), in effect immediately after evaluating the throw-expression (18.8.3.1). May also be called directly by the program.

It is not clear what is "in effect". It was clear in previous drafts where paragraphs 1 and 2 read:

Called by the implementation when exception handling must be abandoned for any of several reasons (15.5.1). May also be called directly by the program.

Effects: Calls the terminate_handler function in effect immediately after evaluating the throw-expression (18.8.3.1), if called by the implementation, or calls the current terminate_handler function, if called by the program.

It was changed by N3189. The same applies to function unexpected (D. 11.4, paragraph 1).

Assuming the previous wording is still intended, the wording can be read "unless std::terminate is called by the program, we will use the handler that was in effect immediately after evaluating the throw-expression".

This assumes that there is some throw-expression connected to every situation that triggers the call to std::terminate. But this is not the case:

Which one is referred to?

In case std::nested_exception::rethrow_nested is called for an object that has captured no exception, there is no throw-expression involved directly (and may no throw be involved even indirectly).

Next, 18.8.3.1 [terminate.handler], paragraph 2 says

Required behavior: A terminate_handler shall terminate execution of the program without returning to the caller.

This seems to allow that the function may exit by throwing an exception (because word "return" implies a normal return).

One could argue that words "terminate execution of the program" are sufficient, but then why "without returning to the caller" would be mentioned. In case such handler throws, noexcept specification in function std::terminate is violated, and std::terminate would be called recursively - should std::abort not be called in case of recursive std::terminate call? On the other hand some controlled recursion could be useful, like in the following technique.

The here mentioned wording changes by N3189 in regard to 18.8.3.4 [terminate] p1 were done for a better separation of effects (Effects element) and additional normative wording explanations (Remarks element), there was no meaning change intended. Further, there was already a defect existing in the previous wording, which was not updated when further situations where defined, when std::terminate where supposed to be called by the implementation.

The part

"in effect immediately after evaluating the throw-expression"

should be removed and the quoted reference to 18.8.3.1 [terminate.handler] need to be part of the effects element where it refers to the current terminate_handler function, so should be moved just after

"Effects: Calls the current terminate_handler function."

It seems ok to allow a termination handler to exit via an exception, but the suggested idiom should better be replaced by a more simpler one based on evaluating the current exception pointer in the terminate handler, e.g.

void our_terminate (void) {
  std::exception_ptr p = std::current_exception();
  if (p) {
    ... // OK to rethrow and to determine it's nature
  } else {
    ... // Do something else
  }
}

[2011-12-09: Daniel comments]

A related issue is 2111.

Proposed resolution:


2089. std::allocator::construct should use uniform initialization

Section: 20.6.9.1 [allocator.members] Status: New Submitter: David Krauss Opened: 2011-10-07 Last modified: 2012-01-14

View all other issues in [allocator.members].

View all issues with New status.

Discussion:

When the EmplaceConstructible (23.2.1 [container.requirements.general]/13) requirement is used to initialize an object, direct-initialization occurs. Initializing an aggregate or using a std::initializer_list constructor with emplace requires naming the initialized type and moving a temporary. This is a result of std::allocator::construct using direct-initialization, not list-initialization (sometimes called "uniform initialization") syntax.

Altering std::allocator<T>::construct to use list-initialization would, among other things, give preference to std::initializer_list constructor overloads, breaking valid code in an unintuitive and unfixable way — there would be no way for emplace_back to access a constructor preempted by std::initializer_list without essentially reimplementing push_back.

std::vector<std::vector<int>> v;
v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization

The proposed compromise is to use SFINAE with std::is_constructible, which tests whether direct-initialization is well formed. If is_constructible is false, then an alternative std::allocator::construct overload is chosen which uses list-initialization. Since list-initialization always falls back on direct-initialization, the user will see diagnostic messages as if list-initialization (uniform-initialization) were always being used, because the direct-initialization overload cannot fail.

I can see two corner cases that expose gaps in this scheme. One occurs when arguments intended for std::initializer_list satisfy a constructor, such as trying to emplace-insert a value of {3, 4} in the above example. The workaround is to explicitly specify the std::initializer_list type, as in v.emplace_back(std::initializer_list<int>(3, 4)). Since this matches the semantics as if std::initializer_list were deduced, there seems to be no real problem here.

The other case is when arguments intended for aggregate initialization satisfy a constructor. Since aggregates cannot have user-defined constructors, this requires that the first nonstatic data member of the aggregate be implicitly convertible from the aggregate type, and that the initializer list have one element. The workaround is to supply an initializer for the second member. It remains impossible to in-place construct an aggregate with only one nonstatic data member by conversion from a type convertible to the aggregate's own type. This seems like an acceptably small hole.

The change is quite small because EmplaceConstructible is defined in terms of whatever allocator is specified, and there is no need to explicitly mention SFINAE in the normative text.

Proposed resolution:

This wording is relative to the FDIS.

Change 20.6.9.1 [allocator.members] p12 as indicated:

template <class U, class... Args>
  void construct(U* p, Args&&... args);

12 Effects: ::new((void *)p) U(std::forward<Args>(args)...) if is_constructible<U, Args...>::value is true, else ::new((void *)p) U{std::forward<Args>(args)...}


2090. Minor Overconstraint in Mutex Types

Section: 30.4.1.2 [thread.mutex.requirements.mutex] Status: New Submitter: Pete Becker Opened: 2011-10-17 Last modified: 2012-01-14

View all issues with New status.

Discussion:

30.4.1.2 [thread.mutex.requirements.mutex]/6, fourth bullet requires the return type of m.lock() to be void.

This is over-constrained. The true requirement is that the standard library ignores any value that the function returns. Yes, allowing non-void return types means that users can't store a pointer to this member function. No, that's not the least bit important.

[See also the discussion following c++std-lib-31318]

Proposed resolution:


2091. Misplaced effect in m.try_lock_for()

Section: 30.4.1.3 [thread.timedmutex.requirements] Status: New Submitter: Pete Becker Opened: 2011-10-18 Last modified: 2012-01-14

View all issues with New status.

Discussion:

30.4.1.3 [thread.timedmutex.requirements]/4 says, in part,

"Requires: If the tick period of [the argument] is not exactly convertible … [it] shall be rounded up …"

This doesn't belong in the requires clause. It's an effect. It belongs in paragraph 5. Nitpickingly, this would be a technical change: as written it imposes an obligation on the caller, while moving it imposes an obligation on the callee. Although that's certainly not what was intended.

Peter Dimov comments:

Not to mention that it should round down, not up. :-)

Incidentally, I see that the wrong try_lock requirement that the caller shall not own the mutex has entered the standard, after all. Oh well. Let's hope that the real world continues to ignore it.

Proposed resolution:


2092. Vague Wording for condition_variable_any

Section: 30.5.2 [thread.condition.condvarany] Status: New Submitter: Pete Becker Opened: 2011-10-20 Last modified: 2012-01-14

View all other issues in [thread.condition.condvarany].

View all issues with New status.

Discussion:

30.5.2 [thread.condition.condvarany]/4 says, in part, that condition_variable_any() throws an exception "if any native handle type manipulated is not available".

I don't know what that means. Is this intended to say something different from the analogous words for condition_variable() [30.5.1 [thread.condition.condvar]/4], "if some non-memory resource limitation prevents initialization"? If not, it should be worded the same way.

Proposed resolution:


2093. Throws clause of condition_variable::wait with predicate

Section: 30.5.1 [thread.condition.condvar] Status: New Submitter: Alberto Ganesh Barbati Opened: 2011-10-27 Last modified: 2012-01-14

View all other issues in [thread.condition.condvar].

View all issues with New status.

Discussion:

the Throws: clause of condition_variable::wait/wait_xxx functions that take a predicate argument is:

Throws: system_error when an exception is required (30.2.2 [thread.req.exception]).

If executing the predicate throws an exception, I would expect such exception to propagate unchanged to the caller, but the throws clause seems to indicate that it gets mutated into a system_error. T hat's because of 17.5.1.4 [structure.specifications]/4:

"If F’s semantics contains a Throws:, Postconditions:, or Complexity: element, then that supersedes any occurrences of that element in the code sequence."

Is my interpretation correct? Does it match the intent?

Daniel comments:

I don't think that this interpretation is entirely correct, the wording does not say that std::system_error or a derived class must be thrown, it simply is underspecified in this regard (The extreme interpretation is that the behaviour would be undefined, but that would be too far reaching I think). We have better wording for this in 30.4.4.2 [thread.once.callonce] p4, where it says:

"Throws: system_error when an exception is required (30.2.2 [thread.req.exception]), or any exception thrown by func."

or in 30.3.2 [thread.thread.this] p6/p9:

"Throws: Nothing if Clock satisfies the TrivialClock requirements (20.11.3 [time.clock.req]) and operations of Duration do not throw exceptions. [ Note: instantiations of time point types and clocks supplied by the implementation as specified in 20.11.7 [time.clock] do not throw exceptions. — end note ]"

So, the here discussed Throws elements should add lines along the lines of

"Any exception thrown by operations of pred."

and similar wording for time-related operations:

"Any exception thrown by operations of Duration",

"Any exception thrown by operations of chrono::duration<Rep, Period>"

[2011-11-28: Ganesh comments and suggests wording]

As for the discussion about the exception thrown by the manipulation of time-related objects, I believe the argument applies to all functions declared in 30 [thread]. Therefore, instead of adding wording to each member, I would simply move those requirements from 30.3.2 [thread.thread.this] p6/p9 to a new paragraph in 30.2.4 [thread.req.timing].

As for 30.5.2 [thread.condition.condvarany], the member functions wait() and wait_until() are described only in terms of the Effects: clause (so strictly speaking, they need no changes), however, wait_for() is described with a full set of clauses including Throws: and Error conditions:. Either we should add those clauses to wait/wait_until with changes similar to the one above, or remove paragraphs 29 to 34 entirely. By the way, even paragraph 26 could be removed IMHO.

Proposed resolution:

This wording is relative to the FDIS.

  1. Add a new paragraph at the end of 30.2.4 [thread.req.timing]:

    […]

    -6- The resolution of timing provided by an implementation depends on both operating system and hardware. The finest resolution provided by an implementation is called the native resolution.

    -7- Implementation-provided clocks that are used for these functions shall meet the TrivialClock requirements (20.11.3 [time.clock.req]).

    -?- For all functions that specify a timeout, operations on clocks, time points and time duration types may throw exceptions. [ Note: instantiations of clock, time point and duration types supplied by the implementation as specified in 20.11.7 [time.clock] do not throw exceptions. — end note]

  2. Change 30.3.2 [thread.thread.this] as indicated:

    template <class Clock, class Duration>
      void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);;
    

    -4- Effects: Blocks the calling thread for the absolute timeout (30.2.4 [thread.req.timing]) specified by abs_time.

    -5- Synchronization: None.

    -6- Throws: timeout-related exceptions (30.2.4 [thread.req.timing]).Nothing if Clock satisfies the TrivialClock requirements (20.11.3 [time.clock.req]) and operations of Duration do not throw exceptions. [ Note: instantiations of time point types and clocks supplied by the implementation as specified in 20.11.7 [time.clock] do not throw exceptions. — end note]

    template <class Rep, class Period>
      void sleep_for(const chrono::duration<Rep, Period>& rel_time);;
    

    -7- Effects: Blocks the calling thread for the relative timeout (30.2.4 [thread.req.timing]) specified by rel_time.

    -8- Synchronization: None.

    -9- Throws: timeout-related exceptions (30.2.4 [thread.req.timing]).Nothing if operations of chrono::duration<Rep, Period> do not throw exceptions. [ Note: instantiations of time point types and clocks supplied by the implementation as specified in 20.11.7 [time.clock] do not throw exceptions. — end note]

  3. Change 30.4.1.3 [thread.timedmutex.requirements] as indicated:

    -3- The expression m.try_lock_for(rel_time) shall be well-formed and have the following semantics:

    […]

    -5- Effects: The function attempts to obtain ownership of the mutex within the relative timeout (30.2.4 [thread.req.timing]) specified by rel_time. If the time specified by rel_time is less than or equal to rel_time.zero(), the function attempts to obtain ownership without blocking (as if by calling try_lock()). The function shall return within the timeout specified by rel_time only if it has obtained ownership of the mutex object. [Note: As with try_lock(), there is no guarantee that ownership will be obtained if the lock is available, but implementations are expected to make a strong effort to do so. — end note]

    […]

    -8- Synchronization: If try_lock_for() returns true, prior unlock() operations on the same object synchronize with (1.10 [intro.multithread]) this operation.

    -9- Throws: timeout-related exceptions (30.2.4 [thread.req.timing]).Nothing.

    -10- The expression m.try_lock_until(abs_time) shall be well-formed and have the following semantics:

    […]

    -12- Effects: The function attempts to obtain ownership of the mutex. If abs_time has already passed, the function attempts to obtain ownership without blocking (as if by calling try_lock()). The function shall return before the absolute timeout (30.2.4 [thread.req.timing]) specified by abs_time only if it has obtained ownership of the mutex object. [Note: As with try_lock(), there is no guarantee that ownership will be obtained if the lock is available, but implementations are expected to make a strong effort to do so. — end note]

    […]

    -15- Synchronization: If try_lock_until() returns true, prior unlock() operations on the same object synchronize with (1.10 [intro.multithread]) this operation.

    -16- Throws: timeout-related exceptions (30.2.4 [thread.req.timing]).Nothing.

  4. Change 30.5.1 [thread.condition.condvar] as indicated:

    template <class Predicate>
      void wait(unique_lock<mutex>& lock, Predicate pred);
    

    […]

    -15- Effects: Equivalent to:

    while (!pred())
      wait(lock);
    

    […]

    -17- Throws: std::system_error when an exception is required (30.2.2 [thread.req.exception]), timeout-related exceptions (30.2.4 [thread.req.timing]), or any exception thrown by pred.

    […]

    template <class Clock, class Duration>
      cv_status wait_until(unique_lock<mutex>& lock,
                           const chrono::time_point<Clock, Duration>& abs_time);
    

    […]

    -23- Throws: std::system_error when an exception is required (30.2.2 [thread.req.exception]) or timeout-related exceptions (30.2.4 [thread.req.timing]).

    […]

    template <class Rep, class Period>
      cv_status wait_for(unique_lock<mutex>& lock,
                         const chrono::duration<Rep, Period>& rel_time);
    

    […]

    -26- Effects: as ifEquivalent to:

    return wait_until(lock, chrono::steady_clock::now() + rel_time);
    

    […]

    -29- Throws: std::system_error when an exception is required (30.2.2 [thread.req.exception]) or timeout-related exceptions (30.2.4 [thread.req.timing]).

    […]

    template <class Clock, class Duration, class Predicate>
      bool wait_until(unique_lock<mutex>& lock,
                      const chrono::time_point<Clock, Duration>& abs_time,
                      Predicate pred);
    

    […]

    -32- Effects: Equivalent to:

    while (!pred())
      if (wait_until(lock, abs_time) == cv_status::timeout)
        return pred();
    return true;
    

    -33- Returns: pred()

    […]

    -36- Throws: std::system_error when an exception is required (30.2.2 [thread.req.exception]), timeout-related exceptions (30.2.4 [thread.req.timing]), or any exception thrown by pred.

    […]

    template <class Rep, class Period, class Predicate>
      bool wait_for(unique_lock<mutex>& lock,
                    const chrono::duration<Rep, Period>& rel_time,
                    Predicate pred);
    

    […]

    -39- Effects: as ifEquivalent to:

    return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));
    

    […]

    -42- Returns: pred()

    […]

    -44- Throws: std::system_error when an exception is required (30.2.2 [thread.req.exception]), timeout-related exceptions (30.2.4 [thread.req.timing]), or any exception thrown by pred.

    […]

  5. Change 30.5.2 [thread.condition.condvarany] as indicated:

    template <class Lock, class Predicate>
      void wait(Lock& lock, Predicate pred);
    

    -14- Effects: Equivalent to:

    while (!pred())
      wait(lock);
    
    template <class Lock, class Clock, class Duration>
      cv_status wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time);
    

    […]

    -18- Throws: system_error when an exception is required (30.2.2 [thread.req.exception]) or any timeout-related exceptions (30.2.4 [thread.req.timing]).

    […]

    template <class Lock, class Rep, class Period>
      cv_status wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);
    

    […]

    -20- Effects: as ifEquivalent to:

    return wait_until(lock, chrono::steady_clock::now() + rel_time);
    

    […]

    -23- Throws: system_error when an exception is required (30.2.2 [thread.req.exception]) or any timeout-related exceptions (30.2.4 [thread.req.timing]).

    […]

    template <class Lock, class Clock, class Duration, class Predicate>
      bool wait_until(Lock& lock, const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);
    

    -25- Effects: Equivalent to:

    while (!pred())
      if (wait_until(lock, abs_time) == cv_status::timeout)
        return pred();
    return true;
    

    -26- Returns: pred()[Note: There is no blocking if pred() is initially true, even if the timeout has already expired. — end note]

    -27- [Note: The returned value indicates whether the predicate evaluates to true regardless of whether the timeout was triggered. end note]

    template <class Lock, class Rep, class Period, class Predicate>
      bool wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time, Predicate pred);
    

    -28- Effects: as ifEquivalent to:

    return wait_until(lock, chrono::steady_clock::now() + rel_time, std::move(pred));
    

    -29- [Note: There is no blocking if pred() is initially true, even if the timeout has already expired. — end note] -30- Postcondition: lock is locked by the calling thread.

    -31- Returns: pred()

    -32- [Note: The returned value indicates whether the predicate evaluates to true regardless of whether the timeout was triggered. — end note]

    -33- Throws: system_error when an exception is required (30.2.2 [thread.req.exception]).

    -34- Error conditions:

    • equivalent error condition from lock.lock() or lock.unlock().

2094. duration conversion overflow shouldn't participate in overload resolution

Section: 20.11.5.1 [time.duration.cons] Status: New Submitter: Vicente J. Botet Escriba Opened: 2011-10-31 Last modified: 2012-01-14

View all other issues in [time.duration.cons].

View all issues with New status.

Discussion:

20.11.5.1 [time.duration.cons] says:

template <class Rep2, class Period2>
  constexpr duration(const duration<Rep2, Period2>& d);

Remarks: This constructor shall not participate in overload resolution unless treat_as_floating_point<rep>::value is true or both ratio_divide<Period2, period>::den is 1 and treat_as_floating_point<Rep2>::value is false.

The evaluation of ratio_divide<Period2, period>::den could make ratio_divide<Period2, period>::num overflow.

This occur for example when we try to create a millisecond (period=ratio<1,1000>) from an exa-second (Period2=ratio<1018>).

ratio_divide<ratio<1018>, ratio<1,1000>>::num is 1021 which overflows which makes the compiler error.

If the function f is overloaded with milliseconds and seconds

void f(milliseconds);
void f(seconds);

The following fails to compile.

duration<int,exa> r(1);
f(r);

While the conversion to seconds work, the conversion to milliseconds make the program fail at compile time. In my opinion, this program should be well formed and the constructor from duration<int,exa> to milliseconds shouldn't participate in overload resolution as the result can not be represented.

I think the wording of the standard can be improved so no misinterpretations are possible by adding that "no overflow is induced by the conversion".

Proposed resolution:

This wording is relative to the FDIS.

Change the following paragraphs of 20.11.5.1 [time.duration.cons] p4 indicated:

template <class Rep2, class Period2>
  constexpr duration(const duration<Rep2, Period2>& d);

Remarks: This constructor shall not participate in overload resolution unless no overflow is induced in the conversion and treat_as_floating_point<rep>::value is true or both ratio_divide<Period2, period>::den is 1 and treat_as_floating_point<Rep2>::value is false. [ Note: This requirement prevents implicit truncation error when converting between integral-based duration types. Such a construction could easily lead to confusion about the value of the duration. — end note ]


2095. promise and packaged_task missing constructors needed for uses-allocator construction

Section: 30.6.5 [futures.promise], 30.6.9 [futures.task] Status: New Submitter: Jonathan Wakely Opened: 2011-11-01 Last modified: 2012-01-14

View other active issues in [futures.promise].

View all other issues in [futures.promise].

View all issues with New status.

Discussion:

This example is ill-formed according to C++11 because uses_allocator<promise<R>, A>::value is true, but is_constructible<promise<R>, A, promise<R>&&>::value is false. Similarly for packaged_task.

#include <future>
#include <memory>
#include <tuple>

using namespace std;

typedef packaged_task<void()> task;
typedef promise<void> prom;
allocator<task> a;

tuple<task, prom> t1{ allocator_arg, a };
tuple<task, prom> t2{ allocator_arg, a, task{}, prom{} };

Proposed resolution:

This wording is relative to the FDIS.

  1. Add to 30.6.5 [futures.promise], class template promise synopsis, as indicated:

    namespace std {
      template <class R>
      class promise {
      public:
        promise();
        template <class Allocator>
        promise(allocator_arg_t, const Allocator& a);
        template <class Allocator>
        promise(allocator_arg_t, const Allocator& a, promise&& rhs) noexcept;
        promise(promise&& rhs) noexcept;
        promise(const promise& rhs) = delete;
        ~promise();	
        […]
      };
      […]
    }
    
  2. Change 30.6.5 [futures.promise] as indicated:

    promise(promise&& rhs) noexcept;
    template <class Allocator>
    promise(allocator_arg_t, const Allocator& a, promise&& rhs) noexcept;
    

    -5- Effects: constructs a new promise object and transfers ownership of the shared state of rhs (if any) to the newly-constructed object.

    -6- Postcondition: rhs has no shared state.

    -?- [Note: a is not used — end note]

  3. Add to 30.6.9 [futures.task], class template packaged_task synopsis, as indicated:

    namespace std {
      template<class> class packaged_task; // undefined
    
      template<class R, class... ArgTypes>
      class packaged_task<R(ArgTypes...)> {
      public:
        // construction and destruction
        packaged_task() noexcept;
        template <class F>
        explicit packaged_task(F&& f);
        template <class Allocator>
        explicit packaged_task(allocator_arg_t, const Allocator& a) noexcept;
        template <class Allocator>
        explicit packaged_task(allocator_arg_t, const Allocator& a, packaged_task&& rhs) noexcept;
        template<class Allocator>
        explicit packaged_task(allocator_arg_t, const Allocator& a, const packaged_task&) = delete;
        template <class F, class Allocator>
        explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f);
        ~packaged_task();
        […]
      };
      […]
    }
    
  4. Change 30.6.9.1 [futures.task.members] as indicated:

    packaged_task() noexcept;
    template <class Allocator>
      explicit packaged_task(allocator_arg_t, const Allocator& a) noexcept;
    

    -1- Effects: constructs a packaged_task object with no shared state and no stored task.

    -?- [Note: a is not used — end note]

    […]

    packaged_task(packaged_task&& rhs) noexcept;
    template <class Allocator>
      explicit packaged_task(allocator_arg_t, const Allocator& a, packaged_task&& rhs) noexcept;
    

    -5- Effects: constructs a new packaged_task object and transfers ownership of rhs’s shared state to *this, leaving rhs with no shared state. Moves the stored task from rhs to *this.

    -6- Postcondition: rhs has no shared state.

    -?- [Note: a is not used — end note]


2096. Incorrect constraints of future::get in regard to MoveAssignable

Section: 30.6.6 [futures.unique_future] Status: Tentatively Ready Submitter: Daniel Krügler Opened: 2011-11-02 Last modified: 2012-01-14

View all other issues in [futures.unique_future].

View all issues with Tentatively Ready status.

Discussion:

30.6.6 [futures.unique_future] paragraph 15 says the following:

R future::get();

-15- Returns:

There are some problems with the description:

"If the type of the value is MoveAssignable the returned value is moved, otherwise it is copied."

  1. It seems to impose unrealistic constraints on implementations, because how could an implementor recognize whether a user-defined type satisfies the semantics of MoveAssignable? This should be based solely on a pure expression-based requirement, if this is an requirement for implementations.
  2. Reducing MoveAssignable to the plain expression part std::is_move_assignable would solvs (1), but raises another question, namely why a move-assignment should be relevant for a function return based on the value stored in the future state? We would better fall back to std::is_move_constructible instead.
  3. The last criticism I have is about the part

    "the returned value is moved, otherwise it is copied"

    because an implementation won't be able to recognize what the user-defined type will do during an expression that is prepared by the implementation. I think the wording is intended to allow a move by seeding with an rvalue expression via std::move (or equivalent), else the result will be an effective copy construction.

[2011-11-28 Moved to Tentatively Ready after 5 positive votes on c++std-lib.]

Proposed resolution:

This wording is relative to the FDIS.

Change 30.6.6 [futures.unique_future] paragraph 15 as indicated:

R future::get();

-15- Returns:


2097. packaged_task constructors should be constrained

Section: 30.6.9.1 [futures.task.members] Status: New Submitter: Jonathan Wakely Opened: 2011-11-02 Last modified: 2012-01-14

View all other issues in [futures.task.members].

View all issues with New status.

Discussion:

With the proposed resolution of 2067, this no longer selects the copy constructor:

std::packaged_task<void()> p1;
std::packaged_task<void()> p2(p1);

Instead this constructor is a better match:

template <class F>
 explicit packaged_task(F&& f);

This attempts to package a packaged_task, which internally tries to copy p2, which fails because the copy constructor is deleted. For at least one implementation the resulting error message is much less helpful than the expected "cannot call deleted function" because it happens after instantiating several more templates rather than in the context where the constructor is called.

I believe the solution is to constrain to the template constructors so the template argument F cannot be deduced as (possibly cv) packaged_task& or packaged_task. It could be argued this constraint is already implied because packaged_task is not copyable and the template constructors require that "invoking a copy of f shall behave the same as invoking f".

Daniel points out that the variadic constructor of std::thread described in 30.3.1.2 [thread.thread.constr] has a similar problem and suggests a similar wording change, which has been integrated below.

An alternative is to declare thread(thread&) and packaged_task(packaged_task&) as deleted.

Proposed resolution:

This wording is relative to the FDIS.

  1. Insert a new Remarks element to 30.3.1.2 [thread.thread.constr] around p3 as indicated:

    template <class F, class ...Args> explicit thread(F&& f, Args&&... args);
    

    -3- Requires: F and each Ti in Args shall satisfy the MoveConstructible requirements. INVOKE(DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) (20.8.2) shall be a valid expression.

    -?- Remarks: This constructor shall not participate in overload resolution if decay<F>::type is the same type as std::thread.

  2. Insert a new Remarks element to 30.6.9.1 [futures.task.members] around p2 as indicated:

    template <class F>
      packaged_task(F&& f);
    template <class F, class Allocator>
      explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f);
    

    -2- Requires: INVOKE(f, t1, t2, ..., tN, R), where t1, t2, ..., tN are values of the corresponding types in ArgTypes..., shall be a valid expression. Invoking a copy of f shall behave the same as invoking f.

    -?- Remarks: These constructors shall not participate in overload resolution if decay<F>::type is the same type as std::packaged_task<R(ArgTypes...)>.


2098. Minor Inconsistency between promise::set_value and promise::set_value_at_thread_exit

Section: 30.6.5 [futures.promise] Status: New Submitter: Pete Becker Opened: 2011-11-14 Last modified: 2012-01-14

View other active issues in [futures.promise].

View all other issues in [futures.promise].

View all issues with New status.

Discussion:

30.6.5 [futures.promise]/16 says that promise::set_value(const R&) throws any exceptions thrown by R's copy constructor, and that promise_set_value(R&&) throws any exceptions thrown by R's move constructor.

30.6.5 [futures.promise]/22 is the Throws: clause for promise::set_value_at_thread_exit. It has no corresponding requirements, only that these functions throw "future_error if an error condition occurs."

Daniel suggests wording to fix this: The approach is a bit more ambitious and also attempts to fix wording glitches of 30.6.5 [futures.promise]/16, because it would be beyond acceptable efforts of implementations to determine whether a constructor call of a user-defined type will indeed call a copy constructor or move constructor (in the first case it might be a template constructor, in the second case it might also be a copy-constructor, if the type has no move constructor).

Proposed resolution:

This wording is relative to the FDIS.

  1. Change 30.6.5 [futures.promise]/16 as indicated:

    void promise::set_value(const R& r);
    void promise::set_value(R&& r);
    void promise<R&>::set_value(R& r);
    void promise<void>::set_value();
    

    […]

    -16- Throws:

    • future_error if its shared state already has a stored value or exception, or
    • for the first version, any exception thrown by the copy constructor ofconstructor selected to copy an object of R, or
    • for the second version, any exception thrown by the move constructor ofconstructor selected to move an object of R.
  2. Change 30.6.5 [futures.promise]/22 as indicated:

    void promise::set_value_at_thread_exit(const R& r);
    void promise::set_value_at_thread_exit(R&& r);
    void promise<R&>::set_value_at_thread_exit(R& r);
    void promise<void>::set_value_at_thread_exit();
    

    […]

    -16- Throws: future_error if an error condition occurs.

    • future_error if its shared state already has a stored value or exception, or
    • for the first version, any exception thrown by the constructor selected to copy an object of R, or
    • for the second version, any exception thrown by the constructor selected to move an object of R.

2099. Unnecessary constraints of va_start() usage

Section: 18.10 [support.runtime] Status: New Submitter: Daniel Krügler Opened: 2011-11-12 Last modified: 2012-01-14

View all other issues in [support.runtime].

View all issues with New status.

Discussion:

In 18.10 [support.runtime] p3 we find (emphasis mine):

The restrictions that ISO C places on the second parameter to the va_start() macro in header <stdarg.h> are different in this International Standard. The parameter parmN is the identifier of the rightmost parameter in the variable parameter list of the function definition (the one just before the ...).227 If the parameter parmN is declared with a function, array, or reference type, or with a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.

It seems astonishing that the constraints on function types and array types imposes these on the declared parameter parmN, not to the adjusted one (which would not require this extra wording, because that is implicit). This seems to say that a function definition of the form (Thanks to Johannes Schaub for this example)

#include <stdarg.h>

void f(char const paramN[], ...) {
  va_list ap;
  va_start(ap, paramN);
  va_end(ap);
}

would produce undefined behaviour when used.

Similar wording exists in C99 and in the most recent C11 draft in 7.16.1.4 p4

In my opinion the constraints in regard to array types and function types are unnecessary and should be relaxed. Are there really implementations out in the wild that would (according to my understanding incorrectly) provide the declared and not the adjusted type of paramN as deduced type to va_start()?

Proposed resolution:

This wording is relative to the FDIS.

Change 18.10 [support.runtime] p3 as indicated:

The restrictions that ISO C places on the second parameter to the va_start() macro in header <stdarg.h> are different in this International Standard. The parameter parmN is the identifier of the rightmost parameter in the variable parameter list of the function definition (the one just before the ...).227 If the parameter parmN is declared withof a function, array, or reference type, or withof a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.


2100. timed waiting functions cannot timeout if launch::async policy used

Section: 30.6.8 [futures.async] Status: New Submitter: Jonathan Wakely Opened: 2011-11-14 Last modified: 2012-01-14

View other active issues in [futures.async].

View all other issues in [futures.async].

View all issues with New status.

Discussion:

30.6.8 [futures.async] p5 says

If the implementation chooses the launch::async policy,

That should say a non-timed waiting function, otherwise, calling a timed waiting function can block indefinitely waiting for the associated thread to complete, rather than timing out after the specified time.

Since std::thread does not provide a timed_join() function (nor does Pthreads, making it impossible on many platforms) there is no way for a timed waiting function to try to join but return early due to timeout, therefore timed waiting functions either cannot guarantee to timeout or cannot be used to meet the requirement to block until the thread is joined. In order to allow timed waiting functions to timeout the requirement should only apply to non-timed waiting functions.

Proposed resolution:

This wording is relative to the FDIS.

Change 30.6.8 [futures.async] p5 as indicated:

If the implementation chooses the launch::async policy,


2101. Some transformation types can produce impossible types

Section: 20.9.7 [meta.trans] Status: New Submitter: Daniel Krügler Opened: 2011-11-18 Last modified: 2012-01-14

View all issues with New status.

Discussion:

Table 53 — "Reference modifications" says in regard to the type trait add_lvalue_reference (emphasize mine)

If T names an object or function type then the member typedef type shall name T&;

The problem with this specification is that function types with cv-qualifier or ref-qualifier, like

void() const
void() &

are also affected by the first part of the rule, but this would essentially mean, that instantiating add_lvalue_reference with such a type would attempt to form a type that is not defined in the C++ type system, namely

void(&)() const
void(&)() &

The general policy for TransformationTraits is to define always some meaningful mapping type, but this does not hold for add_lvalue_reference, add_rvalue_reference, and in addition to these two for add_pointer as well. The latter one would attempt to form the invalid types

void(*)() const
void(*)() &

A possible reason why those traits were specified in this way is that in C++03 (and that means for TR1), cv-qualifier were underspecified in the core language and several compilers just ignored them during template instantiations. This situation became fixed by adopting CWG issues 295 and 547.

While there is possibly some core language clarification needed (see reflector messages starting from c++std-core-20740), it seems also clear that the library should fix the specification. The suggested resolution follows the style of the specification of the support concepts PointeeType and ReferentType defined in N2914.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change Table 53 — "Reference modifications" in 20.9.7.2 [meta.trans.ref] as indicated:

    Table 53 — Reference modifications
    Template Comments
    template <class T>
    struct
    add_lvalue_reference;
    If T names an object type or if T names a function type that does not have
    cv-qualifiers or a ref-qualifier
    then the member typedef type
    shall name T&; otherwise, if T names a type “rvalue reference to T1” then
    the member typedef type shall name T1&; otherwise, type shall name T.
    template <class T>
    struct
    add_rvalue_reference;
    If T names an object type or if T names a function type that does not have
    cv-qualifiers or a ref-qualifier
    then the member typedef type
    shall name T&&; otherwise, type shall name T. [ Note: This rule reflects
    the semantics of reference collapsing (8.3.2 [dcl.ref]). For example, when a type T
    names a type T1&, the type add_rvalue_reference<T>::type is not an
    rvalue reference. — end note ]
  2. Change Table 56 — "Pointer modifications" in 20.9.7.5 [meta.trans.ptr] as indicated:

    Table 56 — Pointer modifications
    Template Comments
    template <class T>
    struct add_pointer;
    The member typedef type shall name the same type as
    If T names a function type that has cv-qualifiers or a ref-qualifier
    then the member typedef type shall name T; otherwise, it
    shall name the same type as
    remove_reference<T>::type*.

2102. Why is std::launch an implementation-defined type?

Section: 30.6.1 [futures.overview] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2011-11-20 Last modified: 2012-01-14

View other active issues in [futures.overview].

View all other issues in [futures.overview].

View all issues with Tentatively Ready status.

Discussion:

30.6.1 [futures.overview] says std::launch is an implementation-defined bitmask type, which would usually mean the implementation can choose whether to define an enumeration type, or a bitset, or an integer type. But in the case of std::launch it's required to be a scoped enumeration type,

enum class launch : unspecified {
  async = unspecified,
  deferred = unspecified,
  implementation-defined
};

so what is implementation-defined about it, and what is an implementation supposed to document about its choice?

[2011-12-02 Moved to Tentatively Ready after 6 positive votes on c++std-lib.]

Proposed resolution:

This wording is relative to the FDIS.

Change 30.6.1 [futures.overview] paragraph 2 as indicated:

The enum type launch is an implementation-defineda bitmask type (17.5.2.1.3 [bitmask.types]) with launch::async and launch::deferred denoting individual bits. [ Note: Implementations can provide bitmasks to specify restrictions on task interaction by functions launched by async() applicable to a corresponding subset of available launch policies. Implementations can extend the behavior of the first overload of async() by adding their extensions to the launch policy under the “as if” rule. — end note ]


2103. std::allocator_traits<std::allocator<T>>::propagate_on_container_move_assignment

Section: 20.6.9 [default.allocator] Status: New Submitter: Ai Azuma Opened: 2011-11-08 Last modified: 2012-01-14

View all other issues in [default.allocator].

View all issues with New status.

Discussion:

"std::allocator_traits<std::allocator<T>>::propagate_on_container_move_assignment::value" is specified as "false", according to (20.6.9 [default.allocator]) and (20.6.8.1 [allocator.traits.types]). However, according to (23.2.1 [container.requirements.general]), this specification leads to the unneeded requirements (MoveInsertable and MoveAssignable of the value type) on the move assignment operator of containers with the default allocator.

Proposed resolution:

Either of the following two changes;

  1. adding the nested typedef like "typedef std::true_type propagate_on_container_move_assignment;" in the definition of std::allocator class template,
  2. adding the explicit partial specialization of "std::allocator_traits" class template for "std::allocator" class template, in which "propagate_on_container_move_assignment" nested typedef is specified as "std::true_type".

Pablo prefers the first resolution.

[2011-12-02: Pablo comments]

This issue has potentially some overlap with 2108. Should the trait always_compare_equal been added, this issue's resolution should be improved based on that.

Proposed resolution:

This wording is relative to the FDIS.

Change 20.6.9 [default.allocator], the class template allocator synopsis as indicated:

namespace std {
  template <class T> class allocator;

  // specialize for void:
  template <> class allocator<void> {
  public:
    typedef void* pointer;
    typedef const void* const_pointer;
    // reference-to-void members are impossible.
    typedef void value_type;
    template <class U> struct rebind { typedef allocator<U> other; };
  };

  template <class T> class allocator {
  public:
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T value_type;
    template <class U> struct rebind { typedef allocator<U> other; };
    typedef true_type propagate_on_container_move_assignment;

    […]
  };
}

2104. unique_lock move-assignment should not be noexcept

Section: 30.4.2.2 [thread.lock.unique] Status: New Submitter: Anthony Williams Opened: 2011-11-27 Last modified: 2012-01-14

View all issues with New status.

Discussion:

I just noticed that the unique_lock move-assignment operator is declared noexcept. This function may call unlock() on the wrapped mutex, which may throw.

Suggested change: remove the noexcept specification from unique_lock::operator=(unique_lock&&) in 30.4.2.2 [thread.lock.unique] and 30.4.2.2.1 [thread.lock.unique.cons].

Daniel:

I think the situation is actually a bit more complex as it initially looks.

First, the effects of the move-assignment operator are (emphasize mine):

Effects: If owns calls pm->unlock().

Now according to the BasicLockable requirements:

m.unlock()

3 Requires: The current execution agent shall hold a lock on m.

4 Effects: Releases a lock on m held by the current execution agent.

Throws: Nothing.

This shows that unlock itself is a function with narrow contract and for this reasons no unlock function of a mutex or lock itself does have a noexcept specifier according to our mental model.

Now the move-assignment operator attempts to satisfy these requirement of the function and calls it only when it assumes that the conditions are ok, so from the view-point of the caller of the move-assignment operator it looks as if the move-assignment operator would in total a function with a wide contract.

The problem with this analysis so far is, that it depends on the assumed correctness of the state "owns".

Looking at the construction or state-changing functions, there do exist several ones that depend on caller-code satisfying the requirements and there is one guy, who looks most suspicious:

unique_lock(mutex_type& m, adopt_lock_t);

11 Requires: The calling thread own the mutex.
[…]
13 Postconditions: pm == &m and owns == true.

because this function does not even call lock() (which may, but is not required to throw an exception if the calling thread does already own the mutex). So we have in fact still a move-assignment operator that might throw an exception, if the mutex was either constructed or used (call of lock) incorrectly.

The correct fix seems to me to also add a "Throws: Nothing" element to the move-assignment operator, because using it correctly shall now throw an exception.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change 30.4.2.2 [thread.lock.unique], class template unique_lock synopsis as indicated:

    namespace std {
      template <class Mutex>
      class unique_lock {
      public:
        typedef Mutex mutex_type;
        […]
        unique_lock(unique_lock&& u) noexcept;
        unique_lock& operator=(unique_lock&& u) noexcept;
        […]
      };
    }
    
  2. Change 30.4.2.2.1 [thread.lock.unique.cons] around p22 as indicated:

    unique_lock& operator=(unique_lock&& u) noexcept;
    

    -22- Effects: If owns calls pm->unlock().

    -23- Postconditions: pm == u_p.pm and owns == u_p.owns (where u_p is the state of u just prior to this construction), u.pm == 0 and u.owns == false.

    -24- [Note: With a recursive mutex it is possible for both *this and u to own the same mutex before the assignment. In this case, *this will own the mutex after the assignment and u will not. — end note]

    -??- Throws: Nothing.


2105. Inconsistent requirements on const_iterator's value_type

Section: 23.2.1 [container.requirements.general] Status: New Submitter: Jeffrey Yasskin Opened: 2011-11-28 Last modified: 2012-01-14

View all other issues in [container.requirements.general].

View all issues with New status.

Discussion:

In the FDIS, Table 96 specifies X::const_iterator as a "constant iterator type whose value type is T". However, Table 97 specifies X::const_reverse_iterator as an "iterator type whose value type is const T" and which is defined as reverse_iterator<const_iterator>. But reverse_iterator::value_type is just "typename iterator_traits<Iterator>::value_type" 24.5.1.1 [reverse.iterator], so const_iterator and const_reverse_iterator must have the same value_type.

The resolution to issue 322 implies that const_reverse_iterator should change.

Proposed resolution:

This wording is relative to the FDIS.

Change Table 97 — "Reversible container requirements" as indicated

Table 97 — Reversible container requirements
Expression Return type Assertion/note pre-/post-condition Complexity
X::reverse_-
iterator
iterator type whose value type
is T
reverse_iterator<iterator> compile time
X::const_-
reverse_-
iterator
constant iterator type whose value type
is const T
reverse_iterator<const_iterator> compile time

2106. move_iterator wrapping iterators returning prvalues

Section: 24.5.3 [move.iterators] Status: New Submitter: Dave Abrahams Opened: 2011-11-30 Last modified: 2012-01-14

View all other issues in [move.iterators].

View all issues with New status.

Discussion:

Shouldn't move_iterator be specialized so that if the iterator it wraps returns a prvalue when dereferenced, the move_iterator also returns by value? Otherwise, it creates a dangling reference.

Howard: I believe just changing move_iterator<I>::reference would do. A direction might be testing on is_reference<iterator_traits<I>::reference>, or is_reference<decltype(*declval<I>())>.

Daniel: I would prefer to use a consistent style among the iterator adaptors, so I suggest to keep with the iterator_traits typedefs if possible.

using reference = typename conditional<
  is_reference<typename iterator_traits<Iterator>::reference>::value,
  value_type&&,
  value_type
>::type;

We might also want to ensure that if Iterator's reference type is a reference, the referent is equal to value_type (after removal of cv-qualifiers). In general we have no such guarantee.

Marc: In the default case where we don't return value_type&&, should we use value_type or should we keep the reference type of the wrapped iterator?

Daniel: This suggestion looks appealing at first, but the problem here is that using this typedef can make it impossible for move_iterator to satisfy its contract, which means returning an rvalue of the value type (Currently it says rvalue-reference, but this must be fixed as of this issue anyway). I think that user-code can reasonably expect that when it has constructed an object m of move_iterator<It>, where It is a valid mutable iterator type, the expression

It::value_type&& rv = *m;

is well-formed.

Let's set R equal to iterator_traits<Iterator>::reference in the following. We can discuss the following situations:

  1. R is a reference type: We can only return the corresponding xvalue of R, if value_type is reference-related to the referent type, else this is presumably no forward iterator and we cannot say much about it, except that it must be convertible to value_type, so it better should return a prvalue.
  2. R is not a reference type: In this case we can rely on a conversion to value_type again, but not much more. Assume we would return R directly, this might turn out to have a conversion to an lvalue-reference type of the value type (for example). If that is the case, this would indirectly violate the contract of move_iterator.

In regard to the first scenario I suggest that implementations are simply required to check that V2 = remove_cv<remove_reference<R>::type>::type is equal to the value type V1 as a criterion to return this reference as an xvalue, in all other cases it should return the value type directly as prvalue.

The additional advantage of this strategy is, that we always ensure that reference has the correct cv-qualification, if R is a real reference.

It is possible to improve this a bit by indeed supporting reference-related types, this would require to test is_same<V1, V2>::value || is_base_of<V1, V2>::value instead. I'm unsure whether (a) this additional effort is worth it and (b) a strict reading of the forward iterator requirements seems not to allow to return a reference-related type (Whether this is a defect or not is another question).

[2011-12-05: Marc Glisse comments and splits into two resolution alternatives]

I guess I am looking at the speed of:

value_type x;
x = *m;

(copy construction would likely trigger copy elision and thus be neutral) instead of the validity of:

value_type&& x = *m;

In this sense, Daniels earlier proposition that ignored value_type and just did switch_lvalue_ref_to_rvalue_ref<reference> was easier to understand (and it didn't require thinking about reference related types).

The currently proposed resolution has been split into two alternatives.

Proposed resolution:

This wording is relative to the FDIS.

This section shows two mutually exclusive resolutions — only one can be picked!

    1. Change 24.5.3 [move.iterators] p1 as indicated:

      Class template move_iterator is an iterator adaptor with the same behavior as the underlying iterator except that its dereference operator implicitly converts the value returned by the underlying iterator’s dereference operator to an rvalue referenceof the value type. Some generic algorithms can be called with move iterators to replace copying with moving.

    2. Change 24.5.3.1 [move.iterator], class template move_iterator synopsis, as indicated:

      namespace std {
        template <class Iterator>
        class move_iterator {
        public:
          typedef Iterator iterator_type;
          typedef typename iterator_traits<Iterator>::difference_type difference_type;
          typedef Iterator pointer;
          typedef typename iterator_traits<Iterator>::value_type value_type;
          typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
          typedef value_type&&see below reference;
          […]
        };
      }
      
    3. Immediately following the class template move_iterator synopsis in 24.5.3.1 [move.iterator] insert a new paragraph as indicated:

      -?- Let R be iterator_traits<Iterator>::reference and let V be iterator_traits<Iterator>::value_type. If is_reference<R>::value is true and if remove_cv<remove_reference<R>::type>::type is the same type as V, the template instantiation move_iterator<Iterator> shall define the nested type named reference as a synonym for remove_reference<R>::type&&, otherwise as a synonym for V.

    1. Change 24.5.3 [move.iterators] p1 as indicated:

      Class template move_iterator is an iterator adaptor with the same behavior as the underlying iterator except that its dereference operator implicitly converts the value returned by the underlying iterator’s dereference operator to an rvalue reference. Some generic algorithms can be called with move iterators to replace copying with moving.

    2. Change 24.5.3.1 [move.iterator], class template move_iterator synopsis, as indicated:

      namespace std {
        template <class Iterator>
        class move_iterator {
        public:
          typedef Iterator iterator_type;
          typedef typename iterator_traits<Iterator>::difference_type difference_type;
          typedef Iterator pointer;
          typedef typename iterator_traits<Iterator>::value_type value_type;
          typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
          typedef value_type&&see below reference;
          […]
        };
      }
      
    3. Immediately following the class template move_iterator synopsis in 24.5.3.1 [move.iterator] insert a new paragraph as indicated:

      -?- Let R be iterator_traits<Iterator>::reference. If is_reference<R>::value is true, the template instantiation move_iterator<Iterator> shall define the nested type named reference as a synonym for remove_reference<R>::type&&, otherwise as a synonym for R.


2107. Some iterator category should guarantee the lifetime of references

Section: 24.2 [iterator.requirements] Status: New Submitter: Jeffrey Yasskin Opened: 2011-11-21 Last modified: 2012-01-14

View other active issues in [iterator.requirements].

View all other issues in [iterator.requirements].

View all issues with New status.

Discussion:

Many iterators guarantee that references and pointers returned from their methods will outlive the iterator itself. Other useful iterators can't guarantee this, leading to the rule in 24.2 [iterator.requirements] p9 that "Destruction of an iterator may invalidate pointers and references previously obtained from that iterator."

Some algorithms can take advantage of long-lived references by returning them, while they can adapt to short-lived references by returning by value instead. However, there doesn't seem to be a way in the standard to distinguish between these two types of iterators.

The ForwardIterator requirements come close by saying "If a and b are both dereferenceable, then a == b if and only if *a and *b are bound to the same object." (24.2.5 [forward.iterators] p6) However, there are some subtle ways to satisfy this rule and still return a short-lived reference, meaning algorithms can't be guaranteed that forward_iterator_tag will imply long-lived references.

On the other hand, defect 198, which added the invalidation wording to iterator.requirements.general, refers to iterators with short-lived references being used as arguments to reverse_iterator, which requires BidirectionalIterators. If ForwardIterator required long-lived references, this would be impossible.

Either ForwardIterator should be clarified to require long-lived references, or a new category should be added that does.

See also the discussion around c++std-lib-31477.

Daniel: Related to this issue is that when applying N3066 we unintentionally lost some forward iterator requirements from C++03, where we had the post-conditions a == X(a) of X(a), and u == a of any copy operation from a to u. This wording must be restored as well.

Proposed resolution:


2108. No way to identify allocator types that always compare equal

Section: 17.6.3.5 [allocator.requirements] Status: New Submitter: Jonathan Wakely Opened: 2011-12-01 Last modified: 2012-01-14

View other active issues in [allocator.requirements].

View all other issues in [allocator.requirements].

View all issues with New status.

Discussion:

Whether two allocator objects compare equal affects the complexity of container copy and move assignments and also the possibility of an exception being thrown by container move assignments. The latter point means container move assignment cannot be noexcept when propagate_on_container_move_assignment (POCMA) is false for the allocator because there is no way to detect at compile-time if two allocators will compare equal. LWG 2013 means this affects all containers using std::allocator, but even if that is resolved, this affects all stateless allocators which do not explicitly define POCMA to true_type.

One solution would be to add an "always_compare_equal" trait to allocator_traits, but that would be duplicating information that is already defined by the type's equality operator if that operator always returns true. Requiring users to write operator== that simply returns true and also explicitly override a trait to repeat the same information would be unfortunate and risk user errors that allow the trait and actual operator== to disagree.

Dave Abrahams suggested a better solution in message c++std-lib-31532, namely to allow operator== to return true_type, which is convertible to bool but also detectable at compile-time. Adopting this as the recommended way to identify allocator types that always compare equal only requires a slight relaxation of the allocator requirements so that operator== is not required to return bool exactly.

The allocator requirements do not make it clear that it is well-defined to compare non-const values, that should be corrected too.

In message c++std-lib-31615 Pablo Halpern suggested an always_compare_equal trait that could still be defined, but with a sensible default value rather than requiring users to override it, and using that to set sensible values for other allocator traits:

Do we still need always_compare_equal if we can have an operator== that returns true_type? What would its default value be? is_empty<A> || is_convertible<decltype(a == a), true_type>::value, perhaps? One benefit I see to such a definition is that stateless C++03 allocators that don't use the true_type idiom will still benefit from the new trait.

[…]

One point that I want to ensure doesn't get lost is that if we adopt some sort of always_compare_equal-like trait, then propagate_on_container_swap and propagate_on_container_move_assignment should default to always_compare_equal. Doing this will eliminate unnecessary requirements on the container element type, as per [LWG 2103].

Optionally, operator== for std::allocator could be made to return true_type, however if LWG 2103 is adopted that is less important.

Alberto Ganesh Barbati: Suggest either always_compare_equal, all_objects_(are_)equivalent, or all_objects_compare_equal.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change Table 27 — "Descriptive variable definitions" in 17.6.3.5 [allocator.requirements]:

    Table 27 — Descriptive variable definitions
    Variable Definition
    a3, a4 an rvalue ofvalues of (possibly const) type X
    b a value of (possibly const) type Y
  2. Change Table 28 — "Allocator requirements" in 17.6.3.5 [allocator.requirements]:

    Table 28 — Allocator requirements
    Expression Return type Assertion/note pre-/post-condition Default
    a1 == a2a3 == a4 convertible to bool returns true only if storage
    allocated from each can be
    deallocated via the other.
    operator== shall be reflexive,
    symmetric, and transitive, and
    shall not exit via an exception.
    a1 != a2a3 != a4 convertible to bool same as !(a1 == a2)!(a3 == a4)
    a3 == b convertible to bool same as a3 ==
    Y::rebind<T>::other(b)
    a3 != b convertible to bool same as !(a3 == b)
    […]
    a.select_on_-
    container_copy_-
    construction()
    X Typically returns either a or
    X()
    return a;
    X::always_compares_equal Identical to or derived
    from true_type or
    false_type
    true_type if the expression x1 == x2 is
    guaranteed to be true for any two (possibly
    const) values x1, x2 of type X, when
    implicitly converted to bool. See Note B, below.
    true_type, if is_empty<X>::value is true or if
    decltype(declval<const X&>() == declval<const X&>())
    is convertible to true_type, otherwise false_type.
    […]

    Note A: […]

    Note B: If X::always_compares_equal::value or XX::always_compares_equal::value evaluate to true and an expression equivalent to x1 == x2 or x1 != x2 for any two values x1, x2 of type X evaluates to false or true, respectively, the behaviour is undefined.

  3. Change class template allocator_traits synopsis, 20.6.8 [allocator.traits] as indicated:

    namespace std {
      template <class Alloc> struct allocator_traits {
        typedef Alloc allocator_type;
        […]
        typedef see below always_compares_equal;
        typedef see below propagate_on_container_copy_assignment;
        […]
      };
    }
    
  4. Insert the following between 20.6.8.1 [allocator.traits.types] p6 and p7 as indicated:

    typedef see below always_compares_equal;
    

    -?- Type: Alloc::always_compares_equal if such a type exists; otherwise, true_type if is_empty<Alloc>::value is true or if decltype(declval<const Alloc&>() == declval<const Alloc&>()) is convertible to true_type; otherwise, false_type .

    typedef see below propagate_on_container_copy_assignment;
    

    -7- Type: Alloc::propagate_on_container_copy_assignment if such a type exits, otherwise false_type.

  5. Change class template allocator synopsis, 20.6.9 [default.allocator] as indicated:

    namespace std {
      template <class T> class allocator;
    
      // specialize for void:
      template <> class allocator<void> {
      public:
        typedef void* pointer;
        typedef const void* const_pointer;
        // reference-to-void members are impossible.
        typedef void value_type;
        template <class U> struct rebind { typedef allocator<U> other; };
      };
    
      template <class T> class allocator {
      public:
        typedef size_t size_type;
        typedef ptrdiff_t difference_type;
        typedef T* pointer;
        typedef const T* const_pointer;
        typedef T& reference;
        typedef const T& const_reference;
        typedef T value_type;
        template <class U> struct rebind { typedef allocator<U> other; };
        typedef true_type always_compares_equal;
    
        […]
      };
    }
    

2109. Incorrect requirements for hash specializations

Section: 19.5.5 [syserr.hash], 20.7.2.6 [util.smartptr.hash], 20.8.12 [unord.hash], 20.13.1 [type.index.synopsis], 21.6 [basic.string.hash], 23.3.7 [vector.bool], 30.3.1.1 [thread.thread.id] Status: New Submitter: Daniel Krügler Opened: 2011-12-04 Last modified: 2012-01-14

View all issues with New status.

Discussion:

20.7.2.6 [util.smartptr.hash] p2 is specified as follows:

Requires: the template specializations shall meet the requirements of class template hash (20.8.12).

The problem here is the usage of a Requires element, which is actually a pre-condition that a user of a component has to satisfy. But the intent of this wording is actually to be a requirement on implementations. The Requires element should be removed here and the wording should be improved to say what it was intended for.

We have similar situations in basically all other places where the specification of library-provided hash specializations is defined. Usually, the Requires element is incorrect. In the special case of hash<unique_ptr<T, D>> the implementation depends on the behaviour of hash specializations, that could be user-provided. In this case the specification needs to separate the requirements on these specializations and those that are imposed on the implementation.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change 19.5.5 [syserr.hash] as indicated:

    template <> struct hash<error_code>;
    

    -1- Requires: the template specialization shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <system_error> provides a definition for a specialization of the template hash<error_code>. The requirements for the members of this specialization are given in sub-clause 20.8.12 [unord.hash].

  2. Change 20.5.3 [bitset.hash] as indicated:

    template <size_t N> struct hash<bitset<N> >;
    

    -1- Requires: the template specialization shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <bitset> provides a definition for a partial specialization of the hash class template for specializations of class template bitset<N>. The requirements for the members of instantiations of this specialization are given in sub-clause 20.8.12 [unord.hash].

  3. Change 20.7.2.6 [util.smartptr.hash] as indicated:

    template <class T, class D> struct hash<unique_ptr<T, D> >;
    

    -1- Requires: the template specialization shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <memory> provides a definition for a partial specialization of the hash class template for specializations of class template unique_ptr<T, D>. The requirements for the members of instantiations of this specialization are given in sub-clause 20.8.12 [unord.hash]. For an object p of type UP, where UP is unique_ptr<T, D>, hash<UP>()(p) shall evaluate to the same value as hash<typename UP::pointer>()(p.get()). The specialization hash<typename UP::pointer> shall be well-formed.

    -?- Requires: The specialization hash<typename UP::pointer> shall be well-formed and well-defined [Note: the general requirements of class template hash (20.8.12 [unord.hash]) are implied — end note].

    template <class T> struct hash<shared_ptr<T> >;
    

    -2- Requires: the template specialization shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <memory> provides a definition for a partial specialization of the hash class template for specializations of class template shared_ptr<T>. The requirements for the members of instantiations of this specialization are given in sub-clause 20.8.12 [unord.hash]. For an object p of type shared_ptr<T>, hash<shared_ptr<T> >()(p) shall evaluate to the same value as hash<T*>()(p.get()).

  4. Change 20.8.12 [unord.hash] p2 as indicated: [Comment: For unknown reasons the extended integer types are not mentioned here, which looks like an oversight to me and makes also the wording more complicated. See 2119 for this part of the problem. — end comment]

    template <> struct hash<bool>;
    template <> struct hash<char>;
    […]
    template <> struct hash<long double>;
    template <class T> struct hash<T*>;
    

    -2- Requires: the template specializations shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <functional> provides definitions for specializations of the hash class template for each cv-unqualified arithmetic type except for the extended integer types. This header also provides a definition for a partial specialization of the hash class template for any pointer type. The requirements for the members of these specializations are given in sub-clause 20.8.12 [unord.hash].

  5. Change 20.13.4 [type.index.hash] p1 as indicated:

    template <> struct hash<type_index>;
    

    -1- Requires: the template specialization shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <typeindex> provides a definition for a specialization of the template hash<type_index>. The requirements for the members of this specialization are given in sub-clause 20.8.12 [unord.hash]. For an object index of type type_index, hash<type_index>()(index) shall evaluate to the same result as index.hash_code().

  6. Change 21.6 [basic.string.hash] p1 as indicated:

    template <> struct hash<string>;
    template <> struct hash<u16string>;
    template <> struct hash<u32string>;
    template <> struct hash<wstring>;
    

    -1- Requires: the template specializations shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <string> provides definitions for specializations of the hash class template for the types string, u16string, u32string, and wstring. The requirements for the members of these specializations are given in sub-clause 20.8.12 [unord.hash].

  7. Change 23.3.7 [vector.bool] p7 as indicated:

    template <class Allocator> struct hash<vector<bool, Allocator> >;
    

    -7- Requires: the template specialization shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <vector> provides a definition for a partial specialization of the hash class template for specializations of class template vector<bool, Allocator>. The requirements for the members of instantiations of this specialization are given in sub-clause 20.8.12 [unord.hash].

  8. Change 30.3.1.1 [thread.thread.id] p14 as indicated:

    template <> struct hash<thread::id>;
    

    -14- Requires: the template specialization shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <thread> provides a definition for a specialization of the template hash<thread::id>. The requirements for the members of this specialization are given in sub-clause 20.8.12 [unord.hash].


2110. remove can't swap but note says it might

Section: 25.3.8 [alg.remove] Status: New Submitter: Howard Hinnant Opened: 2011-12-07 Last modified: 2012-01-14

View all other issues in [alg.remove].

View all issues with New status.

Discussion:

25.3.8 [alg.remove]/p1 says:

1 Requires: The type of *first shall satisfy the MoveAssignable requirements (Table 22).

This means that remove/remove_if can only use move assignment to permute the sequence. But then 25.3.8 [alg.remove]/p6 (non-normatively) contradicts p1:

6 Note: each element in the range [ret,last), where ret is the returned value, has a valid but unspecified state, because the algorithms can eliminate elements by swapping with or moving from elements that were originally in that range.

Proposed resolution:

This wording is relative to the FDIS.

Change 25.3.8 [alg.remove] as indicated:

template<class ForwardIterator, class T>
  ForwardIterator remove(ForwardIterator first, ForwardIterator last,
                         const T& value);

template<class ForwardIterator, class Predicate>
  ForwardIterator remove_if(ForwardIterator first, ForwardIterator last,
                            Predicate pred);

[…]

-6-Note: each element in the range [ret,last), where ret is the returned value, has a valid but unspecified state, because the algorithms can eliminate elements by swapping with or moving from elements that were originally in that range.


2111. Which unexpected/terminate handler is called from the exception handling runtime?

Section: 18.8.3.4 [terminate], D.13.3 [unexpected] Status: New Submitter: Howard Hinnant Opened: 2011-12-06 Last modified: 2012-01-14

View all other issues in [terminate].

View all issues with New status.

Discussion:

Prior to N3242, modified by N3189, we said this about unexpected():

Effects: Calls the unexpected_handler function in effect immediately after evaluating the throw-expression (D.13.1), if called by the implementation, or calls the current unexpected_handler, if called by the program.

and this about terminate():

Effects: Calls the terminate_handler function in effect immediately after evaluating the throw-expression (18.8.3.1), if called by the implementation, or calls the current terminate_handler function, if called by the program.

But now in both places we say:

Calls the current unexpected_handler function.

and:

Calls the current terminate function.

The difference is that in C++98/03 if a destructor reset a handler during stack unwinding, that new handler was not called if the unwinding later led to unexpected() or terminate() being called. But these new words say that this new handler is called. This is an ABI-breaking change in the way exceptions are handled. Was this change intentional?

N3189 was mainly about introducing exception safety and getters for the handlers. I don't recall the issue of which handler gets called being part of the discussion.

I propose that we revert to the C++98/03 behavior in this regard, lest ABI's such as the Itanium ABI are invalidated. A mechanical way to do this is to revert bullets 9 and 12 of N3189.

[2011-12-09: Daniel comments]

There was no such semantic change intended. It was an unfortunate side effect when trying to better separate different responsibilities in the previous wording.

A related issue is 2088.

Proposed resolution:


2112. User-defined classes that cannot be derived from

Section: 17.6.5 [conforming], 20.6.8 [allocator.traits], 20.12.1 [allocator.adaptor.syn] Status: New Submitter: Daniel Krügler Opened: 2011-11-30 Last modified: 2012-01-14

View other active issues in [conforming].

View all other issues in [conforming].

View all issues with New status.

Discussion:

It is a very established technique for implementations to derive internally from user-defined class types that are used to customize some library component, e.g. deleters and allocators are typical candidates. The advantage of this approach is to possibly take advantage of the empty-base-class optimization (EBCO).

Whether or whether not libraries did take advantage of such a detail didn't much matter in C++03. Even though there did exist a portable idiom to prevent that a class type could be derived from, this idiom has never reached great popularity: The technique required to introduce a virtual base class and it did not really prevent the derivation, but only any construction of such a type. Further, such types are not empty as defined by the std::is_empty trait, so could easily be detected by implementations from TR1 on.

With the new C++11 feature of final classes and final member functions it is now very easy to define an empty, but not derivable from class type. From the point of the user it is quite natural to use this feature for types that he or she did not foresee to be derivable from.

On the other hand, most library implementations (including third-party libraries) often take advantage of EBCO applied to user-defined types used to instantiate library templates internally. As the time of submitting this issue the following program failed to compile on all tested library implementations:

#include <memory>

struct Noop final {
 template<class Ptr>
 void operator()(Ptr) const {}
};

std::unique_ptr<int, Noop> up;

In addition, many std::tuple implementations with empty, final classes as element types failed as well, due to a popular inheritance-based implementation technique. EBCO has also a long tradition to be used in library containers to efficiently store potentially stateless, empty allocators.

It seems that both user and library did the best they could: None of the affected types did impose explicit requirements on the corresponding user-defined types to be derivable from (This capability was not part of the required operations), and libraries did apply EBCO whereever possible to the convenience of the customer.

Nonetheless given the existence of non-derivable-from class types in C++11, libraries have to cope with failing derivations. How should that problem be solved?

It would certainly be possible to add weazel wording to the allocator requirements similar to what we had in C++03, but restricted to derivation-from requirements. I consider this as the bad solution, because it would add new requirements that never had existed before in this explicit form onto types like allocators.

Existing libraries presumably will need internal traits like __is_final or __is_derivable to make EBCO possible in the current form but excluding non-derivable class types. As of this writing this seems to happen already. Problem is that without a std::is_derivable trait, third-party libraries have no portable means to do the same thing as standard library implementations. This should be a good reason to make such a trait public available soon, but seems not essential to have now. Further, this issue should also be considered as a chance to recognice that EBCO has always been a very special corner case (There exist parallels to the previously existing odd core language rule that did make the interplay between std::auto_ptr and std::auto_ptr_ref possible) and that it would be better to provide explicit means for space-efficient storage, not necessarily restricted to inheritance relations, e.g. by marking data members with a special attribute.

At least two descriptions in the current standard should be fixed now for better clarification:

  1. As mentioned by Ganesh, 20.6.8 [allocator.traits] p1 currently contains a (non-normative) note "Thus, it is always possible to create a derived class from an allocator." which should be removed.

  2. As pointed out by Howard, the specification of scoped_allocator_adaptor as of 20.12.1 [allocator.adaptor.syn] already requires derivation from OuterAlloc, but only implies indirectly the same for the inner allocators due to the exposition-only description of member inner. This indirect implication should be normatively required for all participating allocators.

Proposed resolution:


2113. Do library implementers have the freedom to add final to non-polymorphic components?

Section: 17.6.5 [conforming] Status: New Submitter: Daniel Krügler Opened: 2011-11-30 Last modified: 2012-01-14

View other active issues in [conforming].

View all other issues in [conforming].

View all issues with New status.

Discussion:

Related to LWG 2112 the question has been raised whether a library implementation may declare non-polymorphic library components, such as class template std::vector or std::basic_string, as final class types.

This issue is not suggesting to enforce that libraries are required to do that, it is asking whether libraries should have the freedom to do so.

The existing wording in 17.6.5.11 [derivation] does not give a clear permission to do so. In my opinion this position should be clarified in either direction.

Giving implementations this freedom would have both advantages and disadvantages. Several opponents where worried about breakage of code of existing user implementations. On the other hand such types where not designed to be used as base classes. Allowing implementations to mark these components as final could allow them to provide compile-modes that are intentionally restrictive to the advantage of user code that want to be alterted about that. Any implementation that would be concerned about user complaints would not take advantage of this feature anyway.

If agreement exists that such implementation freedom would be useful, wording like

An implementation may declare additional non-virtual member function signatures within a class as final.

or

An implementation may declare additional class without virtual member function signatures as final.

should be added to 17.6.5 [conforming] with corresponding exceptions of these rules (e.g. iterator, unary_function, or pair).

If such freedom should not exist, it seems better to clarify this as well, e.g. by adding around 17.6.5.11 [derivation]:

An implementation shall not declare any class or any member function signature as final.

Proposed resolution:


2114. Incorrect "contextually convertible to bool" requirements

Section: 17.6.3.3 [nullablepointer.requirements], 24.2.3 [input.iterators], 24.2.7 [random.access.iterators], 25.1 [algorithms.general], 25.4 [alg.sorting], 30.2.1 [thread.req.paramname] Status: New Submitter: Daniel Krügler Opened: 2011-12-09 Last modified: 2012-01-14

View all issues with New status.

Discussion:

As of 17.6.3.1 [utility.arg.requirements] Table 17/18, the return types of the expressions

a == b

or

a < b

for types satisfying the EqualityComparable or LessThanComparable types, respectively, are required to be "convertible to bool" which corresponds to a copy-initialization context. But several newer parts of the library that refer to such contexts have lowered the requirements taking advantage of the new terminology of "contextually convertible to bool" instead, which corresponds to a direct-initialization context (In addition to "normal" direct-initialization constructions, operands of logical operations as well as if or switch conditions also belong to this special context).

One example for these new requirements are input iterators which satisfy EqualityComparable but also specify that the expression

a != b

shall be just "contextually convertible to bool". The same discrepancy exists for requirement set NullablePointer in regard to several equality-related expressions.

For random access iterators we have

a < b contextually convertible to bool

as well as for all derived comparison functions, so strictly speaking we could have a random access iterator that does not satisfy the LessThanComparable requirements, which looks like an artifact to me.

Even if we keep with the existing requirements based on LessThanComparable or EqualityComparable we still would have the problem that some current specifications are actually based on the assumption of implicit convertibility instead of "explicit convertibility", e.g. 20.7.1.4 [unique.ptr.special] p3:

template <class T1, class D1, class T2, class D2>
bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);

-3- Returns: x.get() != y.get().

Similar examples exist in 20.7.1.2.2 [unique.ptr.single.dtor] p2, 20.7.1.2.3 [unique.ptr.single.asgn] p9, 20.7.1.2.4 [unique.ptr.single.observers] p1+3+8, etc.

In all these places the expressions involving comparison functions (but not those of the conversion of a NullablePointer to bool!) assume to be "convertible to bool". I think this is a very natural assumption and all delegations of the comparison functions of some type X to some other API type Y in third-party code does so assuming that copy-initialization semantics will just work.

The actual reason for using the newer terminology can be rooted back to LWG 556. My hypotheses is that the resolution of that issue also needs a slight correction. Why so?

The reason for opening that issue were worries based on the previous "convertible to bool" wording. An expressions like "!pred(a, b)" might not be well-formed in those situations, because operator! might not be accessible or might have an unusual semantics (and similarly for other logical operations). This can indeed happen with unusual proxy return types, so the idea was that the evaluation of Predicate, BinaryPredicate (25.1 [algorithms.general] p8+9), and Compare (25.4 [alg.sorting] p2) should be defined based on contextual conversion to bool. Unfortunately this alone is not sufficient: In addition, I think, we also want the predicates to be (implicitly) convertible to bool! Without this wording, several conditions are plain wrong, e.g. 25.2.5 [alg.find] p2, which talks about "pred(*i) != false" (find_if) and "pred(*i) == false" (find_if_not). These expressions are not within a boolean context!

While we could simply fix all these places by proper wording to be considered in a "contextual conversion to bool", I think that this is not the correct solution: Many third-party libraries already refer to the previous C++03 Predicate definition — it actually predates C++98 and is as old as the SGI specification. It seems to be a high price to pay to switch to direct initialization here instead of fixing a completely different specification problem.

A final observation is that we have another definition for a Predicate in 30.2.1 [thread.req.paramname] p2:

If a parameter is Predicate, operator() applied to the actual template argument shall return a value that is convertible to bool.

The problem here is not that we have two different definitions of Predicate in the standard — this is confusing, but this fact alone is not a defect. The first (minor) problem is that this definition does not properly apply to function objects that are function pointers, because operator() is not defined in a strict sense. But the actually worse second problem is that this wording has the very same problem that has originally lead to LWG 556! We only need to look at 30.5.1 [thread.condition.condvar] p15 to recognice this:

while (!pred())
  wait(lock);

The negation expression here looks very familiar to the example provided in LWG 556 and is sensitive to the same "unusual proxy" problem. Changing the 30.2.1 [thread.req.paramname] wording to a corresponding "contextual conversion to bool" wouldn't work either, because existing specifications rely on "convertible to bool", e.g. 30.5.1 [thread.condition.condvar] p32+33+42 or 30.5.2 [thread.condition.condvarany] p25+26+32+33.

To summarize: I believe that LWG 556 was not completely resolved. A pessimistic interpretation is, that even with the current wording based on "contextually convertible to bool" the actual problem of that issue has not been fixed. What actually needs to be required here is some normative wording that basically expresses something along the lines of:

The semantics of any contextual conversion to bool shall be equivalent to the semantics of any implicit conversion to bool.

This is still not complete without having concepts, but it seems to be a better approximation. Another way of solving this issue would be to define a minimum requirements table with equivalent semantics. The proposed wording is a bit simpler but attempts to express the same thing.

Proposed resolution:

This wording is relative to the FDIS.

  1. Change Table 25 — "NullablePointer requirements" in 17.6.3.3 [nullablepointer.requirements] as indicated:

    Table 25 — NullablePointer requirements
    Expression Return type Operational semantics
    […]
    a != b contextually convertible to bool !(a == b)
    a == np
    np == a
    contextually convertible to bool a == P()
    a != np
    np != a
    contextually convertible to bool !(a == np)
  2. Change Table 107 — "Input iterator requirements" in 24.2.3 [input.iterators] as indicated:

    Table 107 — Input iterator requirements (in addition to Iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    a != b contextually convertible to bool !(a == b) pre: (a, b) is in the domain of ==.
    […]
  3. Change Table 111 — "Random access iterator requirements" in 24.2.7 [random.access.iterators] as indicated:

    Table 111 — Random access iterator requirements (in addition to bidirectional iterator)
    Expression Return type Operational semantics Assertion/note
    pre-/post-condition
    […]
    a < b contextually convertible to bool b - a > 0 < is a total ordering relation
    a > b contextually convertible to bool b < a > is a total ordering relation opposite to <.
    a >= b contextually convertible to bool !(a < b)
    a <= b contextually convertible to bool !(a > b)
  4. Change 25.1 [algorithms.general] p8+9 as indicated:

    -8- The Predicate parameter is used whenever an algorithm expects a function object (20.8 [function.objects]) that, when applied to the result of dereferencing the corresponding iterator, returns a value testable as true. In other words, if an algorithm takes Predicate pred as its argument and first as its iterator argument, it should work correctly in the construct pred(*first) implicitly or contextually converted to bool (Clause 4 [conv]). The function object pred shall not apply any non-constant function through the dereferenced iterator.

    -9- The BinaryPredicate parameter is used whenever an algorithm expects a function object that when applied to the result of dereferencing two corresponding iterators or to dereferencing an iterator and type T when T is part of the signature returns a value testable as true. In other words, if an algorithm takes BinaryPredicate binary_pred as its argument and first1 and first2 as its iterator arguments, it should work correctly in the construct binary_pred(*first1, *first2) implicitly or contextually converted to bool (Clause 4 [conv]). BinaryPredicate always takes the first iterator's value_type as its first argument, that is, in those cases when T value is part of the signature, it should work correctly in the construct binary_pred(*first1, value) implicitly or contextually converted to bool (Clause 4 [conv]). binary_pred shall not apply any non-constant function through the dereferenced iterators.

  5. Change 25.4 [alg.sorting] p2 as indicated:

    -2- Compare is a function object type (20.8 [function.objects]). The return value of the function call operation applied to an object of type Compare, when implicitly or contextually converted to bool (4 [conv]), yields true if the first argument of the call is less than the second, and false otherwise. Compare comp is used throughout for algorithms assuming an ordering relation. It is assumed that comp will not apply any non-constant function through the dereferenced iterator.

  6. Change 30.2.1 [thread.req.paramname] p2 as indicated:

    -2- If a parameter is Predicate, operator() applied to the actual template argument shall return a value that is convertible to boolPredicate is a function object type (20.8 [function.objects]). The return value of the function call operation applied to an object of type Predicate, when implicitly or contextually converted to bool (4 [conv]), yields true if the corresponding test condition is satisfied, and false otherwise.


2115. Undefined behaviour for valarray assignments with mask_array index?

Section: 26.6.8 [template.mask.array] Status: New Submitter: Thomas Plum Opened: 2011-12-10 Last modified: 2012-01-14

View all issues with New status.

Discussion:

Recently I received a Service Request (SR) alleging that one of our testcases causes an undefined behavior. The complaint is that 26.6.8 [template.mask.array] in C++11 (and the corresponding subclause in C++03) are interpreted by some people to require that in an assignment "a[mask] = b", the subscript mask and the rhs b must have the same number of elements.

IMHO, if that is the intended requirement, it should be stated explicitly.

In any event, there is a tiny editorial cleanup that could be made:

In C++11, 26.6.8.1 [template.mask.array.overview] para 2 mentions

"the expression a[mask] = b;"

but the semicolon cannot be part of an expression. The correction could omit the semicolon, or change the word "expression" to "assignment" or "statement".

Here is the text of the SR, slightly modified for publication:

Subject: SR01174 LVS _26322Y31 has undefined behavior [open]

[Client:]
The test case t263.dir/_26322Y31.cpp seems to be illegal as it has an undefined behaviour. I searched into the SRs but found SRs were not related to the topic explained in this mail (SR00324, SR00595, SR00838).

const char vl[] = {"abcdefghijklmnopqrstuvwxyz"};
const char vu[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"};
const std::valarray<char> v0(vl, 27), vm5(vu, 5), vm6(vu, 6);
std::valarray<char> x = v0;
[…]
const bool vb[] = {false, false, true, true, false, true};
const std::valarray<bool> vmask(vb, 6);
x = v0;
x[vmask] = vm5;      // ***** HERE....
steq(&x[0], "abABeCghijklmnopqrstuvwxyz");
x2 = x[vmask];       // ***** ....AND HERE
[…]

This problem has already been discussed between [experts]: See thread http://gcc.gnu.org/ml/libstdc++/2009-11/threads.html#00051 Conclusion http://gcc.gnu.org/ml/libstdc++/2009-11/msg00099.html

[Plum Hall:]
Before I log this as an SR, I need to check one detail with you.

I did read the email thread you mentioned, and I did find a citation (see INCITS ISO/IEC 14882-2003 Section 26.3.2.6 on valarray computed assignments):

Quote: "If the array and the argument array do not have the same length, the behavior is undefined",

But this applies to computed assignment (*=, +=, etc), not to simple assignment. Here is the C++03 citation re simple assignment:

26.3.2.2 valarray assignment [lib.valarray.assign]

valarray<T>& operator=(const valarray<T>&);

1 Each element of the *this array is assigned the value of the corresponding element of the argument array. The resulting behavior is undefined if the length of the argument array is not equal to the length of the *this array.

In the new C++11 (N3291), we find ...

26.6.2.3 valarray assignment [valarray.assign]

valarray<T>& operator=(const valarray<T>& v);

1 Each element of the *this array is assigned the value of the corresponding element of the argument array. If the length of v is not equal to the length of *this, resizes *this to make the two arrays the same length, as if by calling resize(v.size()), before performing the assignment.

So it looks like the testcase might be valid for C++11 but not for C++03; what do you think?

[Client:]
I quite agree with you but the two problems I mentioned:

x[vmask] = vm5;      // ***** HERE....
[…]
x2 = x[vmask];       // ***** ....AND HERE

refer to mask_array assignment hence target the C++03 26.3.8 paragraph. Correct?

[Plum Hall:]
I mentioned the contrast between C++03 26.3.2.2 para 1 versus C++11 26.6.2.3 para 1.

But in C++03 26.3.8, I don't find any corresponding restriction. Could you quote the specific requirement you're writing about?

[Client:]
I do notice the difference between c++03 26.3.2.2 and c++11 26.6.2.3 about assignments between different sized valarray and I perfectly agree with you.

But, as already stated, this is not a simple valarray assignment but a mask_array assignment (c++03 26.3.8 / c++11 26.6.8). See c++11 quote below:

26.6.8 Class template mask_array
26.6.8.1 Class template mask_array overview
[....]

  1. This template is a helper template used by the mask subscript operator: mask_array<T> valarray<T>::operator[](const valarray<bool>&).

  2. It has reference semantics to a subset of an array specified by a boolean mask. Thus, the expression a[mask] = b; has the effect of assigning the elements of b to the masked elements in a (those for which the corresponding element in mask is true.)

26.6.8.2 mask_array assignment

void operator=(const valarray<T>&) const;
const mask_array& operator=(const mask_array&) const;

1 These assignment operators have reference semantics, assigning the values of the argument array elements to selected elements of the valarray<T> object to which it refers.

In particular, [one of the WG21 experts] insisted on the piece "the elements of b".

That is why I reported the test t263.dir/_26322Y31.cpp having an undefined behaviour.

[Plum Hall:]
OK, I can see that I will have to ask WG21; I will file an appropriate issue with the Library subgroup. In the meantime, I will mark this testcase as "DISPUTED" so that it is not required for conformance testing, until we get a definitive opinion.

Proposed resolution:


2116. std::swap noexcept(what?)

Section: 20.9.4.3 [meta.unary.prop] Status: New Submitter: Dave Abrahams Opened: 2011-12-09 Last modified: 2012-01-14

View other active issues in [meta.unary.prop].

View all other issues in [meta.unary.prop].

View all issues with New status.

Discussion:

IMO if we specified is_[nothrow_]constructible in terms of a variable declaration whose validity requires destructibility, it is clearly a bug in our specification and a failure to realize the actual original intent. The specification should have been in terms of placement-new.

Daniel:
At the time of the specification this was intended and the solution is not done by removing the destruction semantics of is_constructible.

The design of is_constructible was also impacted by the previous Constructible concept that explicitly contained destruction semantics, because during conceptification of the library it turned out to simplify the constraints in the library because you did not need to add Destructible all the time. It often was implied but never spoken out in C++03.

Pure construction semantics was considered as useful as well, so HasConstructor did also exist and would surely be useful as trait as well.

Another example that is often overlooked: This also affects wrapper types like pair, tuple, array that contain potentially more than one type: This is easy to understand if you think of T1 having a deleted destructor and T2 having a constructor that may throw: Obviously the compiler has potentially need to use the destructor of T1 in the constructor of std::pair<T1, T2> to ensure that the core language requirements are satisfied (All previous fully constructed sub-objects must be destructed).

The core language also honors this fact in 12.8 [class.copy] p11:

A defaulted copy/move constructor for a class X is defined as deleted (8.4.3 [dcl.fct.def.delete]) if X has:
[…]
— any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
[…]

Dave:
This is about is_nothrow_constructible in particular. The fact that it is foiled by not having a noexcept dtor is a defect.

Proposed resolution:


2117. ios_base manipulators should have showgrouping/noshowgrouping

Section: 22.4.2.2.2 [facet.num.put.virtuals], 27.5.3.1.2 [ios::fmtflags], 27.5.6.1 [fmtflags.manip] Status: New Submitter: Benjamin Kosnik Opened: 2011-12-15 Last modified: 2012-01-14

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

View all issues with New status.

Discussion:

Iostreams should include a manipulator to toggle grouping on/off for locales that support grouped digits. This has come up repeatedly and been deferred. See LWG 826 for the previous attempt.

If one is using a locale that supports grouped digits, then output will always include the generated grouping characters. However, very plausible scenarios exist where one might want to output the number, un-grouped. This is similar to existing manipulators that toggle on/off the decimal point, numeric base, or positive sign.

See some user commentary here.

Proposed resolution:

This wording is relative to the FDIS.

  1. Insert in 22.4.2.2.2 [facet.num.put.virtuals] paragraph 5:

    Stage 1: The first action of stage 1 is to determine a conversion specifier. The tables that describe this determination use the following local variables

    fmtflags flags = str.flags() ;
    fmtflags basefield = (flags & (ios_base::basefield));
    fmtflags uppercase = (flags & (ios_base::uppercase));
    fmtflags floatfield = (flags & (ios_base::floatfield));
    fmtflags showpos = (flags & (ios_base::showpos));
    fmtflags showbase = (flags & (ios_base::showbase));
    fmtflags showgrouping = (flags & (ios_base::showgrouping));
    
  2. Change header <ios> synopsis, 27.5.1 [iostreams.base.overview] as indicated:

    #include <iosfwd>
    
    namespace std {
      […]
      // 27.5.6, manipulators:
      […]
      ios_base& showpoint     (ios_base& str);
      ios_base& noshowpoint   (ios_base& str);
      ios_base& showgrouping  (ios_base& str);
      ios_base& noshowgrouping(ios_base& str);
      ios_base& showpos       (ios_base& str);
      ios_base& noshowpos     (ios_base& str);
      […]
    }
    
  3. Change class ios_base synopsis, 27.5.3 [ios.base] as indicated:

    namespace std {
      class ios_base {
      public:
      class failure;
        // 27.5.3.1.2 fmtflags
        typedef T1 fmtflags;
        […]
        static constexpr fmtflags showpoint = unspecified ;
        static constexpr fmtflags showgrouping = unspecified ;
        static constexpr fmtflags showpos = unspecified ;
        […]
      };
    }
    
  4. Add a new entry to Table 122 — "fmtflags effects" as indicated:

    Table 122 — fmtflags effects
    Element Effect(s) if set
    […]
    showpoint generates a decimal-point character unconditionally in generated floatingpoint output
    showgrouping generates grouping characters unconditionally in generated output
    […]
  5. After 27.5.3.1.2 [ios::fmtflags] p12 insert the following:

    ios_base& showgrouping(ios_base& str);
    

    -?- Effects: Calls str.setf(ios_base::showgrouping).

    -?- Returns: str.

    ios_base& noshowgrouping(ios_base& str);
    

    -?- Effects: Calls str.unsetf(ios_base::showgrouping).

    -?- Returns: str.


2118. unique_ptr for array does not support cv qualification conversion of actual argument

Section: 20.7.1.3 [unique.ptr.runtime] Status: New Submitter: Alf P. Steinbach Opened: 2011-12-16 Last modified: 2012-01-14

View all other issues in [unique.ptr.runtime].

View all issues with New status.

Discussion:

N3290 20.7.1.3.1 [unique.ptr.runtime.ctor] "unique_ptr constructors":

These constructors behave the same as in the primary template except that they do not accept pointer types which are convertible to pointer. [Note: One implementation technique is to create private templated overloads of these members. — end note]

This language excludes even pointer itself as type for the actual argument.

But of more practical concern is that both Visual C++ 10.0 and MinGW g++ 4.1.1 reject the code below, where only an implicit cv qualification is needed, which cv qualification is supported by the non-array version:

#include <memory>
using namespace std;

struct T {};

T* foo() { return new T; }
T const* bar() { return foo(); }

int main()
{
   unique_ptr< T const >       p1( bar() );        // OK
   unique_ptr< T const [] >    a1( bar() );        // OK

   unique_ptr< T const >       p2( foo() );        // OK
   unique_ptr< T const [] >    a2( foo() );        // ? this is line #15
}

The intent seems to be clearly specified in 20.7.1.3 [unique.ptr.runtime]/1 second bullet:

— Pointers to types derived from T are rejected by the constructors, and by reset.

But the following language in 20.7.1.3.1 [unique.ptr.runtime.ctor] then rejects far too much...

Proposed new wording of N3290 20.7.1.3.1 [unique.ptr.runtime.ctor] "unique_ptr constructors":

These constructors behave the same as in the primary template except that actual argument pointers p to types derived from T are rejected by the constructors. [Note: One implementation technique is to create private templated overloads of these members. — end note]

This will possibly capture the intent better, and avoid the inconsistency between the non-array and array versions of unique_ptr, by using nearly the exact same phrasing as for the paragraph explaining the intent.

Proposed resolution:

This wording is relative to the FDIS.

Change 20.7.1.3.1 [unique.ptr.runtime.ctor] as indicated:

explicit unique_ptr(pointer p) noexcept;
unique_ptr(pointer p, see below d) noexcept;
unique_ptr(pointer p, see below d) noexcept;

These constructors behave the same as in the primary template except that they do not accept pointer types which are convertible to pointerargument pointers p to types derived from T are rejected by the constructors. [Note: One implementation technique is to create private templated overloads of these members. — end note]


2119. Missing hash specializations for extended integer types

Section: 20.8.12 [unord.hash] Status: New Submitter: Daniel Krügler Opened: 2011-12-16 Last modified: 2012-01-14

View all other issues in [unord.hash].

View all issues with New status.

Discussion:

According to the header <functional> synopsis 20.8 [function.objects] and to the explicit description in 20.8.12 [unord.hash] class template hash specializations shall be provided for all arithmetic types that are not extended integer types. This is not explicitly mentioned, but neither the list nor any normative wording does include them, so it follows by implication.

What are the reasons that extended integer types are excluded? E.g. for numeric_limits corresponding specializations are required. I would expect that an unordered_map with key type std::uintmax_t would just work, but that depends now on whether this type is an extended integer type or not.

This issue is not asking for also providing specializations for the cv-qualified arithmetic types. While this is surely a nice-to-have feature, I consider that restriction as a more secondary problem in practice.

The proposed resolution also fixes a problem mentioned in 2109 in regard to confusing requirements on user-defined types and those on implementations.

Proposed resolution:

This wording is relative to the FDIS.

Change 20.8.12 [unord.hash] p2 as indicated:

template <> struct hash<bool>;
template <> struct hash<char>;
[…]
template <> struct hash<long double>;
template <class T> struct hash<T*>;

-2- Requires: the template specializations shall meet the requirements of class template hash (20.8.12 [unord.hash])The header <functional> provides definitions for specializations of the hash class template for each cv-unqualified arithmetic type. This header also provides a definition for a partial specialization of the hash class template for any pointer type. The requirements for the members of these specializations are given in sub-clause 20.8.12 [unord.hash].


2120. What should async do if neither 'async' nor 'deferred' is set in policy?

Section: 30.6.8 [futures.async] Status: New Submitter: Jonathan Wakely Opened: 2012-01-01 Last modified: 2012-01-14

View other active issues in [futures.async].

View all other issues in [futures.async].

View all issues with New status.

Discussion:

Implementations already disagree, one returns an invalid future with no shared state, one chooses policy == async and one chooses policy == deferred, see c++std-lib-30839, c++std-lib-30840 and c++std-lib-30844. It's not clear if returning an invalid future is allowed by the current wording.

If the intention is to allow an empty future to be returned, then 30.6.8 [futures.async] p3 and p4 should be adjusted to clarify that a shared state might not be created and an invalid future might be returned.

If the intention is that a valid future is always returned, p3 should say something about the case where none of the conditions applies.

Proposed resolution:


2121. app for string streams

Section: 27.8.6 [stringstream.cons] Status: New Submitter: Nicolai Josuttis Opened: 2012-01-15 Last modified: 2012-01-16

View all issues with New status.

Discussion:

This issue was raised while discussing issue 1448.

Note the following program:

string s("s1: 123456789");
ostringstream s1(s, ios_base::out|ios_base::app);
s1 << "hello";
cout << s1.str() << endl;

With g++4.x it prints:

s1: 123456789hello

With VisualC++10 it prints:

hello23456789

From my intuitive understanding the flag "app" should result in the output of g++4.x. I also would read that from 27.5.3.1.4 [ios::openmode] claiming:

app   seek to end before each write

However in issue 1448 P.J.Plauger comments:

I think we should say nothing special about app at construction time (thus leaving the write pointer at the beginning of the buffer). Leave implementers wiggle room to ensure subsequent append writes as they see fit, but don't change existing rules for initial seek position.

Note that the flag ate on both platforms appends "hello" to s.

Proposed resolution:


2122. merge() stability for lists versus forward lists

Section: 23.3.5.5 [list.ops], 23.3.4.6 [forwardlist.ops] Status: New Submitter: Nicolai Josuttis Opened: 2012-01-15 Last modified: 2012-01-16

View all other issues in [list.ops].

View all issues with New status.

Discussion:

forward_list::merge() is specified in 23.3.4.6 [forwardlist.ops], p19 as follows:

This operation shall be stable: for equivalent elements in the two lists, the elements from *this shall always precede the elements from x.

But list::merge() is only specified in 23.3.5.5 [list.ops], p24 as follows:

Remarks: Stable.

Note that in general we define "stable" only for algorithms (see [defns.stable] and 17.6.5.7 [algorithm.stable]) so for member function we should explain it everywhere we use it.

Thus for lists we have to add:

Stable: for equivalent elements in the two lists, the elements from the list always precede the elements from the argument list.

This, BTW, was the specification we had with C++03.

In addition, I wonder whether we also have some guarantees regarding stability saying that the order of equivalent elements of each list merged remains stable (which would be my interpretation of just saying "stable", BTW).

Thus, I'd expect that for equivalent elements we guarantee that

Proposed resolution:

This wording is relative to the FDIS.

  1. Change 23.3.5.5 [list.ops] as indicated:

    void                          merge(list<T,Allocator>& x);
    void                          merge(list<T,Allocator>&& x);
    template <class Compare> void merge(list<T,Allocator>& x, Compare comp);
    template <class Compare> void merge(list<T,Allocator>&& x, Compare comp);

    […]

    -24- Remarks: StableThis operation shall be stable: for equivalent elements in the two lists, the elements from *this shall always precede the elements from x and the order of equivalent elements of *this and x remains stable. If (&x != this) the range [x.begin(), x.end()) is empty after the merge. No elements are copied by this operation. The behavior is undefined if this->get_allocator() != x.get_allocator().

  2. Change 23.3.4.6 [forwardlist.ops] as indicated:

    void merge(forward_list<T,Allocator>& x);
    void merge(forward_list<T,Allocator>&& x);
    template <class Compare> void merge(forward_list<T,Allocator>& x, Compare comp);
    template <class Compare> void merge(forward_list<T,Allocator>&& x, Compare comp);

    […]

    -19- Effects: Merges x into *this. This operation shall be stable: for equivalent elements in the two lists, the elements from *this shall always precede the elements from x and the order of equivalent elements of *this and x remains stable. x is empty after the merge. If an exception is thrown other than by a comparison there are no effects. Pointers and references to the moved elements of x now refer to those same elements but as members of *this. Iterators referring to the moved elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.


2123. merge() allocator requirements for lists versus forward lists

Section: 23.3.4.6 [forwardlist.ops] Status: New Submitter: Nicolai Josuttis Opened: 2012-01-15 Last modified: 2012-01-16

View other active issues in [forwardlist.ops].

View all other issues in [forwardlist.ops].

View all issues with New status.

Discussion:

Sub-clause 23.3.5.5 [list.ops], p24 states for lists:

The behavior is undefined if this->get_allocator() != x.get_allocator().

But there is nothing like that for forward lists in 23.3.4.6 [forwardlist.ops], although I would expect the same undefined behavior there.

Proposed resolution:

This wording is relative to the FDIS.

  1. Add a new paragraph after 23.3.4.6 [forwardlist.ops] p19 as indicated:

    void merge(forward_list<T,Allocator>& x);
    void merge(forward_list<T,Allocator>&& x);
    template <class Compare> void merge(forward_list<T,Allocator>& x, Compare comp);
    template <class Compare> void merge(forward_list<T,Allocator>&& x, Compare comp);

    […]

    -19- Effects: […]

    -?- Remarks: The behavior is undefined if this->get_allocator() != x.get_allocator().