Doc. no. N3087=10-0077
Date: 2010-03-28
Project: Programming Language C++
Reply to: Howard Hinnant <howard.hinnant@gmail.com>

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

Reference ISO/IEC IS 14882:2003(E)

Also see:

The purpose of this document is to record the status of issues which have come before the Library Working Group (LWG) of the ANSI (J16) and ISO (WG21) C++ Standards Committee. Issues represent potential defects in the ISO/IEC IS 14882:2003(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:2003(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.

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).

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

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

CD1 - (Committee Draft 2008) - The full WG21 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 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 (TC), or are closed without action other than a Record of Response (RR ). The intent of this LWG process is that only issues which are truly defects in the Standard move to the formal ISO DR status.

Active Issues


579. erase(iterator) for unordered containers should not return an iterator

Section: 23.2.5 [unord.req] Status: Open Submitter: Joaquín M López Muñoz Opened: 2006-06-13 Last modified: 2010-03-28

View other active issues in [unord.req].

View all other issues in [unord.req].

View all issues with Open status.

Discussion:

See N2023 for full discussion.

[ 2009-12-11 Paolo opens: ]

I'm asking for DR 579 to be re-opened, basing on recent discussions on the library reflector, see Message c++std-lib-26040 and replies.

[ 2010-02-07 Paolo updates wording. ]

As pointed out by Chris in c++std-lib-26040, that an erase(unordered_container, iterator) returning an iterator can easily implemented in user code, if needed; that actually returning an iterator costs nothing for the overload taking two iterators, thus that proposed change is only for consistency; that forward_list::erase_after also returns void (for different reasons, granted, but isn't that any "erase" function in the containers uniformly returns an iterator); that, also in thread started by Chris' message, Alberto pointed out that the proxy idea isn't a good one; that users both of the GNU and Boost implementations are reporting serious performance problems with the current version returning an iterator.

[ 2010-02-07 Original wording saved here: ]

Option 1:

The problem can be eliminated by omitting the requirement that a.erase(q) return an iterator. This is, however, in contrast with the equivalent requirements for other standard containers.

Option 2:

a.erase(q) can be made to compute the next iterator only when explicitly requested: the technique consists in returning a proxy object implicitly convertible to iterator, so that

iterator q1=a.erase(q);

works as expected, while

a.erase(q);

does not ever invoke the conversion-to-iterator operator, thus avoiding the associated computation. To allow this technique, some sections of TR1 along the line "return value is an iterator..." should be changed to "return value is an unspecified object implicitly convertible to an iterator..." Although this trick is expected to work transparently, it can have some collateral effects when the expression a.erase(q) is used inside generic code.

[ 2010-02-09 Moved to Tentatively Ready after 6 positive votes on c++std-lib. ]

[ 2010 Pittsburgh: ]

There was no concensus for moving this to Ready. However there was concensus for moving this to NAD.

Rationale updated below.

[ 2010 Pittsburgh: ]

Reopened and proposed wording updated by Beman.

[ 2010 Pittsburgh: ]

Moved to Ready for Pittsburgh.

[ 2010 Pittsburgh: ]

Reopened. There is some discussion as to whether there is an acceptable implementation of erase which returns iterator. Need more time to study it.

[ 2010-03-27 Joaquín adds: ]

Signature of iterator erase(const_iterator) should be changed to void erase(const_iterator). If this is not viable an acceptable tradeoff could be to make the return type of erase(const_iterator) implementation defined.

The standard should allow implementations of unordered associative containers using either singly or doubly linked lists. N2023 proves that singly-linked lists implementations cannot provide the required complexity for iterator erase(const_iterator). Thus, some action is needed to allow both implementations.

Option 1: Changing the required complexity from O(1) to O(log n). This option merely masks a design flaw. Users are forcefully penalized for what they don't use (the returned iterator). Besides, they would have to learn about the pathological (yet very real) situations where using erase can lead to quadratic performance. Two out of these three objections remain even if some alternative member function like void quick_erase(const_iterator) is thrown in to the interface.

Some objections have been expressed to changing return type of erase to void, arguing that it would break current existing practice with standard library implementations based on doubly-linked lists, where the problem does not occur. However implementations based on drafts should not block the resolution of a serious design issue, more so when the issue will hurt future users of C++, as it's happening already.

Option 2: Make erase return type implementation defined. There's a possible tradeoff with the objectors above consisting in changing the signature to implementation defined erase(iterator), so that returning an iterator is indeed a valid extension. To this it can be argued that this would make implementantions returning an iterator look as somehow promoting proprietary extensions: this in my opinion is not a valid argument since those implementations are already extending the required interface by providing bidirectional iterators (just forward iterators are required).

Rationale:

N2023 was discussed in Portland and the consensus was that there appears to be no need for either change proposed in the paper. The consensus opinion was that since the iterator could serve as its own proxy, there appears to be no need for the change. In general, "converts to" is undesirable because it interferes with template matching.

Post Toronto: There does not at this time appear to be consensus with the Portland consensus.

[ Bellevue: ]

The Bellevue review of this issue reached consensus with the Portland consensus, in contravention of the Toronto non-consensus. Common implementations have the iterator readily available, and most common uses depend on the iterator being returned.

****

The rationale for the change in direction here is best summarized by Paolo's 2010-02-07 comment.

Pittsburgh: Issue is wrong because we believe the standard is consistent as written and the complexity is achievable.

Pittsburgh: We want to enable both existing unordred container implementations.

Proposed resolution:

In 23.2.5 [unord.req], Table 98, change the following as indicated:

Table 98 — Unordered associative container requirements (in addition to container)
Expression Return type Assertion/note pre-/post-condition Complexity
a.erase(q) iterator Erases the element pointed to by q. Return value is the iterator immediately following q prior to the erasure. Average case O(1) O(max(1, 1/a.load_factor()), worst case O(a.size()) O(max(a.size(), a.bucket_count()).
a.erase(q1, q2) iterator Erases all elements in the range [q1, q2). Return value is the iterator immediately following the erased elements prior to the erasure. Average case linear in distance(q1, q2) O(max(distance(q1,q2), 1/a.load_factor())), worst case O(a.size()) O(max(a.size(), a.bucket_count()).
a.quick_erase(q) void Erases the element pointed to by q. Average case O(1), worst case O(a.size()).
a.quick_erase(q1, q2) void Erases all elements in the range [q1, q2). Average case linear in distance(q1, q2), worst case O(a.size()).

Adjust the declarations accordingly in 23.5.1 [unord.map], 23.5.2 [unord.multimap], 23.5.3 [unord.set], and 23.5.4 [unord.multiset].

iterator erase(const_iterator position);
void quick_erase(const_iterator position);  
...
iterator erase(const_iterator first, const_iterator last);
void quick_erase(const_iterator first, const_iterator last);

801. tuple and pair trivial members

Section: 20.4 [tuple] Status: Open Submitter: Lawrence Crowl Opened: 2008-02-18 Last modified: 2010-03-10

View all other issues in [tuple].

View all issues with Open status.

Discussion:

Classes with trivial special member functions are inherently more efficient than classes without such functions. This efficiency is particularly pronounced on modern ABIs that can pass small classes in registers. Examples include value classes such as complex numbers and floating-point intervals. Perhaps more important, though, are classes that are simple collections, like pair and tuple. When the parameter types of these classes are trivial, the pairs and tuples themselves can be trivial, leading to substantial performance wins.

The current working draft make specification of trivial functions (where possible) much easer through defaulted and deleted functions. As long as the semantics of defaulted and deleted functions match the intended semantics, specification of defaulted and deleted functions will yield more efficient programs.

There are at least two cases where specification of an explicitly defaulted function may be desirable.

First, the std::pair template has a non-trivial default constructor, which prevents static initialization of the pair even when the types are statically initializable. Changing the definition to

pair() = default;

would enable such initialization. Unfortunately, the change is not semantically neutral in that the current definition effectively forces value initialization whereas the change would not value initialize in some contexts.

** Does the committee confirm that forced value initialization was the intent? If not, does the committee wish to change the behavior of std::pair in C++0x?

Second, the same default constructor issue applies to std::tuple. Furthermore, the tuple copy constructor is current non-trivial, which effectively prevents passing it in registers. To enable passing tuples in registers, the copy constructor should be make explicitly defaulted. The new declarations are:

tuple() = default;
tuple(const tuple&) = default;

This changes is not implementation neutral. In particular, it prevents implementations based on pointers to the parameter types. It does however, permit implementations using the parameter types as bases.

** How does the committee wish to trade implementation efficiency versus implementation flexibility?

[ Bellevue: ]

General agreement; the first half of the issue is NAD.

Before voting on the second half, it was agreed that a "Strongly Favor" vote meant support for trivial tuples (assuming usual requirements met), even at the expense of other desired qualities. A "Weakly Favor" vote meant support only if not at the expense of other desired qualities.

Concensus: Go forward, but not at expense of other desired qualities.

It was agreed to Alisdair should fold this work in with his other pair/tuple action items, above, and that issue 801 should be "open", but tabled until Alisdair's proposals are disposed of.

[ 2009-05-27 Daniel adds: ]

This is partly solved by 1117.

[ 2009-07 Frankfurt: ]

Wait for dust to settle from fixing exception safety problem with rvalue refs.

[ 2009-07-20 Alisdair adds: ]

Basically, this issue is what should we do with the default constructor for pairs and tuples of trivial types. The motivation of the issue was to force static initialization rather than dynamic initialization, and was rejected in the case of pair as it would change the meaning of existing programs. The advice was "do the best we can" for tuple without changing existing meaning.

Frankfurt seems to simply wait and see the resolution on no-throw move constructors, which (I believe) is only tangentially related to this issue, but as good as any to defer until Santa Cruz.

Looking again now, I think constant (static) initialization for pair can be salvaged by making the default construct constexpr. I have a clarification from Core that this is intended to work, even if the constructor is not trivial/constexpr, so long as no temporaries are implied in the process (even if elided).

[ 2009-10 Santa Cruz: ]

Leave as open. Alisdair to provide wording.

[ 2010 Pittsburgh: ]

We believe this may be NAD Editorial since both pair and tuple now have constexpr default constructors, but we're not sure.

Proposed resolution:


868. default construction and value-initialization

Section: 23 [containers] Status: Review Submitter: Alberto Ganesh Barbati Opened: 2008-07-22 Last modified: 2010-03-27

View all other issues in [containers].

View all issues with Review status.

Discussion:

The term "default constructed" is often used in wording that predates the introduction of the concept of value-initialization. In a few such places the concept of value-initialization is more correct than the current wording (for example when the type involved can be a built-in) so a replacement is in order. Two of such places are already covered by issue 867. This issue deliberately addresses the hopefully non-controversial changes in the attempt of being approved more quickly. A few other occurrences (for example in std::tuple, std::reverse_iterator and std::move_iterator) are left to separate issues. For std::reverse_iterator, see also issue 408. This issue is related with issue 724.

[ San Francisco: ]

The list provided in the proposed resolution is not complete. James Dennett will review the library and provide a complete list and will double-check the vocabulary.

This issue relates to Issue 886 tuple construction

[ 2009-07 Frankfurt ]

The proposed resolution is incomplete.

Move to Tentatively NAD Future. Howard will contact Ganesh for wording. If wording is forthcoming, Howard will move it back to Review.

[ 2009-07-18 Ganesh updated the proposed wording. ]

Howard: Moved back to Review. Note that 20.2.1 [utility.arg.requirements] refers to a section that is not in the current working paper, but does refer to a section that we expect to reappear after the de-concepts merge. This was a point of confusion we did not recognize when we reviewed this issue in Frankfurt.

Howard: Ganesh also includes a survey of places in the WP surveyed for changes of this nature and purposefully not treated:

Places where changes are not being proposed

In the following paragraphs, we are not proposing changes because it's not clear whether we actually prefer value-initialization over default-initialization (now partially covered by 1012):

In the following paragraphs, the expression "default constructed" need not be changed, because the relevant type does not depend on a template parameter and has a user-provided constructor:

[ 2009-08-18 Daniel adds: ]

I have no objections against the currently suggested changes, but I also cross-checked with the list regarding intentionally excluded changes, and from this I miss the discussion of

  1. 21.4.1 [string.require]/2:

    "[..] The Allocator object used shall be a copy of the Allocator> object passed to the basic_string object's constructor or, if the constructor does not take an Allocator argument, a copy of a default-constructed Allocator object."
  2. N2723, 26.5.1.4 [rand.req.eng], Table 109, expression "T()":

    Pre-/post-condition: "Creates an engine with the same initial state as all other default-constructed engines of type X."

    as well as in 26.5.5 [rand.predef]/1-9 (N2914), 26.5.7.1 [rand.util.seedseq]/3, 27.7.1.1.1 [istream.cons]/3, 27.7.2.2 [ostream.cons]/9 (N2914), 28.13 [re.grammar]/2, 30.3.1.4 [thread.thread.assign]/1 (N2914),

    [ Candidates for the "the expression "default constructed" need not be changed" list ]

    I'm fine, if these would be added to the intentionally exclusion list, but mentioning them makes it easier for other potential reviewers to decide on the relevance or not-relevance of them for this issue.

  3. I suggest to remove the reference of [func.referenceclosure.invoke] in the "it's not clear" list, because this component does no longer exist.

  4. I also suggest to add a short comment that all paragraphs in the resolution whether they refer to N2723 or to N2914 numbering, because e.g. "Change 23.3.2.1 [deque.cons] para 5" is an N2723 coordinate, while "Change 23.3.2.2 [deque.capacity] para 1" is an N2914 coordinate. Even better would be to use one default document for the numbering (probably N2914) and mention special cases (e.g. "Change 20.2.1 [utility.arg.requirements] para 2" as referring to N2723 numbering).

[ 2009-08-18 Alisdair adds: ]

I strongly believe the term "default constructed" should not appear in the library clauses unless we very clearly define a meaning for it, and I am not sure what that would be.

In those cases where we do not want to replace "default constructed" with "vale initialized" we should be using "default initialized". If we have a term that could mean either, we reduce portability of programs.

I have not done an exhaustive review to clarify if that is a vendor freedom we have reason to support (e.g. value-init in debug, default-init in release) so I may yet be convinced that LWG has reason to define this new term of art, but generally C++ initialization is confusing enough without supporting further ill-defined terms.

[ 2009-10 Santa Cruz: ]

Move to Ready.

[ 2010 Pittsburgh: ]

Moved to review in order to enable conflict resolution with 704.

[ 2010-03-26 Daniel harmonized the wording with the upcoming FCD. ]

Proposed resolution:

Change 20.2.1 [utility.arg.requirements] para 2:

2 In general, a default constructor is not required. Certain container class member function signatures specify the default constructorT() as a default argument. T() shall be a well-defined expression (8.5) if one of those signatures is called using the default argument (8.3.6).

Change 23.3.2.1 [deque.cons] para 3:

3 Effects: Constructs a deque with n default constructedvalue-initialized elements.

Change 23.3.2.2 [deque.capacity] para 1:

1 Effects: If sz < size(), equivalent to erase(begin() + sz, end());. If size() < sz, appends sz - size() default constructedvalue-initialized elements to the sequence.

Change 23.3.3.1 [forwardlist.cons] para 3:

3 Effects: Constructs a forward_list object with n default constructedvalue-initialized elements.

Change 23.3.3.4 [forwardlist.modifiers] para 22:

22 Effects: [...] For the first signature the inserted elements are default constructedvalue-initialized, and for the second signature they are copies of c.

Change 23.3.4.1 [list.cons] para 3:

3 Effects: Constructs a list with n default constructedvalue-initialized elements.

Change 23.3.4.2 [list.capacity] para 1:

1 Effects: If sz < size(), equivalent to list<T>::iterator it = begin(); advance(it, sz); erase(it, end());. If size() < sz, appends sz - size() default constructedvalue-initialized elements to the sequence.

Change 23.3.6.1 [vector.cons] para 3:

3 Effects: Constructs a vector with n default constructedvalue-initialized elements.

Change 23.3.6.2 [vector.capacity] para 9:

9 Effects: If sz < size(), equivalent to erase(begin() + sz, end());. If size() < sz, appends sz - size() default constructedvalue-initialized elements to the sequence.

951. Various threading bugs #1

Section: 20.10.2.1 [time.traits.is_fp] Status: Open Submitter: Pete Becker Opened: 2009-01-07 Last modified: 2010-02-11

View all issues with Open status.

Discussion:

Related to 953.

20.10.2.1 [time.traits.is_fp] says that the type Rep "is assumed to be ... a class emulating an integral type." What are the requirements for such a type?

[ 2009-05-10 Howard adds: ]

IntegralLike.

[ Batavia (2009-05): ]

As with issue 953, we recommend this issue be addressed in the context of providing concepts for the entire thread header.

We look forward to proposed wording.

Move to Open.

[ 2009-08-01 Howard adds: ]

I have surveyed all clauses of 20.10.2.2 [time.traits.duration_values], 20.10.2.3 [time.traits.specializations] and 20.10.3 [time.duration]. I can not find any clause which involves the use of a duration::rep type where the requirements on the rep type are not clearly spelled out. These requirements were carefully crafted to allow any arithmetic type, or any user-defined type emulating an arithmetic type.

Indeed, treat_as_floating_point becomes completely superfluous if duration::rep can never be a class type.

There will be some Rep types which will not meet the requirements of every duration operation. This is no different than the fact that vector<T> can easily be used for types T which are not DefaultConstructible, even though some members of vector<T> require T to be DefaultConstructible. This is why the requirements on Rep are specified for each operation individually.

In 20.10.2.1 [time.traits.is_fp] p1:

template <class Rep> struct treat_as_floating_point 
  : is_floating_point<Rep> { };
The duration template uses the treat_as_floating_point trait to help determine if a duration object can be converted to another duration with a different tick period. If treat_as_floating_point<Rep>::value is true, then Rep is a floating-point type and implicit conversions are allowed among durations. Otherwise, the implicit convertibility depends on the tick periods of the durations. If Rep is a class type which emulates a floating-point type, the author of Rep can specialize treat_as_floating_point so that duration will treat this Rep as if it were a floating-point type. Otherwise Rep is assumed to be an integral type or a class emulating an integral type.

The phrases "a class type which emulates a floating-point type" and "a class emulating an integral type" are clarifying phrases which refer to the summation of all the requirements on the Rep type specified in detail elsewhere (and should not be repeated here).

This specification has been implemented, now multiple times, and the experience has been favorable. The current specification clearly specifies the requirements at each point of use (though I'd be happy to fix any place I may have missed, but none has been pointed out).

I am amenable to improved wording of this paragraph (and any others), but do not have any suggestions for improved wording at this time. I am strongly opposed to changes which would significantly alter the semantics of the specification under 20.10 [time] without firmly grounded and documented rationale, example implementation, testing, and user experience which relates a positive experience.

I recommend NAD unless someone wants to produce some clarifying wording.

[ 2009-10 Santa Cruz: ]

Stefanus to provide wording to turn this into a note.

[ 2010-02-11 Stefanus provided wording. ]

Proposed resolution:

Change 20.10.2.1 [time.traits.is_fp]/1:

1 The duration template uses the treat_as_floating_point trait to help determine if a duration object can be converted to another duration with a different tick period. If treat_as_floating_point<Rep>::value is true, then Rep is a floating-point type and implicit conversions are allowed among durations. Otherwise, the implicit convertibility depends on the tick periods of the durations. If Rep is a class type which emulates a floating-point type, the author of Rep can specialize treat_as_floating_point so that duration will treat this Rep as if it were a floating-point type. Otherwise Rep is assumed to be an integral type or a class emulating an integral type. [Note: The intention of this trait is to indicate whether a given class behaves like a floating point type, and thus allows division of one value by another with acceptable loss of precision. If treat_as_floating_point<Rep>::value is false, Rep will be treated as if it behaved like an integral type for the purpose of these conversions. — end note]

953. Various threading bugs #3

Section: 20.10.1 [time.clock.req] Status: Open Submitter: Pete Becker Opened: 2009-01-07 Last modified: 2010-02-11

View other active issues in [time.clock.req].

View all other issues in [time.clock.req].

View all issues with Open status.

Discussion:

Related to 951.

20.10.1 [time.clock.req] says that a clock's rep member is "an arithmetic type or a class emulating an arithmetic type." What are the requirements for such a type?

[ 2009-05-10 Howard adds: ]

This wording was aimed directly at the ArithmeticLike concept.

[ Batavia (2009-05): ]

We recommend this issue be addressed in the context of providing concepts for the entire thread header.

May resolve for now by specifying arithmetic types, and in future change to ArithmeticLike. However, Alisdair believes this is not feasible.

Bill disagrees.

We look forward to proposed wording. Move to Open.

[ 2009-08-01 Howard adds: ]

See commented dated 2009-08-01 in 951.

[ 2009-10 Santa Cruz: ]

Stefanus to provide wording to turn this into a note.

[ 2010-02-11 Stephanus provided wording for 951 which addresses this issue as well. ]

Proposed resolution:


956. Various threading bugs #6

Section: 20.10.1 [time.clock.req] Status: Open Submitter: Pete Becker Opened: 2009-01-07 Last modified: 2010-03-28

View other active issues in [time.clock.req].

View all other issues in [time.clock.req].

View all issues with Open status.

Discussion:

20.10.1 [time.clock.req] uses the word "native" in several places, but doesn't define it. What is a "native duration"?

[ 2009-05-10 Howard adds: ]

The standard uses "native" in several places without defining it (e.g. 2.14.3 [lex.ccon]). It is meant to mean "that which is defined by the facility", or something along those lines. In this case it refers to the nested time_point and duration types of the clock. Better wording is welcome.

[ Batavia (2009-05): ]

Move to Open pending proposed wording from Pete.

[ 2009-10-23 Pete provides wording: ]

[ 2009-11-18 Daniel adds: ]

I see that 30.4.2 [thread.timedmutex.requirements]/3 says:

Precondition: If the tick period of rel_time is not exactly convertible to the native tick period, the duration shall be rounded up to the nearest native tick period.

I would prefer to see that adapted as well. Following the same style as the proposed resolution I come up with

Precondition: If the tick period of rel_time is not exactly convertible to the native tick period of the execution environment, the duration shall be rounded up to the nearest native tick period of the execution environment.

[ 2010-03-28 Daniel synced wording with N3092 ]

Proposed resolution:

Remove every occurrence of "native" in 20.10.1 [time.clock.req].

Add the following sentence at the end of 20.10.1 [time.clock.req]/1:

A clock is a bundle consisting of a native duration, a native time_point, and a function now() to get the current time_point. The origin of the clock's time_point is referred to as the clock's epoch. A clock shall meet the requirements in Table 56. The duration and time_point types have the natural size and resolution suggested by the architecture of the execution environment.

964. Various threading bugs #14

Section: 30.5.2 [thread.condition.condvarany] Status: Open Submitter: Pete Becker Opened: 2009-01-07 Last modified: 2010-03-15

View all other issues in [thread.condition.condvarany].

View all issues with Open status.

Discussion:

The requirements for the constructor for condition_variable has several error conditions, but the requirements for the constructor for condition_variable_any has none. Is this difference intentional?

[ Summit: ]

Move to open, pass to Howard. If this is intentional, a note may be helpful. If the error conditions are to be copied from condition_variable, this depends on LWG 965.

[ Post Summit Howard adds: ]

The original intention (N2447) was to let the OS return whatever errors it was going to return, and for those to be translated into exceptions, for both condition_variable and condition_variable_any. I have not received any complaints about specific error conditions from vendors on non-POSIX platforms, but such complaints would not surprise me if they surfaced.

[ 2009-10 Santa Cruz: ]

Leave open. Benjamin to provide wording.

[ 2010 Pittsburgh: ]

We don't have throw clauses for condition variables.

This issue may be dependent on LWG 1268.

Leave open. Detlef will coordinate with Benjamin.

Consider mberging LWG 964, 966, and 1268 into a single paper.

Proposed resolution:


966. Various threading bugs #16

Section: 30.5.1 [thread.condition.condvar] Status: Open Submitter: Pete Becker Opened: 2009-01-07 Last modified: 2010-03-15

View all other issues in [thread.condition.condvar].

View all issues with Open status.

Discussion:

30.5.1 [thread.condition.condvar]: condition_variable::wait and condition_variable::wait_until both have a postcondition that lock is locked by the calling thread, and a throws clause that requires throwing an exception if this postcondition cannot be achieved. How can the implementation detect that this lock can never be obtained?

[ Summit: ]

Move to open. Requires wording. Agreed this is an issue, and the specification should not require detecting deadlocks.

[ 2009-08-01 Howard provides wording. ]

The proposed wording is inspired by the POSIX spec which says:

[EINVAL]
The value specified by cond or mutex is invalid.
[EPERM]
The mutex was not owned by the current thread at the time of the call.

I do not believe [EINVAL] is possible without memory corruption (which we don't specify). [EPERM] is possible if this thread doesn't own the mutex, which is listed as a precondition. "May" is used instead of "Shall" because not all OS's are POSIX.

[ 2009-10 Santa Cruz: ]

Leave open, Detlef to provide improved wording.

[ 2009-10-23 Detlef Provided wording. ]

Detlef's wording put in Proposed resolution. Original wording here:

Change 30.5.1 [thread.condition.condvar] p12, p19 and 30.5.2 [thread.condition.condvarany] p10, p16:

Throws: May throw std::system_error if a precondition is not met. when the effects or postcondition cannot be achieved.

[ 2009-10 Santa Cruz: ]

Leave open, Detlef to provide improved wording.

[ 2009-11-18 Anthony adds: ]

condition_variable::wait takes a unique_lock<mutex>. We know whether or not a unique_lock owns a lock, through use of its owns_lock() member.

I would like to propose the following resolution:

Modify the first sentence of 30.5.1 [thread.condition.condvar] p9:

void wait(unique_lock<mutex>& lock);
9 Precondition: lock is locked by the calling thread lock.owns_lock() is true, and either

...

Replace 30.5.1 [thread.condition.condvar] p11-13 with:

void wait(unique_lock<mutex>& lock);

...

11 Postcondition: lock is locked by the calling thread lock.owns_lock() is true.

12 Throws: std::system_error when the effects or postcondition cannot be achieved if the implementation detects that the preconditions are not met or the effects cannot be achieved. Any exception thrown by lock.lock() or lock.unlock().

13 Error Conditions: The error conditions are implementation defined.

  • equivalent error condition from lock.lock() or lock.unlock().

[ 2010 Pittsburgh: ]

There are heavy conflicts with adopted papers.

This issue is dependent on LWG 1268.

Leave open pending outstanding edits to the working draft. Detlef will provide wording.

Possibly related to 964.

Proposed resolution:

Replace 30.5.1 [thread.condition.condvar] p12, p19 and 30.5.2 [thread.condition.condvarany] p10, p16:

Throws: std::system_error when the effects or postcondition cannot be achieved.

Error conditions:

Throws: It is implementation-defined whether a std::system_error with implementation-defined error condition is thrown if the precondition is not met.


985. Allowing throwing move

Section: 23.2.1 [container.requirements.general] Status: Open Submitter: Rani Sharoni Opened: 2009-02-12 Last modified: 2010-03-28

View other active issues in [container.requirements.general].

View all other issues in [container.requirements.general].

View all issues with Open status.

Discussion:

Introduction

This proposal is meant to resolve potential regression of the N2800 draft, see next section, and to relax the requirements for containers of types with throwing move constructors.

The basic problem is that some containers operations, like push_back, have a strong exception safety guarantee (i.e. no side effects upon exception) that are not achievable when throwing move constructors are used since there is no way to guarantee revert after partial move. For such operations the implementation can at most provide the basic guarantee (i.e. valid but unpredictable) as it does with multi copying operations (e.g. range insert).

For example, vector<T>::push_back() (where T has a move constructor) might resize the vector and move the objects to the new underlying buffer. If move constructor throws it might not be possible to recover the throwing object or to move the old objects back to the original buffer.

The current draft is explicit by disallowing throwing move for some operations (e.g. vector<>::reserve) and not clear about other operations mentioned in 23.2.1 [container.requirements.general]/10 (e.g. single element insert): it guarantees strong exception safety without explicitly disallowing a throwing move constructor.

Regression

This section only refers to cases in which the contained object is by itself a standard container.

Move constructors of standard containers are allowed to throw and therefore existing operations are broken, compared with C++03, due to move optimization. (In fact existing implementations like Dinkumware are actually throwing).

For example, vector< list<int> >::reserve yields undefined behavior since list<int>'s move constructor is allowed to throw. On the other hand, the same operation has strong exception safety guarantee in C++03.

There are few options to solve this regression:

  1. Disallow throwing move and throwing default constructor
  2. Disallow throwing move but disallowing usage after move
  3. Special casing
  4. Disallow throwing move and making it optional

Option 1 is suggested by proposal N2815 but it might not be applicable for existing implementations for which containers default constructors are throwing.

Option 2 limits the usage significantly and it's error prone by allowing zombie objects that are nothing but destructible (e.g. no clear() is allowed after move). It also potentially complicates the implementation by introducing special state.

Option 3 is possible, for example, using default construction and swap instead of move for standard containers case. The implementation is also free to provide special hidden operation for non throwing move without forcing the user the cope with the limitation of option-2 when using the public move.

Option 4 impact the efficiency in all use cases due to rare throwing move.

The proposed wording will imply option 1 or 3 though option 2 is also achievable using more wording. I personally oppose to option 2 that has impact on usability.

Relaxation for user types

Disallowing throwing move constructors in general seems very restrictive since, for example, common implementation of move will be default construction + swap so move will throw if the default constructor will throw. This is currently the case with the Dinkumware implementation of node based containers (e.g. std::list) though this section doesn't refer to standard types.

For throwing move constructors it seem that the implementation should have no problems to provide the basic guarantee instead of the strong one. It's better to allow throwing move constructors with basic guarantee than to disallow it silently (compile and run), via undefined behavior.

There might still be cases in which the relaxation will break existing generic code that assumes the strong guarantee but it's broken either way given a throwing move constructor since this is not a preserving optimization.

[ Batavia (2009-05): ]

Bjarne comments (referring to his draft paper): "I believe that my suggestion simply solves that. Thus, we don't need a throwing move."

Move to Open and recommend it be deferred until after the next Committee Draft is issued.

[ 2009-10 Santa Cruz: ]

Should wait to get direction from Dave/Rani (N2983).

[ 2010-03-28 Daniel updated wording to sync with N3092. ]

The suggested change of 23.3.2.3 [deque.modifiers]/2 should be removed, because the current wording does say more general things:

2 Remarks: If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T there are no effects. If an exception is thrown by the move constructor of a non-CopyConstructible T, the effects are unspecified.

The suggested change of 23.3.6.2 [vector.capacity]/2 should be removed, because the current wording does say more general things:

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-CopyConstructible type, there are no effects.

Proposed resolution:

23.2.1 [container.requirements.general] paragraph 11 add footnote:

-11- Unless otherwise specified (see 23.1.4.1, 23.1.5.1, 23.2.2.3, and 23.2.6.4) all container types defined in this Clause meet the following additional requirements:

[Note: for compatibility with C++ 2003, when "no effect" is required, standard containers should not use the value_type's throwing move constructor when the contained object is by itself a standard container. -- end note]

23.2.5.1 [unord.req.except] change paragraph 2 to say:

-2- For unordered associative containers, if an exception is thrown by any operation other than the container's hash function from within an insert() function inserting a single element, the insert() function has no effect unless the exception is thrown by the contained object move constructor.

-4- For unordered associative containers, if an exception is thrown from within a rehash() function other than by the container's hash function or comparison function, the rehash() function has no effect unless the exception is thrown by the contained object move constructor.

23.3.2.3 [deque.modifiers] change paragraph 2 to say:

-2- Remarks: If an exception is thrown other than by the copy constructor, move constructor or assignment operator of T there are no effects. If an exception is thrown by push_back() or emplace_back() function, that function has no effects unless the exception is thrown by the move constructor of T.

23.3.6.2 [vector.capacity] paragraph 2 change to say:

-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, there are no effects unless the exception is thrown by the contained object move constructor.

23.3.6.2 [vector.capacity] paragraph 12 change to say:

-12- Requires: If value_type has a move constructor, that constructor shall not throw any exceptions. If an exception is thrown, there are no effects unless the exception is thrown by the contained object move constructor.

23.3.6.4 [vector.modifiers] change paragraph 1 to say:

-1- Requires: If value_type has a move constructor, that constructor shall not throw any exceptions. Remarks: If an exception is thrown by push_back() or emplace_back() function, that function has no effect unless the exception is thrown by the move constructor of T.

996. Move operation not well specified

Section: 17 [library] Status: Open Submitter: David Abrahams Opened: 2009-03-06 Last modified: 2009-05-23

View other active issues in [library].

View all other issues in [library].

View all issues with Open status.

Discussion:

There are lots of places in the standard where we talk about "the move constructor" but where we mean "the move operation," i.e. T( move( x ) ).

We also don't account for whether that operation modifies x or not, and we need to.

[ Batavia (2009-05): ]

Move to Open, pending proposed wording from Dave for further review.

Proposed resolution:


1076. unary/binary_negate need constraining and move support

Section: 20.8.9 [negators] Status: Open Submitter: Alisdair Meredith Opened: 2009-03-20 Last modified: 2010-01-31

View all issues with Open status.

Discussion:

The class templates unary/binary_negate need constraining and move support.

Ideally these classes would be deprecated, allowing unary/binary_function to also be deprecated. However, until a generic negate adaptor is introduced that can negate any Callable type, they must be supported so should be constrained. Likewise, they should be movable, and support adopting a move-only predicate type.

In order to preserve ABI compatibility, new rvalue overloads are supplied in preference to changing the existing pass-by-const-ref to pass-by-value.

Do not consider the issue of forwarding mutable lvalues at this point, although remain open to another issue on the topic.

[ 2009-05-01 Daniel adds: ]

IMO the currently proposed resolution needs some updates because it is ill-formed at several places:

  1. In concept AdaptableUnaryFunction change

    typename X::result_type;
    typename X::argument_type;
    

    to

    Returnable result_type = typename X::result_type;
    typename argument_type = typename X::argument_type;
    

    [The replacement "Returnable result_type" instead of "typename result_type" is non-editorial, but maybe you prefer that as well]

  2. In concept AdaptableBinaryFunction change

    typename X::result_type;
    typename X::first_argument_type;
    typename X::second_argument_type;
    

    to

    Returnable result_type = typename X::result_type;
    typename first_argument_type = typename X::first_argument_type;
    typename second_argument_type = typename X::second_argument_type;
    

    [The replacement "Returnable result_type" instead of "typename result_type" is non-editorial, but maybe you prefer that as well.]

  3. In class unary/binary_function

    1. I suggest to change "ReturnType" to "Returnable" in both cases.
    2. I think you want to replace the remaining occurrences of "Predicate" by "P" (in both classes in copy/move from a predicate)
  4. I think you need to change the proposed signatures of not1 and not2, because they would still remain unconstrained: To make them constrained at least a single requirement needs to be added to enable requirement implication. This could be done via a dummy ("requires True<true>") or just explicit as follows:

    1. template <AdaptableUnaryFunction P>
      requires Predicate< P, P::argument_type>
      unary_negate<P> not1(const P&& pred);
      template <AdaptableUnaryFunction P>
      requires Predicate< P, P::argument_type >
      unary_negate<P> not1(P&& pred);
      
      -3- Returns: unary_negate<P>(pred).

      [Don't we want a move call for the second overload as in

      unary_negate<P>(std::move(pred))
      

      in the Returns clause ?]

    2. template <AdaptableBinaryFunction P>
      requires Predicate< P, P::first_argument_type, P::second_argument_type >
      binary_negate<P> not2(const P& pred);
      template <AdaptableBinaryFunction P>
      requires Predicate< P, P::first_argument_type, P::second_argument_type >
      binary_negate<P> not2(P&& pred);
      

      -5- Returns: binary_negate<P>(pred).

      [Don't we want a move call for the second overload as in

      binary_negate<P>(std::move(pred))
      

      in the Returns clause ?]

[ Batavia (2009-05): ]

There is concern that complicating the solution to preserve the ABI seems unnecessary, since we're not in general preserving the ABI.

We would prefer a separate paper consolidating all Clause 20 issues that are for the purpose of providing constrained versions of the existing facilities.

Move to Open.

[ 2009-10 post-Santa Cruz: ]

Leave open pending the potential move constructor paper. Note that we consider the "constraining" part NAD Concepts.

[ 2010-01-31 Alisdair removes the current proposed wording from the proposed wording section because it is based on concepts. That wording is proposed here: ]

Add new concepts where appropriate::

auto concept AdaptableUnaryFunction< typename X > {
  typename X::result_type;
  typename X::argument_type;
}

auto concept AdaptableBinaryFunction< typename X > {
  typename X::result_type;
  typename X::first_argument_type;
  typename X::second_argument_type;
}

Revise as follows:

Base 20.8.3 [base] (Only change is constrained Result)

-1- The following classes are provided to simplify the typedefs of the argument and result types:

namespace std {
  template <class Arg, class ReturnType Result>
  struct unary_function {
     typedef Arg    argument_type;
     typedef Result result_type;
  };

  template <class Arg1, class Arg2, class ReturnType Result>
  struct binary_function {
     typedef Arg1   first_argument_type;
     typedef Arg2   second_argument_type;
     typedef Result result_type;
  };
}

Negators 20.8.9 [negators]:

-1- Negators not1 and not2 take a unary and a binary predicate, respectively, and return their complements (5.3.1).

template <class AdaptableUnaryFunction Predicate>
  requires Predicate< P, P::argument_type >
  class unary_negate
    : public unary_function<typename Predicate::argument_type,bool> {
  public:
    unary_negate(const unary_negate & ) = default;
    unary_negate(unary_negate && );

    requires CopyConstructible< P >
       explicit unary_negate(const Predicate& pred); 
    requires MoveConstructible< P >
       explicit unary_negate(Predicate && pred);

    bool operator()(const typename Predicate::argument_type& x) const;
  };
-2 operator() returns !pred(x).
template <class Predicate>
  unary_negate<Predicate> not1(const Predicate&amp; pred);
template <class Predicate>
  unary_negate<Predicate> not1(Predicate&& pred);
-3- Returns: unary_negate<Predicate>(pred).
template <class AdaptableBinaryFunction Predicate >
  requires Predicate< P, P::first_argument_type, P::second_argument_type >
  class binary_negate
    : public binary_function<typename Predicate::first_argument_type,
                              typename Predicate::second_argument_type, bool> {
  public:
    biary_negate(const binary_negate & ) = default;
    binary_negate(binary_negate && );

    requires CopyConstructible< P >
       explicit binary_negate(const Predicate& pred);
    requires MoveConstructible< P >
       explicit binary_negate(const Predicate& pred);

    bool operator()(const typename Predicate::first_argument_type& x,
                    const typename Predicate::second_argument_type& y) const;
  };
-4- operator() returns !pred(x,y).
template <class Predicate>
  binary_negate<Predicate> not2(const Predicate& pred);
template <class Predicate>
  binary_negate<Predicate> not2(Predicate&& pred);
-5- Returns: binary_negate<Predicate>(pred).

Proposed resolution:


1118. tuple query APIs do not support cv-qualification

Section: 20.4.2.5 [tuple.helper] Status: Open Submitter: Alisdair Meredith Opened: 2009-05-23 Last modified: 2010-03-28

View other active issues in [tuple.helper].

View all other issues in [tuple.helper].

View all issues with Open status.

Discussion:

The APIs tuple_size and tuple_element do not support cv-qualified tuples, pairs or arrays.

The most generic solution would be to supply partial specializations once for each cv-type in the tuple header. However, requiring this header for cv-qualified pairs/arrays seems unhelpful. The BSI editorial suggestion (UK-198/US-69, N2533) to merge tuple into <utility> would help with pair, but not array. That might be resolved by making a dependency between the <array> header and <utility>, or simply recognising the dependency be fulfilled in a Remark.

[ 2009-05-24 Daniel adds: ]

All tuple_size templates with a base class need to derive publicly, e.g.

template <IdentityOf T> class tuple_size< const T > :
   public tuple_size<T> {};

The same applies to the tuple_element class hierarchies.

What is actually meant with the comment

this solution relies on 'metafunction forwarding' to inherit the nested typename type

?

I ask, because all base classes are currently unconstrained and their instantiation is invalid in the constrained context of the tuple_element partial template specializations.

[ 2009-05-24 Alisdair adds: ]

I think a better solution might be to ask Pete editorially to change all declarations of tupling APIs to use the struct specifier instead of class.

"metafunction forwarding" refers to the MPL metafunction protocol, where a metafunction result is declared as a nested typedef with the name "type", allowing metafunctions to be chained by means of inheritance. It is a neater syntax than repeatedly declaring a typedef, and inheritance syntax is slightly nicer when it comes to additional typename keywords.

The constrained template with an unconstrained base is a good observation though.

[ 2009-10 post-Santa Cruz: ]

Move to Open, Alisdair to provide wording. Once wording is provided, Howard will move to Review.

[ 2010-03-28 Daniel deconceptified wording. ]

Proposed resolution:

Add to 20.4.1 [tuple.general] p2 (Header <tuple> synopsis)

// 20.4.2.5, tuple helper classes:
template <class T> class tuple_size; // undefined
template <class T> class tuple_size<const T> : public tuple_size<T> {};
template <class T> class tuple_size<volatile T> : public tuple_size<T> {};
template <class T> class tuple_size<const volatile T> : public tuple_size<T> {};
template <class... Types> class tuple_size<tuple<Types...> >;

template <size_t I, class T> class tuple_element; // undefined
template <size_t I, class T> class tuple_element<I, const T>;
template <size_t I, class T> class tuple_element<I, volatile T>;
template <size_t I, class T> class tuple_element<I, const volatile T>;
template <size_t I, class... Types> class tuple_element<I, tuple<Types...> >;

Add to 20.4.2.5 [tuple.helper]

template <class... Types>
class tuple_size<tuple<Types...> >
  : public integral_constant<size_t, sizeof...(Types)> { };

template <size_t I, class... Types>
class tuple_element<I, tuple<Types...> > {
public:
  typedef TI type;
};

template <size_t I, class T>
  class tuple_element<I, const T> : public add_const<tuple_element<I, T>> {};
template <size_t I, class T>
  class tuple_element<I, volatile T> : public add_volatile<tuple_element<I, T>> {};
template <size_t I, class T>
  class tuple_element<I, const volatile T> : public add_cv<tuple_element<I, T>> {};

1119. tuple query APIs do not support references

Section: 20.4.2.5 [tuple.helper] Status: Open Submitter: Alisdair Meredith Opened: 2009-05-23 Last modified: 2009-10-26

View other active issues in [tuple.helper].

View all other issues in [tuple.helper].

View all issues with Open status.

Discussion:

The tuple query APIs tuple_size and tuple_element do not support references-to-tuples. This can be annoying when a template deduced a parameter type to be a reference, which must be explicitly stripped with remove_reference before calling these APIs.

I am not proposing a resolution at this point, as there is a combinatorial explosion with lvalue/rvalue references and cv-qualification (see previous issue) that suggests some higher refactoring is in order. This might be something to kick back over to Core/Evolution.

Note that we have the same problem in numeric_limits.

[ 2009-10 post-Santa Cruz: ]

Move to Open. Alisdair to provide wording.

Proposed resolution:


1169. num_get not fully compatible with strto*

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

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

View all issues with New 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.

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.


1171. duration types should be literal

Section: 20.10.3 [time.duration] Status: Open Submitter: Alisdair Meredith Opened: 2009-07-06 Last modified: 2010-03-27

View all other issues in [time.duration].

View all issues with Open status.

Discussion:

The duration types in 20.10.3 [time.duration] are exactly the sort of type that should be "literal types" in the new standard. Likewise, arithmetic operations on durations should be declared constexpr.

[ 2009-09-21 Daniel adds: ]

An alternative (and possibly preferable solution for potentially heap-allocating big_int representation types) would be to ask the core language to allow references to const literal types as feasible arguments for constexpr functions.

[ 2009-10-30 Alisdair adds: ]

I suggest this issue moves from New to Open.

Half of this issue was dealt with in paper n2994 on constexpr constructors.

The other half (duration arithmetic) is on hold pending Core support for const & in constexpr functions.

[ 2010-03-15 Alisdair updated wording to be consistent with N3078. ]

Proposed resolution:

Add constexpr to declaration of following functions and constructors:

Modify p1 20.10 [time], and the prototype definitions in 20.10.3.5 [time.duration.nonmember], 20.10.3.6 [time.duration.comparisons], and 20.10.3.7 [time.duration.cast]:

Header <chrono> synopsis

// duration arithmetic
template <class Rep1, class Period1, class Rep2, class Period2>
   typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type
   constexpr operator+(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
template <class Rep1, class Period1, class Rep2, class Period2>
   typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type
   constexpr operator-(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
template <class Rep1, class Period, class Rep2>
   duration<typename common_type<Rep1, Rep2>::type, Period>
   constexpr operator*(const duration<Rep1, Period>& d, const Rep2& s);
template <class Rep1, class Period, class Rep2>
   duration<typename common_type<Rep1, Rep2>::type, Period>
   constexpr operator*(const Rep1& s, const duration<Rep2, Period>& d);
template <class Rep1, class Period, class Rep2>
   duration<typename common_type<Rep1, Rep2>::type, Period>
   constexpr operator/(const duration<Rep1, Period>& d, const Rep2& s);
template <class Rep1, class Period1, class Rep2, class Period2>
   typename common_type<Rep1, Rep2>::type
   constexpr operator/(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);

// duration comparisons
template <class Rep1, class Period1, class Rep2, class Period2>
   constexpr bool operator==(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
template <class Rep1, class Period1, class Rep2, class Period2>
   constexpr bool operator!=(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
template <class Rep1, class Period1, class Rep2, class Period2>
   constexpr bool operator< (const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
template <class Rep1, class Period1, class Rep2, class Period2>
   constexpr bool operator<=(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
template <class Rep1, class Period1, class Rep2, class Period2>
   constexpr bool operator> (const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
template <class Rep1, class Period1, class Rep2, class Period2>
   constexpr bool operator>=(const  duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);

// duration_cast
template <class ToDuration, class Rep, class Period>
   constexpr ToDuration duration_cast(const duration<Rep, Period>& d);

Change 20.10.3 [time.duration]:

template <class Rep, class Period = ratio<1>>
class duration {
  ...
public:
  ...
  constexpr duration(const duration&) = default;
  ...

};

[ Note - this edit already seems assumed by definition of the duration static members zero/min/max. They cannot meaningfully be constexpr without this change. ]


1173. "Equivalence" wishy-washiness

Section: 17 [library] Status: Open Submitter: David Abrahams Opened: 2009-07-14 Last modified: 2009-10-20

View other active issues in [library].

View all other issues in [library].

View all issues with Open status.

Discussion:

Issue: The CopyConstructible requirements are wishy-washy. It requires that the copy is "equivalent" to the original, but "equivalent" is never defined.

I believe this to be an example of a more general lack of rigor around copy and assignment, although I haven't done the research to dig up all the instances.

It's a problem because if you don't know what CopyConstructible means, you also don't know what it means to copy a pair of CopyConstructible types. It doesn't prevent us from writing code, but it is a hole in our ability to understand the meaning of copy.

Furthermore, I'm pretty sure that vector's copy constructor doesn't require the elements to be EqualityComparable, so that table is actually referring to some ill-defined notion of equivalence when it uses ==.

[ 2009 Santa Cruz: ]

Move to "Open". Dave is right that this is a big issue. Paper D2987 ("Defining Move Special Member Functions", Bjarne Stroustrup and Lawrence Crowl) touches on this but does not solve it. This issue is discussed in Elements of Programming.

Proposed resolution:


1175. unordered complexity

Section: 23.2.5 [unord.req] Status: New Submitter: Pablo Halpern Opened: 2009-07-17 Last modified: 2009-07-19

View other active issues in [unord.req].

View all other issues in [unord.req].

View all issues with New 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.

Proposed resolution:


1181. Invalid sub_match comparison operators

Section: 28.9.2 [re.submatch.op] Status: New Submitter: Daniel Krügler Opened: 2009-07-25 Last modified: 2010-03-27

View all other issues in [re.submatch.op].

View all issues with New status.

Discussion:

Several heterogeneous comparison operators of class template sub_match are specified by return clauses that are not valid in general. E.g. 28.9.2 [re.submatch.op]/7:

template <class BiIter, class ST, class SA>
bool operator==(
  const basic_string<
    typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
  const sub_match<BiIter>& rhs);
Returns: lhs == rhs.str().

The returns clause would be ill-formed for all cases where ST != std::char_traits<iterator_traits<BiIter>::value_type> or SA != std::allocator<iterator_traits<BiIter>::value_type>.

The generic character of the comparison was intended, so there are basically two approaches to fix the problem: The first one would define the semantics of the comparison using the traits class ST (The semantic of basic_string::compare is defined in terms of the compare function of the corresponding traits class), the second one would define the semantics of the comparison using the traits class

std::char_traits<iterator_traits<BiIter>::value_type>

which is essentially identical to

std::char_traits<sub_match<BiIter>::value_type>

I suggest to follow the second approach, because this emphasizes the central role of the sub_match object as part of the comparison and would also make sure that a sub_match comparison using some basic_string<char_t, ..> always is equivalent to a corresponding comparison with a string literal because of the existence of further overloads (beginning from 28.9.2 [re.submatch.op]/19). If users really want to take advantage of their own traits::compare, they can simply write a corresponding compare function that does so.

Proposed resolution:

  1. In 28.9.2 [re.submatch.op] change as indicated:

    template <class BiIter, class ST, class SA>
      bool operator==(
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
        const sub_match<BiIter>& rhs);
    
    7 Returns: lhstypename sub_match<BiIter>::string_type(lhs.begin(), lhs.end()) == rhs.str().
    template <class BiIter, class ST, class SA>
      bool operator!=(
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
        const sub_match<BiIter>& rhs);
    
    8 Returns: !(lhs == rhs)lhs != rhs.str().
    template <class BiIter, class ST, class SA>
      bool operator<(
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
        const sub_match<BiIter>& rhs);
    
    9 Returns: lhstypename sub_match<BiIter>::string_type(lhs.begin(), lhs.end()) < rhs.str().
    template <class BiIter, class ST, class SA>
      bool operator>(
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
        const sub_match<BiIter>& rhs);
    
    10 Returns: rhs < lhslhs > rhs.str().
    template <class BiIter, class ST, class SA>
      bool operator>=(
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
        const sub_match<BiIter>& rhs);
    
    11 Returns: !(lhs < rhs)lhs >= rhs.str().
    template <class BiIter, class ST, class SA>
      bool operator<=(
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& lhs,
        const sub_match<BiIter>& rhs);
    
    12 Returns: !(rhs < lhs)lhs <= rhs.str().
    template <class BiIter, class ST, class SA>
      bool operator==(const sub_match<BiIter>& lhs,
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
    
    13 Returns: lhs.str() == rhstypename sub_match<BiIter>::string_type(rhs.begin(), rhs.end()).
    template <class BiIter, class ST, class SA>
      bool operator!=(const sub_match<BiIter>& lhs,
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
    
    14 Returns: !(lhs == rhs)lhs.str() != rhs.
    template <class BiIter, class ST, class SA>
      bool operator<(const sub_match<BiIter>& lhs,
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
    
    15 Returns: lhs.str() < rhstypename sub_match<BiIter>::string_type(rhs.begin(), rhs.end()).
    template <class BiIter, class ST, class SA>
      bool operator>(const sub_match<BiIter>& lhs,
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
    
    16 Returns: rhs < lhslhs.str() > rhs.
    template <class BiIter, class ST, class SA>
      bool operator>=(const sub_match<BiIter>& lhs,
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
    
    17 Returns: !(lhs < rhs)lhs.str() >= rhs.
    template <class BiIter, class ST, class SA>
      bool operator<=(const sub_match<BiIter>& lhs,
        const basic_string<
          typename iterator_traits<BiIter>::value_type, ST, SA>& rhs);
    
    18 Returns: !(rhs < lhs)lhs.str() <= rhs.

1183. basic_ios::set_rdbuf may break class invariants

Section: 27.5.4.2 [basic.ios.members] Status: Open Submitter: Daniel Krügler Opened: 2009-07-28 Last modified: 2009-10-22

View all other issues in [basic.ios.members].

View all issues with Open status.

Discussion:

The protected member function set_rdbuf had been added during the process of adding move and swap semantics to IO classes. A relevant property of this function is described by it's effects in 27.5.4.2 [basic.ios.members]/19:

Effects: Associates the basic_streambuf object pointed to by sb with this stream without calling clear().

This means that implementors of or those who derive from existing IO classes could cause an internal state where the stream buffer could be 0, but the IO class has the state good(). This would break several currently existing implementations which rely on the fact that setting a stream buffer via the currently only ways, i.e. either by calling

void init(basic_streambuf<charT,traits>* sb);

or by calling

basic_streambuf<charT,traits>* rdbuf(basic_streambuf<charT,traits>* sb);

to set rdstate() to badbit, if the buffer is 0. This has the effect that many internal functions can simply check rdstate() instead of rdbuf() for being 0.

I therefore suggest that a requirement is added for callers of set_rdbuf to set a non-0 value.

[ 2009-10 Santa Cruz: ]

Moved to Open. Martin volunteers to provide new wording, where set_rdbuf() sets the badbit but does not cause an exception to be thrown like a call to clear() would.

[ 2009-10-20 Martin provides wording: ]

Proposed resolution:

Change 27.5.4.2 [basic.ios.members] around p. 19 as indicated:

void set_rdbuf(basic_streambuf<charT, traits>* sb);

Effects: Associates the basic_streambuf object pointed to by sb with this stream without calling clear(). Postconditions: rdbuf() == sb.

Effects: As if:


iostate state = rdstate();
try { rdbuf(sb); }
catch(ios_base::failure) {
   if (0 == (state & ios_base::badbit))
       unsetf(badbit);
}

Throws: Nothing.

Rationale:

We need to be able to call set_rdbuf() on stream objects for which (rdbuf() == 0) holds without causing ios_base::failure to be thrown. We also don't want badbit to be set as a result of setting rdbuf() to 0 if it wasn't set before the call. This changed Effects clause maintains the current behavior (as of N2914) without requiring that sb be non-null.

1187. std::decay

Section: 20.7.6.6 [meta.trans.other] Status: Ready Submitter: Jason Merrill Opened: 2009-08-07 Last modified: 2010-03-15

View all other issues in [meta.trans.other].

View all issues with Ready status.

Discussion:

I notice that std::decay is specified to strip the cv-quals from anything but an array or pointer. This seems incorrect for values of class type, since class rvalues can have cv-qualified type (3.10 [basic.lval]/9).

[ 2009-08-09 Howard adds: ]

See the thread starting with c++std-lib-24568 for further discussion. And here is a convenience link to the original proposal. Also see the closely related issue 705.

[ 2010 Pittsburgh: Moved to Ready. ]

Proposed resolution:

Add a note to decay in 20.7.6.6 [meta.trans.other]:

[Note: This behavior is similar to the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) conversions applied when an lvalue expression is used as an rvalue, but also strips cv-qualifiers from class types in order to more closely model by-value argument passing. — end note]

1188. Unordered containers should have a minimum load factor as well as a maximum

Section: 23.2.5 [unord.req], 23.5 [unord] Status: New Submitter: Matt Austern Opened: 2009-08-10 Last modified: 2009-08-11

View other active issues in [unord.req].

View all other issues in [unord.req].

View all issues with New status.

Discussion:

Unordered associative containers have a notion of a maximum load factor: when the number of elements grows large enough, the containers automatically perform a rehash so that the number of elements per bucket stays below a user-specified bound. This ensures that the hash table's performance characteristics don't change dramatically as the size increases.

For similar reasons, Google has found it useful to specify a minimum load factor: when the number of elements shrinks by a large enough, the containers automatically perform a rehash so that the number of elements per bucket stays above a user-specified bound. This is useful for two reasons. First, it prevents wasting a lot of memory when an unordered associative container grows temporarily. Second, it prevents amortized iteration time from being arbitrarily large; consider the case of a hash table with a billion buckets and only one element. (This was discussed even before TR1 was published; it was TR issue 6.13, which the LWG closed as NAD on the grounds that it was a known design feature. However, the LWG did not consider the approach of a minimum load factor.)

The only interesting question is when shrinking is allowed. In principle the cleanest solution would be shrinking on erase, just as we grow on insert. However, that would be a usability problem; it would break a number of common idioms involving erase. Instead, Google's hash tables only shrink on insert and rehash.

The proposed resolution allows, but does not require, shrinking in rehash, mostly because a postcondition for rehash that involves the minimum load factor would be fairly complicated. (It would probably have to involve a number of special cases and it would probably have to mention yet another parameter, a minimum bucket count.)

The current behavior is equivalent to a minimum load factor of 0. If we specify that 0 is the default, this change will have no impact on backward compatibility.

Proposed resolution:

Add two new rows, and change rehash's postcondition in the unordered associative container requirements table in 23.2.5 [unord.req]:

Table 87 — Unordered associative container requirements (in addition to container)
ExpressionReturn typeAssertion/note pre-/post-condition Complexity
a.min_load_factor() float Returns a non-negative number that the container attempts to keep the load factor greater than or equal to. The container automatically decreases the number of buckets as necessary to keep the load factor above this number. constant
a.min_load_factor(z) void Pre: z shall be non-negative. Changes the container's minimum load factor, using z as a hint. [Footnote: the minimum load factor should be significantly smaller than the maximum. If z is too large, the implementation may reduce it to a more sensible value.] constant
a.rehash(n) void Post: a.bucket_count() >= n, and a.size() <= a.bucket_count() * a.max_load_factor(). [Footnote: It is intentional that the postcondition does not mention the minimum load factor. This member function is primarily intended for cases where the user knows that the container's size will increase soon, in which case the container's load factor will temporarily fall below a.min_load_factor().] a.bucket_cout > a.size() / a.max_load_factor() and a.bucket_count() >= n. Average case linear in a.size(), worst case quadratic.

Add a footnote to 23.2.5 [unord.req] p12:

The insert members shall not affect the validity of references to container elements, but may invalidate all iterators to the container. The erase members shall invalidate only iterators and references to the erased elements.

[A consequence of these requirements is that while insert may change the number of buckets, erase may not. The number of buckets may be reduced on calls to insert or rehash.]

Change paragraph 13:

The insert members shall not affect the validity of iterators if (N+n) < z * B zmin * B <= (N+n) <= zmax * B, where N is the number of elements in the container prior to the insert operation, n is the number of elements inserted, B is the container's bucket count, zmin is the container's minimum load factor, and zmax is the container's maximum load factor.

Add to the unordered_map class synopsis in section 23.5.1 [unord.map], the unordered_multimap class synopsis in 23.5.2 [unord.multimap], the unordered_set class synopsis in 23.5.3 [unord.set], and the unordered_multiset class synopsis in 23.5.4 [unord.multiset]:


float min_load_factor() const;
void min_load_factor(float z);

In 23.5.1.1 [unord.map.cnstr], 23.5.2.1 [unord.multimap.cnstr], 23.5.3.1 [unord.set.cnstr], and 23.5.4.1 [unord.multiset.cnstr], change:

... max_load_factor() returns 1.0 and min_load_factor() returns 0.

1190. Setting the maximum load factor should return the previous value

Section: 23.2.5 [unord.req], 23.5 [unord] Status: New Submitter: Matt Austern Opened: 2009-08-10 Last modified: 2009-08-11

View other active issues in [unord.req].

View all other issues in [unord.req].

View all issues with New status.

Discussion:

The unordered associative container requirements table specifies that a.set_max_load_factor(z) has return type void. However, there is a useful piece of information to return: the previous value. Users who don't need it can always ignore it.

Proposed resolution:

In the unordered associative container requirements table, change:

Table 87 — Unordered associative container requirements (in addition to container)
ExpressionReturn typeAssertion/note pre-/post-condition Complexity
a.max_load_factor(z) void float Pre: z shall be positive. Changes the container's maximum load load factor, using z as a hint. Returns: the previous value of a.max_load_factor(). constant

Change the return type of set_max_load_factor in the class synopses in 23.5.1 [unord.map], 23.5.2 [unord.multimap], 23.5.3 [unord.set], and 23.5.4 [unord.multiset].

If issue 1188 is also accepted, make the same changes for min_load_factor.


1191. tuple get API should respect rvalues

Section: 20.4.2.6 [tuple.elem] Status: Open Submitter: Alisdair Meredith Opened: 2009-08-18 Last modified: 2009-10-31

View all issues with Open status.

Discussion:

The tuple get API should respect rvalues. This would allow for moving a single element out of a tuple-like type.

[ 2009-10-30 Alisdair adds: ]

The issue of rvalue overloads of get for tuple-like types was briefly discussed in Santa Cruz.

The feedback was this would be welcome, but we need full wording for the other types (pair and array) before advancing.

I suggest the issue moves to Open from New as it has been considered, feedback given, and it has not (yet) been rejected as NAD.

Proposed resolution:

Add the following signature to p2 20.4.1 [tuple.general]


template <size_t I, class ... Types>
typename tuple_element<I, tuple<Types...> >::type&& get(tuple<Types...> &&);

And again to 20.4.2.6 [tuple.elem].


template <size_t I, class ... Types>
typename tuple_element<I, tuple<Types...> >::type&& get(tuple<Types...>&& t);

Effects: Equivalent to return std::forward<typename tuple_element<I, tuple<Types...> >::type&&>(get<I>(t));

[Note: If a T in Types is some reference type X&, the return type is X&, not X&&. However, if the element type is non-reference type T, the return type is T&&. — end note]

Add the following signature to p1 20.3 [utility]


template <size_t I, class T1, class T2>
typename tuple_element<I, pair<T1,T2> >::type&& get(pair<T1, T2>&&);

And to p5 20.3.5.3 [pair.astuple]


template <size_t I, class T1, class T2>
typename tuple_element<I, pair<T1,T2> >::type&& get(pair<T1, T2>&& p);

Returns: If I == 0 returns std::forward<T1&&>(p.first); if I == 1 returns std::forward<T2&&>(p.second); otherwise the program is ill-formed.

Throws: Nothing.

Add the following signature to 23.3 [sequences] <array> synopsis

template <size_t I, class T, size_t N>
T&& get(array<T,N> &&);

And after p8 23.3.1.8 [array.tuple]

template <size_t I, class T, size_t N>
T&& get(array<T,N> && a);
Effects: Equivalent to return std::move(get<I>(a));

1198. Container adaptor swap: member or non-member?

Section: 23.3.5 [container.adaptors] Status: New Submitter: Pablo Halpern Opened: 2009-08-26 Last modified: 2010-03-28

View all other issues in [container.adaptors].

View all issues with New status.

Discussion:

Under 23.3.5 [container.adaptors] of N2914 the member function of swap of queue and stack call:

swap(c, q.c);

But under 23.3.5 [container.adaptors] of N2723 these members are specified to call:

c.swap(q.c);

Neither draft specifies the semantics of member swap for priority_queue though it is declared.

Although the distinction between member swap and non-member swap is not important when these adaptors are adapting standard containers, it may be important for user-defined containers.

We (Pablo and Howard) feel that it is more likely for a user-defined container to support a namespace scope swap than a member swap, and therefore these adaptors should use the container's namespace scope swap.

[ 2009-09-30 Daniel adds: ]

The outcome of this issue should be considered with the outcome of 774 both in style and in content (e.g. 774 bullet 9 suggests to define the semantic of void priority_queue::swap(priority_queue&) in terms of the member swap of the container).

[ 2010-03-28 Daniel update to diff against N3092. ]

Proposed resolution:

Change 23.3.5.1.1 [queue.defn]:

template <class T, class Container = deque<T> > 
class queue {
   ...
   void swap(queue& q) { using std::swap;
                          c.swap(c, q.c); }
   ...
};

Change 23.3.5.2 [priority.queue]:

template <class T, class Container = vector<T>, 
          class Compare = less<typename Container::value_type> > 
class priority_queue { 
    ...
    void swap(priority_queue& q); { using std::swap;
                                     swap(c, q.c);
                                     swap(comp, q.comp); }
    ...
};

Change 23.3.5.3.1 [stack.defn]:

template <class T, class Container = deque<T> > 
class stack {
   ...
   void swap(stack& s) { using std::swap;
                          c.swap(c, s.c); }
   ...
};

1200. "surprising" char_traits<T>::int_type requirements

Section: 21.2.2 [char.traits.typedefs] Status: New Submitter: Sean Hunt Opened: 2009-09-03 Last modified: 2009-10-28

View all other issues in [char.traits.typedefs].

View all issues with New status.

Discussion:

The footnote for int_type in 21.2.2 [char.traits.typedefs] says that

If eof() can be held in char_type then some iostreams implementations may give surprising results.

This implies that int_type should be a superset of char_type. However, the requirements for char16_t and char32_t define int_type to be equal to int_least16_t and int_least32_t respectively. int_least16_t is likely to be the same size as char_16_t, which may lead to surprising behavior, even if eof() is not a valid UTF-16 code unit. The standard should not prescribe surprising behavior, especially without saying what it is (it's apparently not undefined, just surprising). The same applies for 32-bit types.

I personally recommend that behavior be undefined if eof() is a member of char_type, and another type be chosen for int_type (my personal favorite has always been a struct {bool eof; char_type c;}). Alternatively, the exact results of such a situation should be defined, at least so far that I/O could be conducted on these types as long as the code units remain valid. Note that the argument that no one streams char16_t or char32_t is not really valid as it would be perfectly reasonable to use a basic_stringstream in conjunction with UTF character types.

[ 2009-10-28 Ganesh provides two possible resolutions and expresses a preference for the second: ]

  1. Replace 21.2.3.2 [char.traits.specializations.char16_t] para 3 with:

    The member eof() shall return an implementation-defined constant that cannot appear as a valid UTF-16 code unit UINT_LEAST16_MAX [Note: this value is guaranteed to be a permanently reserved UCS-2 code position if UINT_LEAST16_MAX == 0xFFFF and it's not a UCS-2 code position otherwise — end note].

    Replace 21.2.3.3 [char.traits.specializations.char32_t] para 3 with:

    The member eof() shall return an implementation-defined constant that cannot appear as a Unicode code point UINT_LEAST32_MAX [Note: this value is guaranteed to be a permanently reserved UCS-4 code position if UINT_LEAST32_MAX == 0xFFFFFFFF and it's not a UCS-4 code position otherwise — end note].
  2. In 21.2.3.2 [char.traits.specializations.char16_t], in the definition of char_traits<char16_t> replace the definition of nested typedef int_type with:

    namespace std {
      template<> struct char_traits<char16_t> {
        typedef char16_t         char_type;
        typedef uint_least16_t uint_fast16_t int_type;
         ...
    

    Replace 21.2.3.2 [char.traits.specializations.char16_t] para 3 with:

    The member eof() shall return an implementation-defined constant that cannot appear as a valid UTF-16 code unit UINT_FAST16_MAX [Note: this value is guaranteed to be a permanently reserved UCS-2 code position if UINT_FAST16_MAX == 0xFFFF and it's not a UCS-2 code position otherwise — end note].

    In 21.2.3.3 [char.traits.specializations.char32_t], in the definition of char_traits<char32_t> replace the definition of nested typedef int_type with:

    namespace std {
      template<> struct char_traits<char32_t> {
        typedef char32_t         char_type;
        typedef uint_least32_t uint_fast32_t int_type;
         ...
    

    Replace 21.2.3.3 [char.traits.specializations.char32_t] para 3 with:

    The member eof() shall return an implementation-defined constant that cannot appear as a Unicode code point UINT_FAST32_MAX [Note: this value is guaranteed to be a permanently reserved UCS-4 code position if UINT_FAST32_MAX == 0xFFFFFFFF and it's not a UCS-4 code position otherwise — end note].

Proposed resolution:


1206. Incorrect requires for move_backward and copy_backward

Section: 25.3.2 [alg.move] Status: Ready Submitter: Howard Hinnant Opened: 2009-09-13 Last modified: 2010-03-15

View all issues with Ready status.

Discussion:

25.3.2 [alg.move], p6 says:

template<class BidirectionalIterator1, class BidirectionalIterator2>
  BidirectionalIterator2
    move_backward(BidirectionalIterator1 first,
                  BidirectionalIterator1 last,
                  BidirectionalIterator2 result);

...

Requires: result shall not be in the range [first,last).

This is essentially an "off-by-one" error.

When result == last, which is allowed by this specification, then the range [first, last) is being move assigned into the range [first, last). The move (forward) algorithm doesn't allow self move assignment, and neither should move_backward. So last should be included in the range which result can not be in.

Conversely, when result == first, which is not allowed by this specification, then the range [first, last) is being move assigned into the range [first - (last-first), first). I.e. into a non-overlapping range. Therefore first should not be included in the range which result can not be in.

The same argument applies to copy_backward though copy assigning elements to themselves (result == last) should be harmless (though is disallowed by copy).

[ 2010 Pittsburgh: Moved to Ready. ]

Proposed resolution:

Change 25.3.2 [alg.move], p6:

template<class BidirectionalIterator1, class BidirectionalIterator2>
  BidirectionalIterator2
    move_backward(BidirectionalIterator1 first,
                  BidirectionalIterator1 last,
                  BidirectionalIterator2 result);

...

Requires: result shall not be in the range [(first,last]).

Change 25.3.1 [alg.copy], p13:

template<class BidirectionalIterator1, class BidirectionalIterator2>
  BidirectionalIterator2
    copy_backward(BidirectionalIterator1 first,
                  BidirectionalIterator1 last,
                  BidirectionalIterator2 result);

...

Requires: result shall not be in the range [(first,last]).


1207. Underspecified std::list operations?

Section: 23.3.4.4 [list.ops] Status: Open Submitter: Loïc Joly Opened: 2009-09-13 Last modified: 2010-03-28

View other active issues in [list.ops].

View all other issues in [list.ops].

View all issues with Open status.

Discussion:

It looks to me like some operations of std::list (sort, reverse, remove, unique & merge) do not specify the validity of iterators, pointers & references to elements of the list after those operations. Is it implied by some other text in the standard?

I believe sort & reverse do not invalidating anything, remove & unique only invalidates what refers to erased elements, merge does not invalidate anything (with the same precision as splice for elements who changed of container). Are those assumptions correct ?

[ 2009-12-08 Jonathan Wakely adds: ]

23.2.1 [container.requirements.general] paragraph 11 says iterators aren't invalidated unless specified, so I don't think it needs to be repeated on every function that doesn't invalidate iterators. list::unique says it "eliminates" elements, that should probably be "erases" because IMHO that term is used elsewhere and so makes it clearer that iterators to the erased elements are invalidated.

list::merge coud use the same wording as list::splice w.r.t iterators and references to moved elements.

Suggested resolution:

In 23.3.4.4 [list.ops] change paragraph 19

                                 void unique();
template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);
Effects: Eliminates Erases all but the first element from every consecutive group ...

Add to the end of paragraph 23

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

...

Effects: ... that is, for every iterator i, in the range other than the first, the condition comp(*i, *(i - 1) will be false. 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.

[ 2009-12-12 Loïc adds wording. ]

[ 2010-02-10 Moved to Tentatively Ready after 5 positive votes on c++std-lib. ]

[ 2010-02-10 Alisdair opens: ]

I object to the current resolution of #1207. I believe it is overly strict with regard to list end iterators, being the only mutating operations to require such stability.

More importantly, the same edits need to be applied to forward_list, which uses slightly different words to describe some of these operations so may require subtly different edits (not checked.)

I am prepared to pick up the end() iterator as a separate (new) issue, as part of the FCD ballot review (BSI might tell me 'no' first ;~) but I do want to see forward_list adjusted at the same time.

[ 2010-03-28 Daniel adds the first 5 bullets in an attempt to address Alisdair's concerns. ]

Proposed resolution:

  1. Change 23.3.3.5 [forwardlist.ops]/12 as indicated:

    void remove(const T& value);
    template <class Predicate> void remove_if(Predicate pred);
    
    12 Effects: Erases all the elements in the list referred by a list iterator i for which the following conditions hold: *i == value (for remove()), pred(*i) is true (for remove_if()). This operation shall be stable: the relative order of the elements that are not removed is the same as their relative order in the original list. Invalidates only the iterators and references to the erased elements.
  2. Change 23.3.3.5 [forwardlist.ops]/15 as indicated:

    template <class BinaryPredicate> void unique(BinaryPredicate pred);
    
    15 Effects:: EliminatesErases all but the first element from every consecutive group of equal elements referred to by the iterator i in the range [first + 1,last) for which *i == *(i-1) (for the version with no arguments) or pred(*i, *(i - 1)) (for the version with a predicate argument) holds. Invalidates only the iterators and references to the erased elements.
  3. Change 23.3.3.5 [forwardlist.ops]/19 as indicated:

    void merge(forward_list<T,Allocator>&& x);
    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. 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.

  4. Change 23.3.3.5 [forwardlist.ops]/22 as indicated:

    void sort();
    template <class Compare> void sort(Compare comp);
    

    [..]

    22 Effects:: Sorts the list according to the operator< or the comp function object. This operation shall be stable: the relative order of the equivalent elements is preserved. If an exception is thrown the order of the elements in *this is unspecified. Does not affect the validity of iterators and references.

  5. Change 23.3.3.5 [forwardlist.ops]/24 as indicated:

    void reverse();
    
    24 Effects:: Reverses the order of the elements in the list. Does not affect the validity of iterators and references.
  6. Change 23.3.4.4 [list.ops], p15:

                               void remove(const T& value);
    template <class Predicate> void remove_if(Predicate pred);
    
    Effects: Erases all the elements in the list referred by a list iterator i for which the following conditions hold: *i == value, pred(*i) != false. Invalidates only the iterators and references to the erased elements.
  7. Change 23.3.4.4 [list.ops], p19:

                                     void unique();
    template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);
    
    Effects: Eliminates Erases all but the first element from every consecutive group of equal elements referred to by the iterator i in the range [first + 1,last) for which *i == *(i-1) (for the version of unique with no arguments) or pred(*i, *(i - 1)) (for the version of unique with a predicate argument) holds. Invalidates only the iterators and references to the erased elements.
  8. Change 23.3.4.4 [list.ops], p23:

    void                          merge(list<T,Allocator>&& x);
    template <class Compare> void merge(list<T,Allocator>&& x, Compare comp);
    
    Effects: If (&x == this) does nothing; otherwise, merges the two sorted ranges [begin(), end()) and [x.begin(), x.end()). The result is a range in which the elements will be sorted in non-decreasing order according to the ordering defined by comp; that is, for every iterator i, in the range other than the first, the condition comp(*i, *(i - 1) will be false. 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.
  9. Change 23.3.4.4 [list.ops], p26:

    void reverse();
    
    Effects: Reverses the order of the elements in the list. Does not affect the validity of iterators and references.
  10. Change 23.3.4.4 [list.ops], p30:

                             void sort();
    template <class Compare> void sort(Compare comp);
    
    Effects: Sorts the list according to the operator< or a Compare function object. Does not affect the validity of iterators and references.

1213. Meaning of valid and singular iterator underspecified

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

View all other issues in [iterator.requirements].

View all issues with New 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.9.9 [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?

Proposed resolution:


1214. Insufficient/inconsistent key immutability requirements for associative containers

Section: 23.2.4 [associative.reqmts] Status: New Submitter: Daniel Krügler Opened: 2009-09-20 Last modified: 2010-03-27

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with New 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.

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]


1215. list::merge with unequal allocators

Section: 23.3.4.4 [list.ops] Status: New Submitter: Pablo Halpern Opened: 2009-09-24 Last modified: 2009-09-24

View other active issues in [list.ops].

View all other issues in [list.ops].

View all issues with New status.

Discussion:

In Bellevue (I think), we passed N2525, which, among other things, specifies that the behavior of list::splice is undefined if the allocators of the two lists being spliced do not compare equal. The same rationale should apply to list::merge. The intent of list::merge (AFAIK) is to move nodes from one sorted list into another sorted list without copying the elements. This is possible only if the allocators compare equal.

Proposed resolution:

Relative to the August 2009 WP, N2857, change 23.3.4.4 [list.ops], paragraphs 22-25 as follows:

void merge(list&& x);
template <class Compare> void merge(list&& x, Compare comp);

Requires: both the list and the argument list shall be sorted according to operator< or comp.

Effects: If (&x == this) does nothing; otherwise, merges the two sorted ranges [begin(), end()) and [x.begin(), x.end()). The result is a range in which the elements will be sorted in non-decreasing order according to the ordering defined by comp; that is, for every iterator i, in the range other than the first, the condition comp(*i, *(i - 1)) will be false.

Remarks: 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().

Complexity: At most size() + x.size() - 1 applications of comp if (&x != this); otherwise, no applications of comp are performed. If an exception is thrown other than by a comparison there are no effects.


1234. "Do the right thing" and NULL

Section: 23.2.3 [sequence.reqmts] Status: Open Submitter: Matt Austern Opened: 2009-10-09 Last modified: 2010-03-21

View all other issues in [sequence.reqmts].

View all issues with Open status.

Discussion:

On g++ 4.2.4 (x86_64-linux-gnu), the following file gives a compile error:

#include <vector>
void foo() { std::vector<int*> v(500l, NULL); }

Is this supposed to work?

The issue: if NULL happens to be defined as 0l, this is an invocation of the constructor with two arguments of the same integral type. 23.2.3 [sequence.reqmts]/14 (N3035) says that this will behave as if the the overloaded constructor

X(size_type, const value_type& = value_type(),
  const allocator_type& = allocator_type())

were called instead, with the arguments static_cast<size_type>(first), last and alloc, respectively. However, it does not say whether this actually means invoking that constructor with the exact textual form of the arguments as supplied by the user, or whether the standard permits an implementation to invoke that constructor with variables of the same type and value as what the user passed in. In most cases this is a distinction without a difference. In this particular case it does make a difference, since one of those things is a null pointer constant and the other is not.

Note that an implementation based on forwarding functions will use the latter interpretation.

[ 2010 Pittsburgh: Moved to Open. ]

[ 2010-03-19 Daniel provides wording. ]

This issue can be considered as a refinement of 438.

Proposed resolution:

Change 23.2.3 [sequence.reqmts]/14+15 (N3035) as indicated:

14 For every sequence container defined in this Clause and in Clause 21:

15 In the previous paragraph the alternative binding will fail if first is not implicitly convertible to X::size_type or if last is not implicitly convertible to X::value_type.


1240. Deleted comparison functions of std::function not needed

Section: 20.8.14.2 [func.wrap.func] Status: New Submitter: Daniel Krügler Opened: 2009-10-18 Last modified: 2009-10-19

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

View all issues with New status.

Discussion:

The class template std::function contains the following member declarations:

// deleted overloads close possible hole in the type system
template<class R2, class... ArgTypes2>
  bool operator==(const function<R2(ArgTypes2...)>&) = delete;
template<class R2, class... ArgTypes2>
  bool operator!=(const function<R2(ArgTypes2...)>&) = delete;

The leading comment here is part of the history of std::function, which was introduced with N1402. During that time no explicit conversion functions existed, and the "safe-bool" idiom (based on pointers-to-member) was a popular technique. The only disadvantage of this idiom was that given two objects f1 and f2 of type std::function the expression

f1 == f2;

was well-formed, just because the built-in operator== for pointer to member was considered after a single user-defined conversion. To fix this, an overload set of undefined comparison functions was added, such that overload resolution would prefer those ending up in a linkage error. The new language facility of deleted functions provided a much better diagnostic mechanism to fix this issue.

The central point of this issue is, that with the replacement of the safe-bool idiom by explicit conversion to bool the original "hole in the type system" does no longer exist and therefore the comment is wrong and the superfluous function definitions should be removed as well. An explicit conversion function is considered in direct-initialization situations only, which indirectly contain the so-called "contextual conversion to bool" (4 [conv]/3). These conversions are not considered for == or != as defined by the core language.

Proposed resolution:

In 20.8.14.2 [func.wrap.func]/1, class function change as indicated:

// 20.7.15.2.3, function capacity:
explicit operator bool() const;

// deleted overloads close possible hole in the type system
template<class R2, class... ArgTypes2>
  bool operator==(const function<R2(ArgTypes2...)>&) = delete;
template<class R2, class... ArgTypes2>
  bool operator!=(const function<R2(ArgTypes2...)>&) = delete;

1249. basic_ios default ctor

Section: 27.5.4.1 [basic.ios.cons] Status: New Submitter: Martin Sebor Opened: 2009-10-25 Last modified: 2009-10-26

View all other issues in [basic.ios.cons].

View all issues with New status.

Discussion:

The basic_ios default ctor is required to leave the objects members uninitialized (see below). The paragraph says the object must be initialized by calling basic_ios::init() before it's destroyed by I can't find a requirement that it be initialized before calling any of the class other member functions. Am I not looking in the right place or that an issue?

[ 2009-10-25 Daniel adds: ]

I agree, that your wording makes that clearer, but suggest to write

... calling basic_ios::init() before ...

Doing so, I recommend to adapt that of ios_base(); as well, where we have:

Effects: Each ios_base member has an indeterminate value after construction. These members shall be initialized by calling basic_ios::init. If an ios_base object is destroyed before these initializations have taken place, the behavior is undefined.

Proposed resolution:

Change 27.5.2.7 [ios.base.cons] p1:

ios_base();
Effects: Each ios_base member has an indeterminate value after construction. These The object's members shall be initialized by calling basic_ios::init before the object's first use or before it is destroyed, whichever comes first; otherwise the behavior is undefined.. If an ios_base object is destroyed before these initializations have taken place, the behavior is undefined.

Change 27.5.4.1 [basic.ios.cons] p2:

basic_ios();
Effects: Constructs an object of class basic_ios (27.5.2.7) leaving its member objects uninitialized. The object shall be initialized by calling its basic_ios::init before its first use or before it is destroyed, whichever comes first; otherwise the behavior is undefined. member function. If it is destroyed before it has been initialized the behavior is undefined.

1252. wbuffer_convert::state_type inconsistency

Section: 22.3.3.2.3 [conversions.buffer] Status: New Submitter: Bo Persson Opened: 2009-10-21 Last modified: 2009-10-31

View all issues with New status.

Discussion:

The synopisis for wbuffer_convert 22.3.3.2.3 [conversions.buffer]/2 contains

typedef typename Tr::state_type   state_type; 

making state_type a synonym for (possibly) some char_traits<x>::state_type.

However, in paragraph 9 of the same section, we have

typedef typename Codecvt::state_type state_type;
The type shall be a synonym for Codecvt::state_type.

From what I can see, it might be hard to implement wbuffer_convert if the types were not both std::mbstate_t, but I cannot find a requirement that they must be the same type.

Proposed resolution:


1253. invalidation of iterators and emplace vs. insert inconsistence in assoc. containers

Section: 23.2.4 [associative.reqmts] Status: New Submitter: Boris Dušek Opened: 2009-10-24 Last modified: 2010-01-25

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with New status.

Discussion:

In the latest published draft N2960, section 23.2.4 [associative.reqmts], paragraph 8, it is specifies that that insert does not invalidate any iterators. As per 23.2.1 [container.requirements.general], paragraph 12, this holds true not only for insert, but emplace as well. This gives the insert member a special treatment w.r.t. emplace member in 23.2.4 [associative.reqmts], par. 8, since both modify the container. For the sake of consistency, in 23.2.4 [associative.reqmts], par. 8: either reference to insert should be removed (i.e. count on 23.2.1 [container.requirements.general], par. 12), or reference to emplace be added (i.e. mention all members of assoc. containers that modify it).

[ 2009-11-18 Chris provided wording. ]

This suggested wording covers both the issue discussed, and a number of other identical issues (namely insert being discussed without emplace). I'm happy to go back and split and introduce a new issue if appropriate, but I think the changes are fairly mechanical and obvious.

[ 2010-01-23 Daniel Krügler and J. Daniel García updated wording to make the use of hint consistent with insert. ]

Proposed resolution:

Modify bullet 1 of 23.2.1 [container.requirements.general], p10:

10 Unless otherwise specified (see 23.2.4.1, 23.2.5.1, 23.3.2.3, and 23.3.6.4) all container types defined in this Clause meet the following additional requirements:

Modify 23.2.4 [associative.reqmts], p4:

4 An associative container supports unique keys if it may contain at most one element for each key. Otherwise, it supports equivalent keys. The set and map classes support unique keys; the multiset and multimap classes support equivalent keys. For multiset and multimap, insert, emplace, and erase preserve the relative ordering of equivalent elements.

Modify Table 96 — Associative container requirements in 23.2.4 [associative.reqmts]:

Table 96 — Associative container requirements (in addition to container)
Expression Return type Assertion/note
pre-/post-condition
Complexity
...
a_eq.emplace(args) iterator inserts a T object t constructed with std::forward<Args>(args)... and returns the iterator pointing to the newly inserted element. If a range containing elements equivalent to t exists in a_eq, t is inserted at the end of that range. logarithmic
a.emplace_hint(p, args) iterator equivalent to a.emplace(std::forward<Args>(args)...). Return value is an iterator pointing to the element with the key equivalent to the newly inserted element. The const_iterator p is a hint pointing to where the search should start. The element is inserted as close as possible to the position just prior to p. Implementations are permitted to ignore the hint. logarithmic in general, but amortized constant if the element is inserted right after before p
...

Modify 23.2.4 [associative.reqmts], p8:

8 The insert and emplace members shall not affect the validity of iterators and references to the container, and the erase members shall invalidate only iterators and references to the erased elements.

Modify 23.2.4.1 [associative.reqmts.except], p2:

2 For associative containers, if an exception is thrown by any operation from within an insert() or emplace() function inserting a single element, the insert() function insertion has no effect.

Modify 23.2.5 [unord.req], p6, p12 and p13:

6 An unordered associative container supports unique keys if it may contain at most one element for each key. Otherwise, it supports equivalent keys. unordered_set and unordered_map support unique keys. unordered_multiset and unordered_multimap support equivalent keys. In containers that support equivalent keys, elements with equivalent keys are adjacent to each other. For unordered_multiset and unordered_multimap, insert, emplace, and erase preserve the relative ordering of equivalent elements.

12 The insert and emplace members shall not affect the validity of references to container elements, but may invalidate all iterators to the container. The erase members shall invalidate only iterators and references to the erased elements.

13 The insert and emplace members shall not affect the validity of iterators if (N+n) < z * B, where N is the number of elements in the container prior to the insert operation, n is the number of elements inserted, B is the container's bucket count, and z is the container's maximum load factor.

Modify 23.2.5.1 [unord.req.except], p2:

2 For unordered associative containers, if an exception is thrown by any operation other than the container's hash function from within an insert() or emplace() function inserting a single element, the insert() insertion function has no effect.

1260. is_constructible<int*,void*> reports true

Section: 20.7.4.3 [meta.unary.prop] Status: New Submitter: Peter Dimov Opened: 2009-11-07 Last modified: 2009-11-08

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

View all issues with New status.

Discussion:

The specification of is_constructible<T,Args...> in N3000 uses

static_cast<T>(create<Args>()...)

for the one-argument case, but static_cast also permits unwanted conversions such as void* to T* and Base* to Derived*.

Proposed resolution:

Change 20.7.4.3 [meta.unary.prop], p6:

the predicate condition for a template specialization is_constructible<T, Args> shall be satisfied, if and only if the following expression CE variable definition would be well-formed:


1268. The Mutex requirements in 30.4.1 and 30.4.2 are wrong

Section: 30.4 [thread.mutex] Status: Open Submitter: Anthony Williams Opened: 2009-11-17 Last modified: 2010-03-28

View all other issues in [thread.mutex].

View all issues with Open status.

Discussion:

The Mutex requirements in 30.4.1 [thread.mutex.requirements] and 30.4.2 [thread.timedmutex.requirements] confuse the requirements on the behaviour of std::mutex et al with the requirements on Lockable types for use with std::unique_lock, std::lock_guard and std::condition_variable_any.

[ 2010 Pittsburgh: ]

Concepts of threads chapter and issue presentation are: Lockable < Mutex < TimedMutex and Lockable < TimedLockable < TimedMutex.

Typo in failed deletion of Mutex in 30.4.4 p4 edits.

Lockable requirements are too weak for condition_variable_any, but the Mutex requirements are too strong.

Need subset of Lockable requirements for condition_variable_any that does not include try_lock. E.g. CvLockable < Lockable.

Text needs updating to recent draft changes.

Needs to specify exception behavior in Lockable.

The current standard is fine for what it says, but it places requirements that are too strong on authors of mutexes and locks.

Move to open status. Suggest Anthony look at condition_variable_any requirements. Suggest Anthony refine requirements/concepts categories.

Related to 964 and 966

[ 2010-03-28 Daniel synced with N3092. ]

Proposed resolution:

Add a new section to 30.2 [thread.req] after 30.2.4 [thread.req.timing] as follows:

30.2.5 Requirements for Lockable types

The standard library templates unique_lock (30.4.3.2 [thread.lock.unique]), lock_guard (30.4.3.1 [thread.lock.guard]), lock, try_lock (30.4.4 [thread.lock.algorithm]) and condition_variable_any (30.5.2 [thread.condition.condvarany]) all operate on user-supplied Lockable objects. Such an object must support the member functions specified for either the Lockable Requirements or the TimedLockable requirements as appropriate to acquire or release ownership of a lock by a given thread. [Note: the nature of any lock ownership and any synchronization it may entail are not part of these requirements. — end note]

30.2.5.1 Lockable Requirements

In order to qualify as a Lockable type, the following expressions must be supported, with the specified semantics, where m denotes a value of type L that supports the Lockable:

The expression m.lock() shall be well-formed and have the following semantics:

Effects:
Block until a lock can be acquired for the current thread.
Return type:
void

The expression m.try_lock() shall be well-formed and have the following semantics:

Effects:
Attempt to acquire a lock for the current thread without blocking.
Return type:
bool
Returns:
true if the lock was acquired, false otherwise.

The expression m.unlock() shall be well-formed and have the following semantics:

Effects:
Release a lock on m held by the current thread.
Return type:
void
Throws:
Nothing if the current thread holds a lock on m.

30.2.5.2 TimedLockable Requirements

For a type to qualify as TimedLockable it must meet the Lockable requirements, and additionally the following expressions must be well-formed, with the specified semantics, where m is an instance of a type TL that supports the TimedLockable requirements, rel_time denotes instantiation of duration (20.10.3 [time.duration]) and abs_time denotes an instantiation of time_point (20.10.4 [time.point])

The expression m.try_lock_for(rel_time) shall be well-formed and have the following semantics:

Effects:
Attempt to acquire a lock for the current thread within the specified time period.
Return type:
bool
Returns:
true if the lock was acquired, false otherwise.

The expression m.try_lock_until(abs_time) shall be well-formed and have the following semantics:

Effects:
Attempt to acquire a lock for the current thread before the specified point in time.
Return type:
bool
Returns:
true if the lock was acquired, false otherwise.

Replace 30.4.1 [thread.mutex.requirements] paragraph 2 with the following:

2 This section describes requirements on template argument types used to instantiate templates defined in the mutex types supplied by the C++ standard library. The template definitions in the C++ standard library refer These types shall conform to the named Mutex requirements whose details are set out below. In this description, m is an object of a Mutex type one of the standard library mutex types std::mutex, std::recursive_mutex, std::timed_mutex or std::recursive_timed_mutex..

Add the following paragraph after 30.4.1 [thread.mutex.requirements] paragraph 2:

A Mutex type shall conform to the Lockable requirements (30.2.5.1).

Replace 30.4.2 [thread.timedmutex.requirements] paragraph 1 with the following:

The C++ standard library TimedMutex types std::timed_mutex and std::recursive_timed_mutex A TimedMutex type shall meet the requirements for a Mutex type. In addition, itthey shall meet the requirements set out in this Clause 30.4.2below, where rel_time denotes an instantiation of duration (20.10.3 [time.duration]) and abs_time denotes an instantiation of time_point (20.10.4 [time.point]).

Add the following paragraph after 30.4.2 [thread.timedmutex.requirements] paragraph 1:

A TimedMutex type shall conform to the TimedLockable requirements (30.2.5.1).

Add the following paragraph following 30.4.3.1 [thread.lock.guard] paragraph 1:

The supplied Mutex type shall meet the Lockable requirements (30.2.5.1).

Add the following paragraph following 30.4.3.2 [thread.lock.unique] paragraph 1:

The supplied Mutex type shall meet the Lockable requirements (30.2.5.1). unique_lock<Mutex> meets the Lockable requirements. If Mutex meets the TimedLockable requirements (30.2.5.2) then unique_lock<Mutex> also meets the TimedLockable requirements.

Replace the use of "mutex" or "mutex object" with "lockable object" throughout clause 30.4.3 [thread.lock] paragraph 1:

1 A lock is an object that holds a reference to a mutexlockable object and may unlock the mutexlockable object during the lock's destruction (such as when leaving block scope). A thread of execution may use a lock to aid in managing mutex ownership of a lockable object in an exception safe manner. A lock is said to own a mutexlockable object if it is currently managing the ownership of that mutexlockable object for a thread of execution. A lock does not manage the lifetime of the mutexlockable object it references. [ Note: Locks are intended to ease the burden of unlocking the mutexlockable object under both normal and exceptional circumstances. — end note ]

30.4.3 [thread.lock] paragaph 2:

2 Some lock constructors take tag types which describe what should be done with the mutexlockable object during the lock's constuction.

30.4.3.1 [thread.lock.guard] paragaph 1:

1 An object of type lock_guard controls the ownership of a mutexlockable object within a scope. A lock_guard object maintains ownership of a mutexlockable object throughout the lock_guard object's lifetime. The behavior of a program is undefined if the mutexlockable object referenced by pm does not exist for the entire lifetime (3.8) of the lock_guard object. Mutex shall meet the Lockable requirements (30.2.5.1).

30.4.3.2 [thread.lock.unique] paragaph 1:

1 An object of type unique_lock controls the ownership of a mutexlockable object within a scope. Mutex oOwnership of the lockable object may be acquired at construction or after construction, and may be transferred, after acquisition, to another unique_lock object. Objects of type unique_lock are not copyable but are movable. The behavior of a program is undefined if the contained pointer pm is not null and the mutex pointed to by pm does not exist for the entire remaining lifetime (3.8) of the unique_lock object. Mutex shall meet the Lockable requirements (30.2.5.1).

Add the following to the precondition of unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) in 30.4.3.2.1 [thread.lock.unique.cons] paragraph 18:

template <class Clock, class Duration>
  unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);
18 Requires: If mutex_type is not a recursive mutex the calling thread does not own the mutex. The supplied mutex_type type shall meet the TimedLockable requirements (30.2.5.2).

Add the following to the precondition of unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) in 30.4.3.2.1 [thread.lock.unique.cons] paragraph 22

22 Requires: If mutex_type is not a recursive mutex the calling thread does not own the mutex. The supplied mutex_type type shall meet the TimedLockable requirements (30.2.5.2).

Add the following as a precondition of bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) before 30.4.3.2.2 [thread.lock.unique.locking] paragraph 8

template <class Clock, class Duration>
  bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);
Requires: The supplied mutex_type type shall meet the TimedLockable requirements (30.2.5.2).

Add the following as a precondition of bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) before 30.4.3.2.2 [thread.lock.unique.locking] paragraph 12

template <class Rep, class Period>
  bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);
Requires: The supplied mutex_type type shall meet the TimedLockable requirements (30.2.5.2).

Replace 30.4.4 [thread.lock.algorithm] p1 with the following:

template <class L1, class L2, class... L3> int try_lock(L1&, L2&, L3&...);
1 Requires: Each template parameter type shall meet the Mutex Lockable requirements (30.2.5.1)., except that a call to try_lock() may throw an exception. [Note: The unique_lock class template meets these requirements when suitably instantiated. — end note]

Replace 30.4.4 [thread.lock.algorithm] p4 with the following:

template <class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);
4 Requires: Each template parameter type shall meet the MutexMutex Lockable requirements (30.2.5.1)., except that a call to try_lock() may throw an exception. [Note: The unique_lock class template meets these requirements when suitably instantiated. — end note]

Replace 30.5.2 [thread.condition.condvarany] paragraph 1 with:

1 A Lock type shall meet the requirements for a Mutex type Lockable requirements (30.2.5.1), except that try_lock is not required. [Note: All of the standard mutex types meet this requirement. — end note]

1278. inconsistent return values for forward_list::insert_after

Section: 23.3.3.4 [forwardlist.modifiers] Status: Ready Submitter: Bo Persson Opened: 2009-11-25 Last modified: 2010-03-15

View all other issues in [forwardlist.modifiers].

View all issues with Ready status.

Discussion:

After applying LDR149, forward_list now has 5 overloads of insert_after, all returning an iterator.

However, two of those - inserting a single object - return "An iterator pointing to a copy of x [the inserted object]" while the other three - inserting zero or more objects - return an iterator equivalent to the position parameter, pointing before any possibly inserted objects.

Is this the intended change?

I don't really know what insert_after(position, empty_range) should really return, but always returning position seems less than useful.

[ 2010-02-04 Howard adds: ]

I agree this inconsistency will be error prone and needs to be fixed. Additionally emplace_after's return value is unspecified.

[ 2010-02-04 Nico provides wording. ]

[ 2010 Pittsburgh: ]

We prefer to return an iterator to the last inserted element. Modify the proposed wording and then set to Ready.

[ 2010-03-15 Howard adds: ]

Wording updated and set to Ready.

Proposed resolution:

In forward_list modifiers 23.3.3.4 [forwardlist.modifiers] make the following modifications:

iterator insert_after(const_iterator position, size_type n, const T& x);

...

10 Returns: position. An iterator pointing to the last inserted copy of x or position if n == 0.

template <class InputIterator>
  iterator insert_after(const_iterator position, InputIterator first, InputIterator last);

...

13 Returns: position. An iterator pointing to the last inserted element or position if first == last.

iterator insert_after(const_iterator position, initializer_list<T> il);

...

15 Returns: position. An iterator pointing to the last inserted element or position if il is empty.

template <class... Args>
  iterator emplace_after(const_iterator position, Args&&... args);

...

17 ...

Returns: An iterator pointing to the new constructed element from args.


1279. forbid [u|bi]nary_function specialization

Section: 20.8.3 [base] Status: New Submitter: Alberto Ganesh Barbati Opened: 2009-11-30 Last modified: 2010-03-27

View all other issues in [base].

View all issues with New status.

Discussion:

A program should not be allowed to add specialization of class templates unary_function and binary_function, in force of 17.6.3.2.1 [namespace.std]/1. If a program were allowed to specialize these templates, the library could no longer rely on them to provide the intended typedefs or there might be other undesired interactions.

[ 2010-03-27 Daniel adds: ]

Accepting issue 1290 would resolve this issue as NAD editorial.

Proposed resolution:

Change paragraph 20.8.3 [base]/1 as follows:

1 The following classes class templates are provided to simplify the typedefs of the argument and result types:. A program shall not declare specializations of these templates.

1281. CopyConstruction and Assignment between ratios having the same normalized form

Section: 20.6.1 [ratio.ratio] Status: Review Submitter: Vicente Juan Botet Escribá Opened: 2009-12-07 Last modified: 2010-03-27

View all other issues in [ratio.ratio].

View all issues with Review status.

Discussion:

CopyConstruction and Assignment between ratios having the same normalized form. Current N3000 do not allows to copy-construct or assign ratio instances of ratio classes having the same normalized form.

Two ratio classes ratio<N1,D1> and ratio<N2,D2> have the same normalized form if

ratio<N1, D1>::num == ratio<N2, D2>::num &&
ratio<N1, D1>::den == ratio<N2, D2>::den

This simple example

ratio<1,3> r1;
ratio<3,9> r2;
r1 = r2; // (1)

fails to compile in (1). Other example

ratio<1,3> r1;
ratio_subtract<ratio<2,3>, ratio<1,3>>::type r2;
r1 = r2;  

The nested type of ratio_subtract<ratio<2,3>, ratio<1,3>> could be ratio<3,9> so the compilation could fail. It could also be ratio<1,3> and the compilation succeeds.

In 20.6.2 [ratio.arithmetic] 3 and similar clauses

3 The nested typedef type shall be a synonym for ratio<T1, T2> where T1 has the value R1::num * R2::den - R2::num * R1::den and T2 has the value R1::den * R2::den.

the meaning of synonym let think that the result shall be a normalized ratio equivalent to ratio<T1, T2>, but there is not an explicit definition of what synonym means in this context.

Additionally we should add a typedef for accessing the normalized ratio, and change 20.6.2 [ratio.arithmetic] to return only this normalized result.

[ 2010 Pittsburgh: ]

There is no consensus to add the converting copy constructor or converting copy assignment operator. However there was consensus to add the typedef.

Proposed wording modified. Original proposed wording preserved here. Moved to Review.

Make ratio default constructible, copy-constructible and assignable from any ratio which has the same reduced form.

Add to 20.6.1 [ratio.ratio] synopsis

template <intmax_t N, intmax_t D = 1>
class ratio {
public:
  static constexpr intmax_t num;
  static constexpr intmax_t den;

  typedef ratio<num, den> type;

  ratio() = default;
  template <intmax_t N2, intmax_t D2>
    ratio(const ratio<N2, D2>&);
  template <intmax_t N2, intmax_t D2>
    ratio& operator=(const ratio<N2, D2>&);
};

Add to 20.6.1 [ratio.ratio]:

Two ratio classes ratio<N1,D1> and ratio<N2,D2> have the same reduced form if ratio<N1,D1>::type is the same type as ratio<N2,D2>::type

Add a new section: [ratio.cons]

Construction and assignment [ratio.cons]

template <intmax_t N2, intmax_t D2>
  ratio(const ratio<N2, D2>& r);

Effects: Constructs a ratio object.

Remarks: This constructor shall not participate in overload resolution unless r has the same reduced form as *this.

template <intmax_t N2, intmax_t D2>
  ratio& operator=(const ratio<N2, D2>& r);

Effects: None.

Returns: *this.

Remarks: This operator shall not participate in overload resolution unless r has the same reduced form as *this.

Change 20.6.2 [ratio.arithmetic]

Implementations may use other algorithms to compute these values. If overflow occurs, a diagnostic shall be issued.

template <class R1, class R2> struct ratio_add {
  typedef see below type;
};
The nested typedef type shall be a synonym for ratio<T1, T2>::type where T1 has the value R1::num * R2::den + R2::num * R1::den and T2 has the value R1::den * R2::den.
template <class R1, class R2> struct ratio_subtract {
  typedef see below type;
};
The nested typedef type shall be a synonym for ratio<T1, T2>::type where T1 has the value R1::num * R2::den - R2::num * R1::den and T2 has the value R1::den * R2::den.
template <class R1, class R2> struct ratio_multiply {
  typedef see below type;
};
The nested typedef type shall be a synonym for ratio<T1, T2>::type where T1 has the value R1::num * R2::num and T2 has the value R1::den * R2::den.
template <class R1, class R2> struct ratio_divide {
  typedef see below type;
};
The nested typedef type shall be a synonym for ratio<T1, T2>::type where T1 has the value R1::num * R2::den and T2 has the value R1::den * R2::num.

[ 2010-03-27 Howard adds: ]

Daniel brought to my attention the recent addition of the typedef type to the FCD N3092:

typedef ratio type;

This issue was discussed in Pittsburgh, and the decision there was to accept the typedef as proposed and move to Review. Unfortunately the issue was accidently applied to the FCD, and incorrectly. The FCD version of the typedef refers to ratio<N, D>, but the typedef is intended to refer to ratio<num, den> which in general is not the same type.

I've updated the wording to diff against N3092.

Proposed resolution:

Add to 20.6.1 [ratio.ratio] synopsis

template <intmax_t N, intmax_t D = 1>
class ratio {
public:
  static constexpr intmax_t num;
  static constexpr intmax_t den;

  typedef ratio<num, den> type;
};

1289. Generic casting requirements for smart pointers

Section: 20.3 [utility] Status: New Submitter: Ion Gaztañaga Opened: 2009-12-14 Last modified: 2010-03-27

View all other issues in [utility].

View all issues with New status.

Discussion:

In section 20.2.5 [allocator.requirements], Table 40 — Allocator requirements, the following expression is required for allocator pointers:

Table 40 — Allocator requirements
Expression Return type Assertion/note
pre-/post-condition
Default
static_cast<X::pointer>(w) X::pointer static_cast<X::pointer>(w) == p  

To achieve this expression, a smart pointer writer must introduce an explicit conversion operator from smart_ptr<void> to smart_ptr<T> so that static_cast<pointer>(void_ptr) is a valid expression. Unfortunately this explicit conversion weakens the safety of a smart pointer since the following expression (invalid for raw pointers) would become valid:

smart_ptr<void> smart_v = ...;
smart_ptr<T> smart_t(smart_v);

On the other hand, shared_ptr also defines its own casting functions in 20.9.11.2.10 [util.smartptr.shared.cast], and although it's unlikely that a programmer will use shared_ptr as allocator::pointer, having two different ways to do the same cast operation does not seem reasonable. A possible solution would be to replace static_cast<X::pointer>(w) expression with a user customizable (via ADL) static_pointer_cast<value_type>(w), and establish the xxx_pointer_cast functions introduced by shared_ptr as the recommended generic casting utilities of the standard.

Unfortunately, we've experienced problems in Boost when trying to establish xxx_pointer_cast as customization points for generic libraries (http://objectmix.com/c/40424-adl-lookup-explicit-template-parameters.html) because these casting functions are called with explicit template parameters and the standard says in 14.8.1 [temp.arg.explicit] p.8 "Explicit template argument specification":

8 ...But when a function template with explicit template arguments is used, the call does not have the correct syntactic form unless there is a function template with that name visible at the point of the call. If no such name is visible, the call is not syntactically well-formed and argument-dependent lookup does not apply.

So we can do this:

template<class BasePtr>
void generic_ptr_swap(BasePtr p)
{
  //ADL customization point
  swap(p, p);
  //...
}

but not the following:

template<class BasePtr>
void generic_ptr_algo(BasePtr p)
{
  typedef std::pointer_traits<BasePtr>::template
     rebind<Derived> DerivedPtr;
  DerivedPtr dp = static_pointer_cast<Derived>(p);
}

The solution to make static_pointer_cast a customization point is to add a generic declaration (no definition) of static_pointer_cast in a namespace (like std) and apply "using std::static_pointer_cast" declaration to activate ADL:

namespace std{

template<typename U, typename T>
unspecified
static_pointer_cast(T&&) = delete;

}

template<class BasePtr>
void generic_ptr_algo(BasePtr p)
{
  typedef std::pointer_traits<BasePtr>::template
     rebind<Derived> DerivedPtr;

  //ADL applies because static_pointer_cast is made
  //  visible according to [temp.arg.explicit]/8
  using std::static_pointer_cast;

  DerivedPtr dp = static_pointer_cast<Derived>(p);

  //...
}

A complete solution will need also the definition of static_pointer_cast for raw pointers, and this definition has been present in Boost (http://www.boost.org/boost/ pointer_cast.hpp) for years.

[ 2010-03-26 Daniel made editorial adjustments to the proposed wording. ]

Proposed resolution:

Add to section 20.3 [utility] Utility components, Header <utility> synopsis:

// 20.3.X, generic pointer cast functions

template<typename U, typename T>
unspecified
static_pointer_cast(T&&) = delete;

template<typename U, typename T>
unspecified
dynamic_pointer_cast(T&&) = delete;

template<typename U, typename T>
unspecified
const_pointer_cast(T&&) = delete;

//Overloads for raw pointers
template<typename U, typename T>
auto static_pointer_cast(T* t) -> decltype(static_cast<U*>(t));

template<typename U, typename T>
auto dynamic_pointer_cast(T* t) -> decltype(dynamic_cast<U*>(t));

template<typename U, typename T>
auto const_pointer_cast(T* t) -> decltype(const_cast<U*>(t));

Add to section 20.3 [utility] Utility components, a new subclause 20.3.X Pointer cast utilities [pointer.cast]:

20.3.X Pointer cast utilities [pointer.cast]

1 The library defines generic pointer casting function templates so that template code can explicitly make these names visible and activate argument-dependent lookup for pointer cast calls.

//Generic declarations
template<typename U, typename T>
unspecified
static_pointer_cast(T&&) = delete;

template<typename U, typename T>
unspecified
dynamic_pointer_cast(T&&) = delete;

template<typename U, typename T>
unspecified
const_pointer_cast(T&&) = delete;

2 The library also defines overloads of these functions for raw pointers.

//Overloads for raw pointers
template<typename U, typename T>
auto static_pointer_cast(T* t) -> decltype(static_cast<U*>(t));
Returns: static_cast<U*>(t)
template<typename U, typename T>
auto dynamic_pointer_cast(T* t) -> decltype(dynamic_cast<U*>(t));
Returns: dynamic_cast<U*>(t)
template<typename U, typename T>
auto const_pointer_cast(T* t) -> decltype(const_cast<U*>(t));
Returns: const_cast<U*>(t)

[Example:

#include <utility> //static_pointer_cast
#include <memory>  //pointer_traits

class Base{};
class Derived : public Base{};

template<class BasePtr>
void generic_pointer_code(BasePtr b)
{
   typedef std::pointer_traits<BasePtr>::template
      rebind<Derived> DerivedPtr;

   using std::static_pointer_cast;
   //ADL applies now that static_pointer_cast is visible
   DerivedPtr d = static_pointer_cast<Derived>(b);
}

end example]

Replace in section 20.2.5 [allocator.requirements] Table 40 — Allocator requirements, the following table entries for allocator pointers:

Table 40 — Allocator requirements
Expression Return type Assertion/note
pre-/post-condition
Default
static_pointer_cast<X::pointerT>(w) X::pointer static_pointer_cast<X::pointerT>(w) == p  
static_pointer_cast<X::const_pointerconst T>(w) X::const_pointer static_pointer_cast<X::const_pointerconst T>(z) == q  

1290. Don't require [u|bi]nary_function inheritance

Section: 20.8 [function.objects] Status: New Submitter: Daniel Krügler Opened: 2009-12-14 Last modified: 2009-12-19

View all other issues in [function.objects].

View all issues with New status.

Discussion:

This issue is a follow-up of the discussion on issue 870 during the 2009 Santa Cruz meeting.

The class templates unary_function and binary_function are actually very simple typedef providers,

namespace std {

template <class Arg, class Result>
struct unary_function {
 typedef Arg argument_type;
 typedef Result result_type;
};

template <class Arg1, class Arg2, class Result>
struct binary_function {
 typedef Arg1 first_argument_type;
 typedef Arg2 second_argument_type;
 typedef Result result_type;
};

}

which may be used as base classes (similarly to the iterator template), but were originally not intended as a customization point. The SGI documentation introduced the concept Adaptable Unary Function as function objects "with nested typedefs that define its argument type and result type" and a similar definition for Adaptable Binary Function related to binary_function. But as of TR1 a protocol was introduced that relies on inheritance relations based on these types. 20.8.4 [refwrap]/3 b. 3 requires that a specialization of reference_wrapper<T> shall derive from unary_function, if type T is "a class type that is derived from std::unary_function<T1, R>" and a similar inheritance-based rule for binary_function exists as well.

As another disadvantage it has been pointed out in the TR1 issue list, N1837 (see section 10.39), that the requirements of mem_fn 20.8.13 [func.memfn]/2+3 to derive from std::unary_function/std::binary_function under circumstances, where the provision of corresponding typedefs would be sufficient, unnecessarily prevent implementations that take advantage of empty-base-class- optimizations.

Both requirements should be relaxed in the sense that the reference_wrapper should provide typedef's argument_type, first_argument_type, and second_argument_type based on similar rules as the weak result type rule (20.8.2 [func.require]/3) does specify the presence of result_type member types.

For a related issue see also 1279.

Proposed resolution:

[ The here proposed resolution is an attempt to realize the common denominator of the reflector threads c++std-lib-26011, c++std-lib-26095, and c++std-lib-26124. ]

  1. Change 20.8.3 [base]/1 as indicated: [The intend is to provide an alternative fix for issue 1279 and some editorial harmonization with existing wording in the library, like 24.4.2 [iterator.basic]/1]

    1 The following class templates are provided to simplify the definition of typedefs of the argument and result types for function objects. The behavior of a program that adds specializations for any of these templates is undefined.:

    namespace std {
     template <class Arg, class Result>
     struct unary_function {
       typedef Arg argument_type;
       typedef Result result_type;
     };
    }
    
    namespace std {
     template <class Arg1, class Arg2, class Result>
     struct binary_function {
       typedef Arg1 first_argument_type;
       typedef Arg2 second_argument_type;
       typedef Result result_type;
     };
    }
    
  2. Change 20.8.4 [refwrap], class template reference_wrapper synopsis as indicated: [The intent is to remove the requirement that reference_wrapper derives from unary_function or binary_function if the situation requires the definition of the typedefs argument_type, first_argument_type, or second_argument_type. This change is suggested, because the new way of definition uses the same strategy as the weak result type specification applied to argument types, which provides the following advantages: It creates less potential conflicts between [u|bi]nary_function bases and typedefs in a function object and it ensures that user-defined function objects which provide typedefs but no such bases are handled as first class citizens.]

    namespace std {
     template <class T> class reference_wrapper
       : public unary_function<T1, R> // see below
       : public binary_function<T1, T2, R> // see below
     {
     public :
       // types
       typedef T type;
       typedef see below result_type; // not always defined
       typedef see below argument_type; // not always defined
       typedef see below first_argument_type; // not always defined
       typedef see below second_argument_type; // not always defined
    
       // construct/copy/destroy
       ...
     };
    
  3. Change 20.8.4 [refwrap]/3 as indicated: [The intent is to remove the requirement that reference_wrapper derives from unary_function if the situation requires the definition of the typedef argument_type and result_type. Note that this clause does concentrate on argument_type alone, because the result_type is already ruled by p. 2 via the weak result type specification. The new way of specifying argument_type is equivalent to the weak result type specification]

    3 The template instantiation reference_wrapper<T> shall be derived from std::unary_function<T1, R>define a nested type named argument_type as a synonym for T1 only if the type T is any of the following:

    • a function type or a pointer to function type taking one argument of type T1 and returning R
    • a pointer to member function R T0::f cv (where cv represents the member function's cv-qualifiers); the type T1 is cv T0*
    • a class type that is derived from std::unary_function<T1, R>with a member type argument_type; the type T1 is T::argument_type
  4. Change 20.8.4 [refwrap]/4 as indicated: [The intent is to remove the requirement that reference_wrapper derives from binary_function if the situation requires the definition of the typedef first_argument_type, second_argument_type, and result_type. Note that this clause does concentrate on first_argument_type and second_argument_type alone, because the result_type is already ruled by p. 2 via the weak result type specification. The new way of specifying first_argument_type and second_argument_type is equivalent to the weak result type specification]

    The template instantiation reference_wrapper<T> shall be derived from std::binary_function<T1, T2, R>define two nested types named first_argument_type and second_argument_type as a synonym for T1 and T2, respectively, only if the type T is any of the following:

    • a function type or a pointer to function type taking two arguments of types T1 and T2 and returning R
    • a pointer to member function R T0::f(T2) cv (where cv represents the member function's cv-qualifiers); the type T1 is cv T0*
    • a class type that is derived from std::binary_function<T1, T2, R>with member types first_argument_type and second_argument_type; the type T1 is T::first_argument_type and the type T2 is T::second_argument_type
  5. Change 20.8.13 [func.memfn]/2+3 as indicated: [The intent is to remove the requirement that mem_fn's return type has to derive from [u|bi]nary_function. The reason for suggesting the change here is to better support empty-base-class optimization choices as has been pointed out in N1837]

    2 The simple call wrapper shall be derived from std::unary_function<cv T*, Ret>define two nested types named argument_type and result_type as a synonym for cv T* and Ret, respectively, when pm is a pointer to member function with cv-qualifier cv and taking no arguments, where Ret is pm's return type.

    3 The simple call wrapper shall be derived from std::binary_function<cv T*, T1, Ret>define three nested types named first_argument_type, second_argument_type, and result_type as a synonym for cv T*, T1, and Ret, respectively, when pm is a pointer to member function with cv-qualifier cv and taking one argument of type T1, where Ret is pm's return type.


1292. std::function should support all callable types

Section: 20.8.14.2.1 [func.wrap.func.con] Status: New Submitter: Daniel Krügler Opened: 2009-12-19 Last modified: 2009-12-21

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

View all issues with New status.

Discussion:

Some parts of the specification of std::function is unnecessarily restricted to a subset of all callable types (as defined in 20.8.1 [func.def]/3), even though the intent clearly is to be usable for all of them as described in 20.8.14.2 [func.wrap.func]/1. This argument becomes strengthened by the fact that current C++0x-compatible compilers work fine with them:

#include <functional>
#include <iostream>

struct A
{
  int foo(int i) const {return i+1;}
};

struct B
{
  int mem;
};

int main()
{
  std::function<int(const A&, int)> f(&A::foo);
  A a;
  std::cout << f(a, 1) << '\n';
  std::cout << f.target_type().name() << '\n';
  typedef int (A::* target_t)(int) const;
  target_t* p = f.target<target_t>();
  std::cout << (p != 0) << '\n';
  std::function<int(B&)> f2(&B::mem);
  B b = { 42 };
  std::cout << f2(b) << '\n';
  std::cout << f2.target_type().name() << '\n';
  typedef int (B::* target2_t);
  target2_t* p2 = f2.target<target2_t>();
  std::cout << (p2 != 0) << '\n';
}

The problematics passages are 20.8.14.2.1 [func.wrap.func.con]/10:

template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);

...

10 Postconditions: !*this if any of the following hold:

because it does not consider pointer to data member and all constraints based on function objects which like 20.8.14.2 [func.wrap.func]/2 or 20.8.14.2.5 [func.wrap.func.targ]/3. The latter two will be resolved by the proposed resolution of 870 and are therefore not handled here.

Proposed resolution:

Change 20.8.14.2.1 [func.wrap.func.con]/10+11 as indicated:

template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);

...

10 Postconditions: !*this if any of the following hold:

11 Otherwise, *this targets a copy of f or, initialized with std::move(f) if f is not a pointer to member function, and targets a copy of mem_fn(f) if f is a pointer to member function. [Note: implementations are encouraged to avoid the use of dynamically allocated memory for small function objects, for example, where f's target is an object holding only a pointer or reference to an object and a member function pointer. — end note]


1294. Difference between callable wrapper and forwarding call wrapper unclear

Section: 20.8.2 [func.require] Status: New Submitter: Jens Maurer Opened: 2009-12-21 Last modified: 2009-12-21

View other active issues in [func.require].

View all other issues in [func.require].

View all issues with New status.

Discussion:

The current wording in the standard makes it hard to discriminate the difference between a "call wrapper" as defined in 20.8.1 [func.def]/5+6:

5 A call wrapper type is a type that holds a callable object and supports a call operation that forwards to that object.

6 A call wrapper is an object of a call wrapper type.

and a "forwarding call wrapper" as defined in 20.8.2 [func.require]/4:

4 [..] A forwarding call wrapper is a call wrapper that can be called with an argument list. [Note: in a typical implementation forwarding call wrappers have an overloaded function call operator of the form

template<class... ArgTypes>
R operator()(ArgTypes&&... args) cv-qual;

end note]

Reason for this lack of clear difference seems to be that the wording adaption to variadics and rvalues that were applied after it's original proposal in N1673:

[..] A forwarding call wrapper is a call wrapper that can be called with an argument list t1, t2, ..., tN where each ti is an lvalue. The effect of calling a forwarding call wrapper with one or more arguments that are rvalues is implementation defined. [Note: in a typical implementation forwarding call wrappers have overloaded function call operators of the form

template<class T1, class T2, ..., class TN>
R operator()(T1& t1, T2& t2, ..., TN& tN) cv-qual;

end note]

combined with the fact that the word "forward" has two different meanings in this context. This issue attempts to clarify the difference better.

Proposed resolution:

Change 20.8.2 [func.require]/4 as indicated:

4 [..] A forwarding call wrapper is a call wrapper that can be called with an arbitrary argument list and uses perfect forwarding to deliver the arguments to the wrapped callable object. [Note: in a typical implementation forwarding call wrappers have an overloaded function call operator of the form

template<class... ArgTypes>
R operator()(ArgTypes&&... args) cv-qual;

end note]


1295. Contradictory call wrapper requirements

Section: 20.8.2 [func.require] Status: New Submitter: Daniel Krügler Opened: 2009-12-22 Last modified: 2009-12-23

View other active issues in [func.require].

View all other issues in [func.require].

View all issues with New status.

Discussion:

20.8.2 [func.require]/3 b 1 says

3 If a call wrapper (20.8.1 [func.def]) has a weak result type the type of its member type result_type is based on the type T of the wrapper's target object (20.8.1 [func.def]):

The first two enumerated types (function and reference to function) can never be valid types for T, because

20.8.1 [func.def]/7

7 A target object is the callable object held by a call wrapper.

and 20.8.1 [func.def]/3

3 A callable type is a pointer to function, a pointer to member function, a pointer to member data, or a class type whose objects can appear immediately to the left of a function call operator.

exclude functions and references to function as "target objects".

Proposed resolution:

Change 20.8.2 [func.require]/3 b 1 as indicated:

3 If a call wrapper (20.8.1 [func.def]) has a weak result type the type of its member type result_type is based on the type T of the wrapper's target object (20.8.1 [func.def]):


1297. unique_ptr's relational operator functions should induce a total order

Section: 20.9.10.4 [unique.ptr.special] Status: New Submitter: Daniel Krügler Opened: 2009-12-23 Last modified: 2010-03-27

View all issues with New status.

Discussion:

The comparison functions of unique_ptr currently directly delegate to the underlying comparison functions of unique_ptr<T, D>::pointer. This is disadvantageous, because this would not guarantee to induce a total ordering for native pointers and it is hard to define a total order for mixed types anyway.

The currently suggested resolution for shared_ptr comparison as of 1262 uses a normalization strategy: They perform the comparison on the composite pointer type (5.9 [expr.rel]). This is not exactly possible for unique_ptr in the presence of user-defined pointer-like types but the existing definition of std::duration comparison as of 20.10.3.6 [time.duration.comparisons] via common_type of both argument types demonstrates a solution of this problem. The approach can be seen as the general way to define a composite pointer type and this is the approach which is used for here suggested wording change.

For consistency reasons I would have preferred the same normalization strategy for == and !=, but Howard convinced me not to do so (now).

Proposed resolution:

Change 20.9.10.4 [unique.ptr.special]/4-7 as indicated: [The implicit requirements and remarks imposed on the last three operators are the same as for the first one due to the normative "equivalent to" usage within a Requires element, see 17.5.1.4 [structure.specifications]/4. The effects of this change are that all real pointers wrapped in a unique_ptr will order like shared_ptr does.]

template <class T1, class D1, class T2, class D2>
  bool operator<(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);

Requires: Let CT be common_type<unique_ptr<T1, D1>::pointer, unique_ptr<T2, D2>::pointer>::type. Then CT shall satisfy the LessThanComparable requirements.

4 Returns: less<CT>()(x.get(), y.get())x.get() < y.get().

Remarks: If unique_ptr<T1, D1>::pointer is not implicitly convertible to CT or unique_ptr<T2, D2>::pointer is not implicitly convertible to CT, the program is ill-formed.

template <class T1, class D1, class T2, class D2>
  bool operator<=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
5 Effects: Equivalent to return !(y < x) Returns: x.get() <= y.get().
template <class T1, class D1, class T2, class D2>
  bool operator>(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
6 Effects: Equivalent to return (y < x) Returns: x.get() > y.get().
template <class T1, class D1, class T2, class D2>
  bool operator>=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
7 Effects: Equivalent to return !(x < y) Returns: x.get() >= y.get().

1310. forward_list splice_after from lvalues

Section: 23.3.3.5 [forwardlist.ops] Status: New Submitter: Howard Hinnant Opened: 2010-02-05 Last modified: 2010-02-05

View all other issues in [forwardlist.ops].

View all issues with New status.

Discussion:

We've moved 1133 to Tentatively Ready and I'm fine with that.

1133 adds lvalue-references to the splice signatures for list. So now list can splice from lvalue and rvalue lists (which was the intent of the original move papers btw). During the discussion of this issue it was mentioned that if we want to give the same treatment to forward_list, that should be a separate issue.

This is that separate issue.

Consider the following case where you want to splice elements from one place in a forward_list to another. Currently this must be coded like so:

fl.splice_after(to_here, std::move(fl), from1, from2);

This looks pretty shocking to me. I would expect to be able to code instead:

fl.splice_after(to_here, fl, from1, from2);

but we currently don't allow it.

When I say move(fl), I consider that as saying that I don't care about the value of fl any more (until I assign it a new value). But in the above example, this simply isn't true. I do care about the value of fl after the move, and I'm not assigning it a new value. I'm merely permuting its current value.

I propose adding forward_list& overloads to the 3 splice_after members. For consistency's sake (principal of least surprise) I'm also proposing to overload merge this way as well.

Proposed resolution:

Add to the synopsis of 23.3.3 [forwardlist]:

template <class T, class Allocator = allocator<T> >
class forward_list {
public:
  ...
  void splice_after(const_iterator p, forward_list& x);
  void splice_after(const_iterator p, forward_list&& x);
  void splice_after(const_iterator p, forward_list& x, const_iterator i);
  void splice_after(const_iterator p, forward_list&& x, const_iterator i);
  void splice_after(const_iterator p, forward_list& x,
                    const_iterator first, const_iterator last);
  void splice_after(const_iterator p, forward_list&& x,
                    const_iterator first, const_iterator last);
  ...
  void merge(forward_list& x);
  void merge(forward_list&& x);
  template <class Compare> void merge(forward_list& x, Compare comp);
  template <class Compare> void merge(forward_list&& x, Compare comp);
  ...
};

Add to the signatures of 23.3.3.5 [forwardlist.ops]:

void splice_after(const_iterator p, forward_list& x);
void splice_after(const_iterator p, forward_list&& x);

1 ...

void splice_after(const_iterator p, forward_list& x, const_iterator i);
void splice_after(const_iterator p, forward_list&& x, const_iterator i);

5 ...

void splice_after(const_iterator p, forward_list& x,
                const_iterator first, const_iterator last);
void splice_after(const_iterator p, forward_list&& x,
                const_iterator first, const_iterator last);

9 ...

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

18 ...


1316. scoped_allocator_adaptor operator== has no definition

Section: 20.9.6 [allocator.adaptor] Status: New Submitter: Pablo Halpern Opened: 2009-02-11 Last modified: 2010-02-11

View all issues with New status.

Discussion:

The WP (N3000) contains these declarations:

template <class OuterA1, class OuterA2, class... InnerAllocs>
  bool operator==(const scoped_allocator_adaptor<OuterA1, InnerAllocs...>& a,
                  const scoped_allocator_adaptor<OuterA2, InnerAllocs...>& b);
template <class OuterA1, class OuterA2, class... InnerAllocs>
  bool operator!=(const scoped_allocator_adaptor<OuterA1, InnerAllocs...>& a,
                  const scoped_allocator_adaptor<OuterA2, InnerAllocs...>& b);

But does not define what the behavior of these operators are.

Proposed resolution:

Add a new section after 20.9.6.3 [allocator.adaptor.members]:

Scoped allocator operators [scoped.adaptor.operators]

template <class OuterA1, class OuterA2, class... InnerAllocs>
  bool operator==(const scoped_allocator_adaptor<OuterA1, InnerAllocs...>& a,
                  const scoped_allocator_adaptor<OuterA2, InnerAllocs...>& b);
Returns: a.outer_allocator() == b.outer_allocator() if sizeof...(InnerAllocs) is zero; otherwise, a.outer_allocator() == b.outer_allocator() && a.inner_allocator() == b.inner_allocator().
template <class OuterA1, class OuterA2, class... InnerAllocs>
  bool operator!=(const scoped_allocator_adaptor<OuterA1, InnerAllocs...>& a,
                  const scoped_allocator_adaptor<OuterA2, InnerAllocs...>& b);
Returns: !(a == b).

1318. N2982 removes previous allocator capabilities

Section: 20.9.4.1 [allocator.traits.types] Status: New Submitter: Pete Becker Opened: 2010-02-11 Last modified: 2010-02-25

View all issues with New status.

Discussion:

N2982 says that containers should have a nested typedef that defines their reference_type as value_type&; the previous standard deferred to the allocator to define its reference_type, and containers simply passed the allocator's typedef on. This change is a mistake. Allocators should define both a pointer type and a reference type. That's essential for their original purpose, which was to make different memory models transparent. If an allocator defines a pointer type that isn't compatible with a normal pointer it also has to define a corresponding reference type. For example (and please forgive a Windows-ism), if an allocator's pointer is T __far*, then it's reference has to be T __far&. Otherwise everything crashes (under the hood, references are pointers and have to have the same memory access mechanics). Extensions such as this for more general memory models were explicitly encouraged by C++03, and the allocator's pointer and reference typedefs were the hooks for such extensions. Removing the allocator's reference and const_reference typedefs makes those extensions unimplementable and breaks existing implementations that rely on those hooks.

[ 2010-02-25 Alisdair adds: ]

vector<bool>::reference is a nested class, and not a typedef. It should be removed from the list of containers when this change is made.

In general, I am unfcomfortable placing this reference requirement on each container, as I would prefer to require:

is_same<Container::reference, Container::iterator::reference>

This distinction is important, if we intend to support proxy iterators. The iterator paper in the pre-Pittsburgh mailing (N3046) does not make this proposal, but organises clause 24 in such a way this will be much easier to specify.

The changes to clause 20 remain important for all the reasons Pete highlights.

Proposed resolution:

  1. Add the following two rows to Table 40, Allocator requirements:

    Table 40 — Allocator requirements
    Expression Return type Assertion/note
    pre-/post-condition
    Default
    X::reference T&
    X::const_reference const T&
  2. 2. Change the following two rows in Table 40:

    Table 40 — Allocator requirements
    Expression Return type Assertion/note
    pre-/post-condition
    Default
    *p T& X::reference
    *q const T& X::const_reference
  3. Add the following typedef declarations to allocator_traits 20.9.4 [allocator.traits]:

    template <class Alloc> struct allocator_traits {
      ...
      typedef see below reference;
      typedef see below const_reference;
      ...
    
  4. Add the following descriptions to 20.9.4.1 [allocator.traits.types]:

    typedef see below reference;
    Type: Alloc::reference if such a type exists; otherwise, value_type&.
    typedef see below const reference;
    Type: Alloc::const_reference if such a type exists; otherwise, const value_type&.
  5. Add the following typdef declarations to scoped_allocator_adaptor 20.9.6 [allocator.adaptor]:

    template <class OuterAlloc, class... InnerAllocs>
    class scoped_allocator_adaptor : public OuterAlloc {
      ...
      typedef typename OuterTraits::reference reference;
      typedef typename OuterTraits::const_reference const_reference;
      ...
    
  6. Change the nested typedefs reference and const_reference to:

    typedef typename allocator_traits<Allocator>::reference reference;
    typedef typename allocator_traits<Allocator>::const_reference const_reference;
    

    for each of the following class templates:

    deque 23.3.2 [deque]
    forward_list 23.3.3 [forwardlist]
    list 23.3.4 [list]
    queue 23.3.5.1.1 [queue.defn]
    priority_queue 23.3.5.2 [priority.queue]
    stack 23.3.5.3.1 [stack.defn]
    vector 23.3.6 [vector]
    vector<bool> 23.3.7 [vector.bool]
    map 23.4.1 [map]
    multimap 23.4.2 [multimap]
    set 23.4.3 [set]
    multiset 23.4.4 [multiset]
    unordered_map 23.5.1 [unord.map]
    unordered_multimap 23.5.2 [unord.multimap]
    unordered_set 23.5.3 [unord.set]
    unordered_multiset 23.5.4 [unord.multiset]
    basic_string 21.4 [basic.string]
    match_results 28.10 [re.results]

1319. Containers should require an iterator that is at least a Forward Iterator

Section: 23.2.1 [container.requirements.general] Status: New Submitter: Alisdair Meredith Opened: 2010-02-16 Last modified: 2010-02-16

View other active issues in [container.requirements.general].

View all other issues in [container.requirements.general].

View all issues with New status.

Discussion:

The requirements on container iterators are spelled out in 23.2.1 [container.requirements.general], table 91.

Table 91 — Container requirements
Expression Return type Operational semantics Assertion/note
pre-/post-condition
Complexity
...
X::iterator iterator type whose value type is T any iterator category except output iterator. Convertible to X::const_iterator. compile time
X::const_iterator constant iterator type whose value type is T any iterator category except output iterator compile time
...

As input iterators do not have the multi-pass guarantee, they are not suitable for iterating over a container. For example, taking two calls to begin(), incrementing either iterator might invalidate the other. While data structures might be imagined where this behaviour produces interesting and useful results, it is very unlikely to meet the full set of requirements for a standard container.

Proposed resolution:

Change Table 91 in 23.2.1 [container.requirements.general] as indicated:

Table 91 — Container requirements
Expression Return type Operational semantics Assertion/note
pre-/post-condition
Complexity
...
X::iterator iterator type whose value type is T any iterator category except output iterator. A Forward, Bidirectional or Random Access Iterator. Convertible to X::const_iterator. compile time
X::const_iterator constant iterator type whose value type is T any iterator category except output iterator A Forward, Bidirectional or Random Access Iterator. compile time
...

1320. Header for iter_swap

Section: 24.3 [iterator.synopsis] Status: New Submitter: Alisdair Meredith Opened: 2010-02-16 Last modified: 2010-02-16

View all issues with New status.

Discussion:

The iter_swap function template appears in the <algorithm> header, yet its main use is in building further algorithms, not calling existing ones. The main clients are implementers of data structures and their iterators, so it seems most appropriate to place the template in the <iterator> header instead.

Note that this is not an issue for implementers of the standard library, as they rarely use the standard headers directly, designing a more fine-grained set of headers for their own internal use. This option is not available to customers of the standard library.

Note that we cannot remove iter_swap from <algorithm> without breaking code, but there is no reason we cannot offer the same declaration via two standard headers. Alternatively, require <algorithm> to #include <iterator>, but introducing the dependency on the iterator adaptors seems un-necessary.

Proposed resolution:

Add the declaration of iter_swap to the <iterator> header synopsis (24.3 [iterator.synopsis]), with a note that it is documented in clause 25 [algorithms].

...
template <class T, size_t N> T* end(T (&array)[N]);

// documented in 25 [algorithms]
template<class ForwardIterator1, class ForwardIterator2>
  void iter_swap(ForwardIterator1 a, ForwardIterator2 b);

1322. Explicit CopyConstructible requirements are insufficient

Section: 20.2.1 [utility.arg.requirements] Status: New Submitter: Daniel Krügler Opened: 2010-02-16 Last modified: 2010-03-27

View all other issues in [utility.arg.requirements].

View all issues with New status.

Discussion:

With the acceptance of library defect 822 only direct-initialization is supported, and not copy-initialization in the requirement sets MoveConstructible and CopyConstructible. This is usually a good thing, if only the library implementation needs to obey these restrictions, but the Empire strikes back quickly:

  1. Affects user-code: std::exception_ptr is defined purely via requirements, among them CopyConstructible. A strict reading of the standard would make implementations conforming where std::exception_ptr has an explicit copy-c'tor and user-code must code defensively. This is a very unwanted effect for such an important component like std::exception_ptr.

  2. Wrong re-use: Recently proposed requirement sets (NullablePointer as of N3025, Hash) or cleanup of existing requirement sets (e.g. iterator requirements as of N3046) tend to reuse existing requirement sets, so reusing CopyConstructible is attempting, even in cases, where the intend is to support copy-initialization as well.

  3. Inconsistency: The current iterator requirements set Table 102 (output iterator requirements) and Table 103 (forward iterator requirements) demonstrate quite clearly a strong divergence of copy-semantics: The specified semantics of

    X u(a);
    X u = a;
    

    are underspecified compared to the most recent clarifications of the CopyConstructible requirements, c.f. issue 1309 which is very unsatisfactory. This will become worse for each further issue that involves the CopyConstructible specification (for possible directions see 1173).

The suggested resolution is to define two further requirements implicit-MoveConstructible and implicit-CopyConstructible (or any other reasonable name like MoveConvertible and CopyConvertible) each with a very succinct but precise meaning solving all three problems mentioned above.

Proposed resolution:

  1. Add the following new table ?? after Table 34 — MoveConstructible requirements [moveconstructible]:

    Table ?? — Implicit MoveConstructible requirements [implicit.moveconstructible] (in addition to MoveConstructible)
    Expression Operational Semantics
    T u = rv; Equivalent to: T u(rv);
  2. Add the following new table ?? after Table 35 — CopyConstructible requirements [copyconstructible]:

    Table ?? — Implicit CopyConstructible requirements [implicit.copyconstructible] (in addition to CopyConstructible)
    Expression Operational Semantics
    T u = v; Equivalent to: T u(v);
  3. Change 20.2.3 [nullablepointer.requirements]/1 as follows:

    A NullablePointer type is a pointer-like type that supports null values. A type P meets the requirements of NullablePointer if:

    • P satisfies the requirements of EqualityComparable, DefaultConstructible, implicit CopyConstructible, CopyAssignable, and Destructible,
    • [..]
  4. Change 20.2.4 [hash.requirements]/1 as indicated: [explicit copy-constructible functors could not be provided as arguments to any algorithm that takes these by value. Also a typo is fixed.]

    1 A type H meets the Hash requirements if:

    • it is a function object type (20.8),
    • it satisfiesifes the requirements of implicit CopyConstructible and Destructible (20.2.1),
    • [..]
  5. Change 20.7.1 [meta.rqmts]/1+2 as indicated:

    1 A UnaryTypeTrait describes a property of a type. It shall be a class template that takes one template type argument and, optionally, additional arguments that help define the property being described. It shall be DefaultConstructible, implicit CopyConstructible, [..]

    2 A BinaryTypeTrait describes a relationship between two types. It shall be a class template that takes two template type arguments and, optionally, additional arguments that help define the relationship being described. It shall be DefaultConstructible, implicit CopyConstructible, and [..]

  6. Change 20.8.2 [func.require]/4 as indicated: [explicit copy-constructible functors could not be provided as arguments to any algorithm that takes these by value]

    4 Every call wrapper (20.8.1) shall be implicit MoveConstructible. A simple call wrapper is a call wrapper that is implicit CopyConstructible and CopyAssignable and whose copy constructor, move constructor, and assignment operator do not throw exceptions. [..]
  7. Change 20.8.4 [refwrap]/1 as indicated:

    1 reference_wrapper<T> is an implicit CopyConstructible and CopyAssignable wrapper around a reference to an object or function of type T.
  8. Change 20.8.10.1.2 [func.bind.bind]/5+9 as indicated:

    5 Remarks: The return type shall satisfy the requirements of implicit MoveConstructible. If all of FD and TiD satisfy the requirements of CopyConstructible, then the return type shall satisfy the requirements of implicit CopyConstructible. [Note: this implies that all of FD and TiD are MoveConstructible. — end note]

    [..]

    9 Remarks: The return type shall satisfy the requirements of implicit MoveConstructible. If all of FD and TiD satisfy the requirements of CopyConstructible, then the return type shall satisfy the requirements of implicit CopyConstructible. [Note: this implies that all of FD and TiD are MoveConstructible. — end note]

  9. Change 20.8.10.1.3 [func.bind.place] as indicated:

    1 All placeholder types shall be DefaultConstructible and implicit CopyConstructible, and [..]
  10. Change 20.9.10 [unique.ptr]/5 as indicated:

    5 Each object of a type U instantiated form the unique_ptr template specified in this subclause has the strict ownership semantics, specified above, of a unique pointer. In partial satisfaction of these semantics, each such U is implicit MoveConstructible and MoveAssignable, but is not CopyConstructible nor CopyAssignable. The template parameter T of unique_ptr may be an incomplete type.
  11. Change 20.9.11.2 [util.smartptr.shared]/2 as indicated:

    2 Specializations of shared_ptr shall be implicit CopyConstructible, CopyAssignable, and LessThanComparable, [..]
  12. Change 20.9.11.3 [util.smartptr.weak]/2 as indicated:

    2 Specializations of weak_ptr shall be implicit CopyConstructible and CopyAssignable, allowing their use in standard containers. The template parameter T of weak_ptr may be an incomplete type.
  13. Change 24.2.2 [iterator.iterators]/2 as indicated: [This fixes a defect in the Iterator requirements. None of the usual algorithms accepting iterators would be usable with iterators with explicit copy-constructors]

    2 A type X satisfies the Iterator requirements if:

    • X satisfies the implicit CopyConstructible, CopyAssignable, and Destructible requirements (20.2.1) and lvalues of type X are swappable (20.2.2), and [..]
    • ...
  14. Change D.10.1 [auto.ptr]/3 as indicated:

    3 [..] Instances of auto_ptr meet the requirements of implicit MoveConstructible and MoveAssignable, but do not meet the requirements of CopyConstructible and CopyAssignable. — end note]

1323. basic_string::replace should use const_iterator

Section: 21.4.6.6 [string::replace] Status: New Submitter: Daniel Krügler Opened: 2010-02-19 Last modified: 2010-03-27

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

View all issues with New status.

Discussion:

In contrast to all library usages of purely positional iterator values several overloads of std::basic_string::replace still use iterator instead of const_iterator arguments. The paper N3021 quite nicely visualizes the purely positional responsibilities of the function arguments.

This should be fixed to make the library consistent, the proposed changes are quite mechanic.

Proposed resolution:

  1. In 21.4 [basic.string], class template basic_string synopsis change as indicated:

    // 21.4.6 modifiers:
    ...
    basic_string& replace(const_iterator i1, const_iterator i2,
                          const basic_string& str);
    basic_string& replace(const_iterator i1, const_iterator i2,
                          const charT* s, size_type n);
    basic_string& replace(const_iterator i1, const_iterator i2,
                          const charT* s);
    basic_string& replace(const_iterator i1, const_iterator i2,
                          size_type n, charT c);
    template<class InputIterator>
      basic_string& replace(const_iterator i1, const_iterator i2,
                            InputIterator j1, InputIterator j2);
    basic_string& replace(const_iterator, const_iterator,
                          initializer_list<charT>);
    
  2. In 21.4.6.6 [string::replace] before p.18, change the following signatures as indicated:

    basic_string& replace(const_iterator i1, const_iterator i2, const basic_string& str);
    
  3. In 21.4.6.6 [string::replace] before p.21, change the following signatures as indicated:

    basic_string&
      replace(const_iterator i1, const_iterator i2, const charT* s, size_type n);
    
  4. In 21.4.6.6 [string::replace] before p.24, change the following signatures as indicated:

    basic_string& replace(const_iterator i1, const_iterator i2, const charT* s);
    
  5. In 21.4.6.6 [string::replace] before p.27, change the following signatures as indicated:

    basic_string& replace(const_iterator i1, const_iterator i2, size_type n,
                          charT c);
    
  6. In 21.4.6.6 [string::replace] before p.30, change the following signatures as indicated:

    template<class InputIterator>
      basic_string& replace(const_iterator i1, const_iterator i2,
                            InputIterator j1, InputIterator j2);
    
  7. In 21.4.6.6 [string::replace] before p.33, change the following signatures as indicated:

    basic_string& replace(const_iterator i1, const_iterator i2,
                          initializer_list<charT> il);
    

1324. Still too many implicit conversions for pair and tuple

Section: 20.3.5.2 [pairs.pair], 20.4.2.1 [tuple.cnstr] Status: New Submitter: Daniel Krügler Opened: 2010-03-20 Last modified: 2010-03-27

View other active issues in [pairs.pair].

View all other issues in [pairs.pair].

View all issues with New status.

Discussion:

In analogy to library defect 811, tuple's variadic constructor

template <class... UTypes>
explicit tuple(UTypes&&... u);

creates the same problem as pair:

#include <tuple>

int main()
{
  std::tuple<char*> p(0);
}

produces a similar compile error for a recent gcc implementation.

I suggest to follow the same resolution path as has been applied to pair's corresponding c'tor, that is require that these c'tors should not participate in overload resolution, if the arguments are not implicitly convertible to the element types.

Further-on both pair and tuple provide converting constructors from different pairs/tuples that should be not available, if the corresponding element types are not implicitly convertible. It seems astonishing that in the following example

struct A {
  explicit A(int);
};

A  a = 1; // Error

std::tuple<A> ta = std::make_tuple(1); // # OK?

the initialization marked with # could be well-formed.

Proposed resolution:

[ Only constraints on constructors are suggested. Adding similar constraints on assignment operators is considered as QoI, because the assigments wouldn't be well-formed anyway. ]

  1. Following 20.3.5.2 [pairs.pair]/5 add a new Remarks element:

    template<class U, class V> pair(const pair<U, V>& p);
    

    5 Effects: Initializes members from the corresponding members of the argument, performing implicit conversions as needed.

    Remarks: This constructor shall not participate in overload resolution unless U is implicitly convertible to first_type and V is implicitly convertible to second_type.

  2. Following 20.3.5.2 [pairs.pair]/6 add a new Remarks element:

    template<class U, class V> pair(pair<U, V>&& p);
    

    6 Effects: The constructor initializes first with std::move(p.first) and second with std::move(p.second).

    Remarks: This constructor shall not participate in overload resolution unless U is implicitly convertible to first_type and V is implicitly convertible to second_type.

  3. Following 20.4.2.1 [tuple.cnstr]/7 add a new Remarks element:

    template <class... UTypes>
    explicit tuple(UTypes&&... u);
    

    6 Requires: Each type in Types shall satisfy the requirements of MoveConstructible (Table 33) from the corresponding type in UTypes. sizeof...(Types) == sizeof...(UTypes).

    7 Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u).

    Remarks: This constructor shall not participate in overload resolution unless each type in UTypes is implicitly convertible to its corresponding type in Types.

  4. Following 20.4.2.1 [tuple.cnstr]/13 add a new Remarks element:

    template <class... UTypes> tuple(const tuple<UTypes...>& u);
    

    12 Requires: Each type in Types shall be constructible from the corresponding type in UTypes. sizeof...(Types) == sizeof...(UTypes).

    13 Effects: Constructs each element of *this with the corresponding element of u.

    Remarks: This constructor shall not participate in overload resolution unless each type in UTypes is implicitly convertible to its corresponding type in Types.

    14 [Note: enable_if can be used to make the converting constructor and assignment operator exist only in the cases where the source and target have the same number of elements. — end note]

  5. Following 20.4.2.1 [tuple.cnstr]/16 add a new Remarks element:

    template <class... UTypes> tuple(tuple<UTypes...>&& u);
    

    15 Requires: Each type in Types shall shall satisfy the requirements of MoveConstructible (Table 33) from the corresponding type in UTypes. sizeof...(Types) == sizeof...(UTypes).

    16 Effects: Move-constructs each element of *this with the corresponding element of u.

    Remarks: This constructor shall not participate in overload resolution unless each type in UTypes is implicitly convertible to its corresponding type in Types.

    [Note: enable_if can be used to make the converting constructor and assignment operator exist only in the cases where the source and target have the same number of elements. — end note]

  6. Following 20.4.2.1 [tuple.cnstr]/18 add a new Remarks element:

    template <class U1, class U2> tuple(const pair<U1, U2>& u);
    

    17 Requires: The first type in Types shall be constructible from U1 and the second type in Types shall be constructible from U2. sizeof...(Types) == 2.

    18 Effects: Constructs the first element with u.first and the second element with u.second.

    Remarks: This constructor shall not participate in overload resolution unless U1 is implicitly convertible to the first type in Types and U2 is implicitly convertible to the second type in Types.

  7. Following 20.4.2.1 [tuple.cnstr]/20 add a new Remarks element:

    template <class U1, class U2> tuple(pair<U1, U2>&& u);
    

    19 Requires: The first type in Types shall shall satisfy the requirements of MoveConstructible(Table 33) from U1 and the second type in Types shall be move-constructible from U2. sizeof...(Types) == 2.

    20 Effects: Constructs the first element with std::move(u.first) and the second element with std::move(u.second)

    Remarks: This constructor shall not participate in overload resolution unless U1 is implicitly convertible to the first type in Types and U2 is implicitly convertible to the second type in Types.


1325. bitset

Section: 20.5.1 [bitset.cons] Status: New Submitter: Christopher Jefferson Opened: 2010-03-07 Last modified: 2010-03-15

View all other issues in [bitset.cons].

View all issues with New status.

Discussion:

As mentioned on the boost mailing list:

The following code, valid in C++03, is broken in C++0x due to ambiguity between the "unsigned long long" and "char*" constructors.

#include <bitset>
std::bitset<10> b(0);

[ The proposed resolution has been reviewed by Stephan T. Lavavej. ]

Proposed resolution:

In WD N3035, 20.5 [template.bitset]/1, replace the fourth bitset constructor:

explicit bitset(const char *str);
template <class charT>
   explicit bitset(
     const charT *str,
     typename basic_string<charT>::size_type pos = 0,
     typename basic_string<charT>::size_type n =
       basic_string<charT>::npos,
     charT zero = charT('0'), charT one = charT('1'));

And in 20.5.1 [bitset.cons]/8:

explicit bitset(const char *str);
Effects: Constructs an object of class bitset<N> as if by bitset(string(str)).
template <class charT>
 explicit
 bitset(const charT *str,
   typename basic_string<charT>::size_type pos = 0,
   typename basic_string<charT>::size_type n =
     basic_string<charT>::npos,
     charT zero = charT('0'), charT one = charT('1'));
Effects: Constructs an object of class bitset<N> as if by bitset(basic_string<charT>(str), pos, n, zero, one).

1326. Missing/wrong preconditions for pair and tuple functions

Section: 20.3.5.2 [pairs.pair], 20.4.2.1 [tuple.cnstr] Status: New Submitter: Daniel Krügler Opened: 2010-03-07 Last modified: 2010-03-27

View other active issues in [pairs.pair].

View all other issues in [pairs.pair].

View all issues with New status.

Discussion:

There are several constructors and creation functions of std::tuple that impose requirements on it's arguments, that are unnecessary restrictive and don't match the intention for the supported argument types. This is related to the fact that tuple is supposed to accept both object types and lvalue-references and the usual MoveConstructible and CopyConstructible requirements are bad descriptions for non-const references. Some examples:

  1. 20.4.2.1 [tuple.cnstr] before p.4 and p.8, resp.:

    explicit tuple(const Types&...);
    
    4 Requires: Each type in Types shall be copy constructible.
    tuple(const tuple& u) = default;
    
    8 Requires: Each type in Types shall satisfy the requirements of CopyConstructible (Table 34).

    A tuple that contains lvalue-references to non-const can never satisfy the CopyConstructible requirements. CopyConstructible requirements refine the MoveConstructible requirements and this would require that these lvalue-references could bind rvalues. But the core language does not allow that. Even, if we would interpret that requirement as referring to the underlying non-reference type, this requirement would be wrong as well, because there is no reason to disallow a type such as

    struct NoMoveNoCopy {
      NoMoveNoCopy(NoMoveNoCopy&&) = delete;
      NoMoveNoCopy(const NoMoveNoCopy&) = delete;
      ...
    }:
    

    for the instantiation of std::tuple<NoMoveNoCopy&> and that of it's copy constructor.

    A more reasonable requirement for this example would be to require that "is_constructible<Ti, const Ti&>::value shall evaluate to true for all Ti in Types". In this case the special reference-folding and const-merging rules of references would make this well-formed in all cases. We could also add the further constraint "if Ti is an object type, it shall satisfy the CopyConstructible requirements", but this additional requirement seems not really to help here. Ignoring it would only mean that if a user would provide a curious object type C that satisfies the std::is_constructible<C, const C&> test, but not the "C is CopyConstructible" test would produce a tuple<C> that does not satisfy the CopyConstructible requirements as well.

  2. 20.4.2.1 [tuple.cnstr] before p.6 and p.10, resp.:

    template <class... UTypes>
    explicit tuple(UTypes&&... u);
    
    6 Requires: Each type in Types shall satisfy the requirements of MoveConstructible (Table 33) from the corresponding type in UTypes. sizeof...(Types) == sizeof...(UTypes).
    tuple(tuple&& u);
    
    10 Requires: Each type in Types shall shall satisfy the requirements of MoveConstructible (Table 33).

    We have a similar problem as in (a): Non-const lvalue-references are intended template arguments for std::tuple, but cannot satisfy the MoveConstructible requirements. In this case the correct requirements would be

    is_constructible<Ti, Ui>::value shall evaluate to true for all Ti in Types and for all Ui in UTypes

    and

    is_constructible<Ti, Ti>::value shall evaluate to true for all Ti in Types

    respectively.

Many std::pair member functions do not add proper requirements, e.g. the default c'tor does not require anything. This is corrected within the suggested resolution. Further-on the P/R has been adapted to the FCD numbering.

[ 2010-03-25 Daniel updated wording: ]

The issue became updated to fix some minor inconsistencies and to ensure a similarly required fix for std::pair, which has the same specification problem as std::tuple, since pair became extended to support reference members as well.

Proposed resolution:

  1. Change 20.3.5.2 [pairs.pair]/1 as indicated [The changes for the effects elements are not normative changes, they just ensure harmonization with existing wording style]:

    constexpr pair();
    

    Requires: first_type and second_type shall satisfy the DefaultConstructible requirements.

    1 Effects: Value-initializes first and second.Initializes its members as if implemented: pair() : first(), second() { }.

  2. Change 20.3.5.2 [pairs.pair]/2 as indicated:

    pair(const T1& x, const T2& y);
    

    Requires: is_constructible<T1, const T1&>::value is true and is_constructible<T2, const T2&>::value is true.

    2 Effects: The constructor initializes first with x and second with y.

  3. Change 20.3.5.2 [pairs.pair]/3 as indicated:

    template<class U, class V> pair(U&& x, V&& y);
    

    Requires: is_constructible<first_type, U>::value is true and is_constructible<second_type, V>::value is true.

    3 Effects: The constructor initializes first with std::forward<U>(x) and second with std::forward<V>(y).

    4 Remarks: If U is not implicitly convertible to first_type or V is not implicitly convertible to second_type this constructor shall not participate in overload resolution.

  4. Change 20.3.5.2 [pairs.pair]/5 as indicated [The change in the effects element should be non-normatively and is in compatible to the change suggestion of 1324]:

    template<class U, class V> pair(const pair<U, V>& p);
    

    Requires: is_constructible<first_type, const U&>::value is true and is_constructible<second_type, const V&>::value is true.

    5 Effects: Initializes members from the corresponding members of the argument, performing implicit conversions as needed.

  5. Change 20.3.5.2 [pairs.pair]/6 as indicated:

    template<class U, class V> pair(pair<U, V>&& p);
    

    Requires: is_constructible<first_type, U>::value is true and is_constructible<second_type, V>::value is true.

    6 Effects: The constructor initializes first with std::moveforward<U>(p.first) and second with std::moveforward<V>(p.second).

  6. Change 20.3.5.2 [pairs.pair]/7+8 as indicated [The deletion in the effects element should be non-normatively]:

    template<class... Args1, class... Args2>
      pair(piecewise_construct_t,
           tuple<Args1...> first_args, tuple<Args2...> second_args);
    

    7 Requires: is_constructible<first_type, Args1...>::value is true and is_constructible<second_type, Args2...>::value is true. All the types in Args1 and Args2 shall be CopyConstructible (Table 35). T1 shall be constructible from Args1. T2 shall be constructible from Args2.

    8 Effects: The constructor initializes first with arguments of types Args1... obtained by forwarding the elements of first_args and initializes second with arguments of types Args2... obtained by forwarding the elements of second_args. (Here, forwarding an element x of type U within a tuple object means calling std::forward<U>(x).) This form of construction, whereby constructor arguments for first and second are each provided in a separate tuple object, is called piecewise construction.

  7. Change 20.3.5.2 [pairs.pair] before 12 as indicated:

    pair& operator=(pair&& p);
    

    Requires: first_type and second_type shall satisfy the MoveAssignable requirements.

    12 Effects: Assigns to first with std::move(p.first) and to second with std::move(p.second).

    13 Returns: *this.

  8. Change [pairs.pair] before 14 as indicated: [The heterogeneous usage of MoveAssignable is actually not defined, but the library uses it at several places, so we follow this tradition until a better term has been agreed on. One alternative could be to write "first_type shall be assignable from an rvalue of U [..]"]

    template<class U, class V> pair& operator=(pair<U, V>&& p);
    

    Requires: first_type shall be MoveAssignable from U and second_type shall be MoveAssignable from V.

    14 Effects: Assigns to first with std::move(p.first) and to second with std::move(p.second).

    15 Returns: *this.

  9. Change 20.4.2.1 [tuple.cnstr]/4+5 as indicated:

    explicit tuple(const Types&...);
    

    4 Requires: is_constructible<Ti, const Ti&>::value == true for eEach type Ti in Types shall be copy constructible.

    5 Effects: Copy iInitializes each element with the value of the corresponding parameter.

  10. Change 20.4.2.1 [tuple.cnstr]/6 as indicated:

    template <class... UTypes>
    explicit tuple(UTypes&&... u);
    

    6 Requires: is_constructible<Ti, Ui>::value == true for eEach type Ti in Types shall satisfy the requirements of MoveConstructible (Table 33) fromand for the corresponding type Ui in UTypes. sizeof...(Types) == sizeof...(UTypes).

    7 Effects: Initializes the elements in the tuple with the corresponding value in std::forward<UTypes>(u).

  11. Change 20.4.2.1 [tuple.cnstr]/8+9 as indicated:

    tuple(const tuple& u) = default;
    

    8 Requires: is_constructible<Ti, const Ti&>::value == true for eEach type Ti in Types shall satisfy the requirements of CopyConstructible(Table 34).

    9 Effects: InitializesCopy constructs each element of *this with the corresponding element of u.

  12. Change 20.4.2.1 [tuple.cnstr]/10+11 as indicated:

    tuple(tuple&& u);
    

    10 Requires: Let i be in [0, sizeof...(Types)) and let Ti be the ith type in Types. Then is_constructible<Ti, Ti>::value shall be true for all i. Each type in Types shall shall satisfy the requirements of MoveConstructible (Table 34).

    11 Effects: For each Ti in Types, initializes the ith Move-constructs each element of *this with the corresponding element of std::forward<Ti>(get<i>(u)).

  13. Change 20.4.2.1 [tuple.cnstr]/15+16 as indicated:

    template <class... UTypes> tuple(tuple<UTypes...>&& u);
    

    15 Requires: Let i be in [0, sizeof...(Types)), Ti be the ith type in Types, and Ui be the ith type in UTypes. Then is_constructible<Ti, Ui>::value shall be true for all i. Each type in Types shall shall satisfy the requirements of MoveConstructible (Table 34) from the corresponding type in UTypes. sizeof...(Types) == sizeof...(UTypes).

    16 Effects: For each type Ti, initializes the ith Move-constructs each element of *this with the corresponding element of std::forward<Ui>(get<i>(u)).

  14. Change 20.4.2.1 [tuple.cnstr]/19+20 as indicated:

    template <class U1, class U2> tuple(pair<U1, U2>&& u);
    

    19 Requires: is_constructible<T1, U1>::value == true for tThe first type T1 in Types shall shall satisfy the requirements of MoveConstructible(Table 33) from U1 and is_constructible<T2, U2>::value == true for the second type T2 in Types shall be move-constructible from U2. sizeof...(Types) == 2.

    20 Effects: InitializesConstructs the first element with std::forward<U1>move(u.first) and the second element with std::forward<U2>move(u.second).

  15. Change 20.4.2.4 [tuple.creation]/9-16 as indicated:

    template <class... TTypes, class... UTypes>
    tuple<TTypes..., UTypes...> tuple_cat(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
    

    9 Requires: is_constructible<Ti, const Ti&>::value == true for each type TiAll the types in TTypes shall be CopyConstructible (Table 34). is_constructible<Ui, const Ui&>::value == true for each type UiAll the types in UTypes shall be CopyConstructible (Table 34).

    10 Returns: A tuple object constructed by initializingcopy constructing its first sizeof...(TTypes) elements from the corresponding elements of t and initializingcopy constructing its last sizeof...(UTypes) elements from the corresponding elements of u.

    template <class... TTypes, class... UTypes>
    tuple<TTypes..., UTypes...> tuple_cat(tuple<TTypes...>&& t, const tuple<UTypes...>& u);
    

    11 Requires: Let i be in [0, sizeof...(TTypes)), Ti be the ith type in Types, j be in [0, sizeof...(UTypes)), and Uj be the jth type in UTypes. is_constructible<Ti, Ti>::value shall be true for each type Ti and is_constructible<Uj, const Uj&>::value shall be true for each type Uj All the types in TTypes shall be MoveConstructible (Table 34). All the types in UTypes shall be CopyConstructible (Table 35).

    12 Returns: A tuple object constructed by initializing the ith element with std::forward<Ti>(get<i>(t)) for all Ti in TTypes and initializing the (j+sizeof...(TTypes))th element with get<j>(u) for all Uj in UTypes. move constructing its first sizeof...(TTypes) elements from the corresponding elements of t and copy constructing its last sizeof...(UTypes) elements from the corresponding elements of u.

    template <class... TTypes, class... UTypes>
    tuple<TTypes..., UTypes...> tuple_cat(const tuple<TTypes...>& t, tuple<UTypes...>&& u);
    

    13 Requires: Let i be in [0, sizeof...(TTypes)), Ti be the ith type in Types, j be in [0, sizeof...(UTypes)), and Uj be the jth type in UTypes. is_constructible<Ti, const Ti&>::value shall be true for each type Ti and is_constructible<Uj, Uj>::value shall be true for each type Uj All the types in TTypes shall be CopyConstructible (Table 35). All the types in UTypes shall be MoveConstructible (Table 34).

    14 Returns: A tuple object constructed by initializing the ith element with get<i>(t) for each type Ti and initializing the (j+sizeof...(TTypes))th element with std::forward<Uj>(get<j>(u)) for each type Uj copy constructing its first sizeof...(TTypes) elements from the corresponding elements of t and move constructing its last sizeof...(UTypes) elements from the corresponding elements of u.

    template <class... TTypes, class... UTypes>
    tuple<TTypes..., UTypes...> tuple_cat(tuple<TTypes...>&& t, tuple<UTypes...>&& u);
    

    15 Requires: Let i be in [0, sizeof...(TTypes)), Ti be the ith type in Types, j be in [0, sizeof...(UTypes)), and Uj be the jth type in UTypes. is_constructible<Ti, Ti>::value shall be true for each type Ti and is_constructible<Uj, Uj>::value shall be true for each type Uj All the types in TTypes shall be MoveConstructible (Table 34). All the types in UTypes shall be MoveConstructible (Table 34).

    16 Returns: A tuple object constructed by initializing the ith element with std::forward<Ti>(get<i>(t)) for each type Ti and initializing the (j+sizeof...(TTypes))th element with std::forward<Uj>(get<j>(u)) for each type Uj move constructing its first sizeof...(TTypes) elements from the corresponding elements of t and move constructing its last sizeof...(UTypes) elements from the corresponding elements of u.


1327. templates defined in <cmath> replacing C macros with the same name

Section: 26.8 [c.math] Status: Open Submitter: Michael Wong Opened: 2010-03-07 Last modified: 2010-03-15

View all other issues in [c.math].

View all issues with Open status.

Discussion:

In 26.8 [c.math]p12 The templates defined in <cmath> replace the C99 macros with the same names. The templates have the following declarations:

namespace std {
template <class T> bool signbit(T x);
template <class T> int fpclassify(T x);
template <class T> bool isfinite(T x);
template <class T> bool isinf(T x);
template <class T> bool isnan(T x);
template <class T> bool isnormal(T x);
template <class T> bool isgreater(T x, T y);
template <class T> bool isgreaterequal(T x, T y);
template <class T> bool isless(T x, T y);
template <class T> bool islessequal(T x, T y);
template <class T> bool islessgreater(T x, T y);
template <class T> bool isunordered(T x, T y);
}

and p13:

13 The templates behave the same as the C99 macros with corresponding names defined in C99 7.12.3, Classification macros, and C99 7.12.14, Comparison macros in the C standard.

The C Std versions look like this:

7.12.14.1/p1:

Synopsis

1 #include <math.h>

int isgreaterequal(real-floating x, real-floating y);

which is not necessarily the same types as is required by C++ since the two parameters may be different. Would it not be better if it were truly aligned with C?

[ 2010 Pittsburgh: Bill to ask WG-14 if heterogeneous support for the two-parameter macros is intended. ]

Proposed resolution:


1328. istream extractors not setting failbit if eofbit is already set

Section: 27.7.1.1.3 [istream::sentry] Status: New Submitter: Paolo Carlini Opened: 2010-02-17 Last modified: 2010-03-09

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

View all issues with New status.

Discussion:

basing on the recent discussion on the library reflector, see c++std-lib-27728 and follow ups, I hereby formally ask for LWG 419 to be re-opened, the rationale being that according to the current specifications, per n3000, it seems actually impossible to seek away from end of file, contrary to the rationale which led 342 to its closure as NAD. My request is also supported by Martin Sebor, and I'd like also to add, as tentative proposed resolution for the re-opened issue, the wording suggested by Sebor, thus, change the beginning of 27.7.1.1.3 [istream::sentry]/2, to:

2 Effects: If (!noskipws && !is.good()) is false true, calls is.setstate(failbit). Otherwise prepares for formatted or unformatted input. ...

Proposed resolution:

Change 27.7.1.1.3 [istream::sentry]/2:

2 Effects: If (!noskipws && !is.good()) is false true, calls is.setstate(failbit). Otherwise prepares for formatted or unformatted input. ...

1330. Move container requirements into requirements tables

Section: 23.2 [container.requirements] Status: New Submitter: Nicolai Josuttis Opened: 2010-03-10 Last modified: 2010-03-10

View all other issues in [container.requirements].

View all issues with New status.

Discussion:

Abstract:

In general, it seems that in a couple of places container behavior is not described in requirement tables although it is a general behavior.

History:

Issue 676 added move semantics to unordered containers. For the added insert functions the Editor requested to put their semantic description into a requirements table rather than describing them for each container individually. The text however was taken from the associative containers, where we also have the semantics for each container described. Also, 1034 is to some extend requesting a clarification of the requirement tables and it turned out that in other places we have the same problem (e.g. we have no general requirement for type pointer and const_pointer although each container has them with issue 1306).

From my personal list of functions in requirement tables and containers, the following types/functions are missing in requirement tables:

As a special case, we lack the following requirements for all sequence containers BUT array (so special wording or a new container category is required):

Note that we also might have to add additional requirements on other places for sequence containers because having an allocator requires additional statements for the treatment of the allocators. E.g. swap for containers with allocators is not specified in any requirement table.

And finally, if we have the requirements in the requirements tables, we can remove the corresponding descriptions for the individual container. However, note that sequence container requirements have NO complexity column, so that we still need container specific descriptions for the functions listed there.

Proposed resolution:


1331. incorporate move special member functions into library

Section: 17 [library] Status: New Submitter: Martin Sebor Opened: 2010-03-10 Last modified: 2010-03-11

View other active issues in [library].

View all other issues in [library].

View all issues with New status.

Discussion:

Review the library portion of the spec and incorporate the newly added core feature Move Special Member Functions (N3044).

Proposed resolution:


1332. Let Hash objects throw!

Section: 20.2.4 [hash.requirements] Status: New Submitter: Daniel Krügler Opened: 2010-03-26 Last modified: 2010-03-27

View all issues with New status.

Discussion:

The currently added Hash requirements demand in Table 40 — Hash requirements [hash]:

Table 40 — Hash requirements [hash]
Expression Return type Requirement
h(k) size_t Shall not throw exceptions. [..]

While it surely is a generally accepted idea that hash function objects should not throw exceptions, this basic constraint for such a fundamental requirement set does neither match the current library policy nor real world cases:

  1. There are little known situations where a swap or move operation may throw an exception and in some popular domains such functions are required not to throw. But the library invested already efforts for good reasons to require "working" container implementations in the presence of throwing move or swap operations, see e.g. 23.2.4.1 [associative.reqmts.except], 23.2.5.1 [unord.req.except].
  2. The container library is already specified to cope with potentially throwing comparers, predicates, and hash function objects, see above.
  3. The new definition goes beyond the original hash requirements as specified by SGI library in regard to the exception requirement:

    http://www.sgi.com/tech/stl/HashFunction.html
  4. There are indeed real-world examples of potentially throwing hash functions, typically when the proxy pattern is used and when the to-be hashed proxied instance is some volatile object, e.g. a file or internet resource, that might suddenly be unavailable at the time of hashing.
  5. With the new noexcept language facility libraries can still take advantage of no-throw guarantees of hasher functions with stricter guarantees.

Even though the majority of all known move, swap, and hash functions won't throw and in some cases must not throw, it seems like unnecessary over-constraining the definition of a Hash functor not to propagate exceptions in any case and it contradicts the general principle of C++ to impose such a requirement for this kind of fundamental requirement.

Proposed resolution:

  1. Change Table 40 — Hash requirements [hash] as indicated:

    Table 40 — Hash requirements [hash]
    Expression Return type Requirement
    h(k) size_t Shall not throw exceptions. [..]
  2. Add to 20.8.15 [unord.hash]/1 a new bullet:

    1 The unordered associative containers defined in Clause 23.5 use specializations of the class template hash as the default hash function. For all object types Key for which there exists a specialization hash<Key>, the instantiation hash<Key> shall:

    • satisfy the Hash requirements (20.2.4), with Key as the function call argument type, the DefaultConstructible requirements (33), the CopyAssignable requirements (37),
    • be swappable (20.2.2) for lvalues,
    • provide two nested types result_type and argument_type which shall be synonyms for size_t and Key, respectively,
    • satisfy the requirement that if k1 == k2 is true, h(k1) == h(k2) is also true, where h is an object of type hash<Key> and k1 and k2 are objects of type Key,.
    • satisfy the requirement that the expression h(k), where h is an object of type hash<Key> and k is an object of type Key, shall not throw an expression, unless hash<Key> is a user-defined specialization that depends on at least one user-defined type.

1333. Missing forwarding during std::function invocation

Section: 20.8.14.2.4 [func.wrap.func.inv] Status: New Submitter: Daniel Krügler Opened: 2010-03-26 Last modified: 2010-03-27

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

View all issues with New status.

Discussion:

The current wording of 20.8.14.2.4 [func.wrap.func.inv]/1:

R operator()(ArgTypes... args) const
Effects: INVOKE(f, t1, t2, ..., tN, R) (20.8.2), where f is the target object (20.8.1) of *this and t1, t2, ..., tN are the values in args....

uses an unclear relation between the actual args and the used variables ti. It should be made clear, that std::forward has to be used to conserve the expression lvalueness.

Proposed resolution:

Change 20.8.14.2.4 [func.wrap.func.inv]/1+2 as indicated:

R operator()(ArgTypes... args) const

1 Effects:: INVOKE(f, std::forward<ArgTypes>(args)...t1, t2, ..., tN, R) (20.8.2), where f is the target object (20.8.1) of *this and t1, t2, ..., tN are the values in args....

2 Returns: Nothing if R is void, otherwise the return value of INVOKE(f, std::forward<ArgTypes>(args)...t1, t2, ..., tN, R).

3 Throws: bad_function_call if !*this; otherwise, any exception thrown by the wrapped callable object.


1334. Insert iterators are broken for some proxy containers compared to C++03

Section: 24.5.2.2.2 [back.insert.iter.op=], 24.5.2.4.2 [front.insert.iter.op=], X [insert.insert.iter.op=] Status: New Submitter: Daniel Krügler Opened: 2010-03-28 Last modified: 2010-03-28

View all issues with New status.

Discussion:

In C++03 this was valid code:

#include <vector>
#include <iterator>

int main() {
  typedef std::vector<bool> Cont;
  Cont c;
  std::back_insert_iterator<Cont> it = std::back_inserter(c);
  *it = true;
}

In C++0x this code does no longer compile because of an ambiguity error for this operator= overload pair:

back_insert_iterator<Container>&
operator=(typename Container::const_reference value);

back_insert_iterator<Container>&
operator=(typename Container::value_type&& value);

This is so, because for proxy-containers like std::vector<bool> the const_reference usually is a non-reference type and in this case it's identical to Container::value_type, thus forming the ambiguous overload pair

back_insert_iterator<Container>&
operator=(bool value);

back_insert_iterator<Container>&
operator=(bool&& value);

The same problem exists for std::back_insert_iterator, std::front_insert_iterator, and std::insert_iterator.

One possible fix would be to require that const_reference of a proxy container must not be the same as the value_type, but this would break earlier valid code. The alternative would be to change the first signature to

back_insert_iterator<Container>&
operator=(const typename Container::const_reference& value);

This would have the effect that this signature always expects an lvalue or rvalue, but it would not create an ambiguity relative to the second form with rvalue-references. [For all non-proxy containers the signature will be the same as before due to reference-collapsing and const folding rules]

Proposed resolution:

  1. Change 24.5.2.1 [back.insert.iterator], class back_insert_iterator synopsis as indicated:

    template <class Container>
    class back_insert_iterator :
      public iterator<output_iterator_tag,void,void,void,void> {
    protected:
      Container* container;
    public:
      [..]
      back_insert_iterator<Container>&
        operator=(const typename Container::const_reference& value);
      back_insert_iterator<Container>&
        operator=(typename Container::value_type&& value);
      [..]
    };
    
  2. Change 24.5.2.2.2 [back.insert.iter.op=] before p. 1 as indicated:

    back_insert_iterator<Container>&
      operator=(const typename Container::const_reference& value);
    
  3. Change 24.5.2.3 [front.insert.iterator], class front_insert_iterator synposis as indicated:

    template <class Container>
    class front_insert_iterator :
      public iterator<output_iterator_tag,void,void,void,void> {
    protected:
      Container* container;
    public:
      [..]
      front_insert_iterator<Container>&
        operator=(const typename Container::const_reference& value);
      front_insert_iterator<Container>&
        operator=(typename Container::value_type&& value);
      [..]
    };
    
  4. Change 24.5.2.4.2 [front.insert.iter.op=] before p.1 as indicated:

    front_insert_iterator<Container>&
      operator=(const typename Container::const_reference& value);
    
  5. Change 24.5.2.5 [insert.iterator], class insert_iterator synopsis as indicated:

    template <class Container>
    class insert_iterator :
      public iterator<output_iterator_tag,void,void,void,void> {
    protected:
      Container* container;
      typename Container::iterator iter;
    public:
      [..]
      insert_iterator<Container>&
        operator=(const typename Container::const_reference& value);
      insert_iterator<Container>&
        operator=(typename Container::value_type&& value);
      [..]
    };
    
  6. Change 24.5.2.6.2 [insert.iter.op=] before p. 1 as indicated:

    insert_iterator<Container>&
      operator=(const typename Container::const_reference& value);