Revised 2016-07-11 at 06:07:25 UTC
Section: 17 [library] Status: Open Submitter: David Abrahams Opened: 2009-07-14 Last modified: 2016-02-12
Priority: 3
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.
[ 2010 Rapperswil: ]
This issue is quite vague, so it is difficult to know if and when it has been resolved. John Lakos wrote a paper covering this area a while back, and there is a real interest in providing some sort of clean-up in the future. We need a more clearly draughted issues with an addressable set of concerns, ideally with a paper proposing a resolution, but for a future revision of the standard. Move to Tentatively NAD Future.
[ Moved to NAD Future at 2010-11 Batavia ]
Proposed resolution:
Section: 23.2.5 [unord.req] Status: Open Submitter: Pablo Halpern Opened: 2009-07-17 Last modified: 2016-02-12
Priority: 3
View other active issues in [unord.req].
View all other issues in [unord.req].
View all issues with Open status.
Discussion:
When I look at the unordered_* constructors, I think the complexity is poorly described and does not follow the style of the rest of the standard.
The complexity for the default constructor is specified as constant. Actually, it is proportional to n, but there are no invocations of value_type constructors or other value_type operations.
For the iterator-based constructor the complexity should be:
Complexity: exactly n calls to construct value_type from InputIterator::value_type (where n = distance(f,l)). The number of calls to key_equal::operator() is proportional to n in the average case and n*n in the worst case.
[ 2010 Rapperswil: ]
Concern that the current wording may require O(1) where that cannot be delivered. We need to look at both the clause 23 requirements tables and the constructor description of each unordered container to be sure.
Howard suggests NAD Editorial as we updated the container requirement tables since this issue was written.
Daniel offers to look deeper, and hopefully produce wording addressing any outstanding concerns at the next meeting.
Move to Open.
[2011-02-26: Daniel provides wording]
I strongly suggest to clean-up the differences between requirement tables and individual specifications. In the usual way, the most specific specifications wins, which is in this case the wrong one. In regard to the concern expressed about missing DefaultConstructible requirements of the value type I disagree: The function argument n is no size-control parameter, but only some effective capacity parameter: No elements will be value-initialized by these constructors. The necessary requirement for the value type, EmplaceConstructible into *this, is already listed in Table 103 — Unordered associative container requirements. Another part of the proposed resolution is the fact that there is an inconsistency of the complexity counting when both a range and a bucket count is involved compared to constructions where only bucket counts are provided: E.g. the construction X a(n); has a complexity of n bucket allocations, but this part of the work is omitted for X a(i, j, n);, even though it is considerable larger (in the average case) for n ≫ distance(i, j).
[2011-03-24 Madrid meeting]
Move to deferred
[ 2011 Bloomington ]
The proposed wording looks good. Move to Review.
[2012, Kona]
Fix up some presentation issues with the wording, combining the big-O expressions into single expressions rather than the sum of two separate big-Os.
Strike "constant or linear", prefer "linear in the number of buckets". This allows for number of buckets being larger than requested n as well.
Default n to "unspecified" rather than "implementation-defined". It seems an un-necessary burden asking vendors to document a quantity that is easily determined through the public API of these classes.
Replace distance(f,l) with "number of elements in the range [f,l)"
Retain in Review with the updated wording
[2012, Portland: Move to Open]
The wording still does not call out Pablo's original concern, that the element constructor is called no more than N times, and that the N squared term applies to moves during rehash.
Inconsistent use of O(n)+O(N) vs. O(n+N), with a preference for the former.
AJM to update wording with a reference to "no more than N element constructor calls".
Matt concerned that calling out the O(n) requirements is noise, and dangerous noise in suggesting a precision we do not mean. The cost of constructing a bucket is very different to constructing an element of user-supplied type.
AJM notes that if there are multiple rehashes, the 'n' complexity is probably not linear.
Matt suggests back to Open, Pablo suggests potentially NAD if we keep revisitting without achieving a resolution.
Matt suggests complexity we are concerned with is the number of operations, such as constructing elements, moving nodes, and comparing/hashing keys. We are less concerned with constructing buckets, which are generally noise in this bigger picture.
[2015-01-29 Telecon]
AM: essentially correct, but do we want to complicate the spec?
HH: Pablo has given us permission to NAD it JM: when I look at the first change in the P/R I find it mildly disturbing that the existing wording says you have a constant time constructor with a single element even if your n is 10^6, so I think adding this change makes people aware there might be a large cost in initializing the hash table, even though it doesn't show up in user-visible constructions. HH: one way to avoid that problem is make the default ctor noexcept. Then the container isn't allowed to create an arbitrarily large hash table AM: but this is the constructor where the user provides n MC: happy with the changes, except I agree with the editorial recommendation to keep the two 𝒪s separate. JW: yes, the constant 'k' is different in 𝒪(n) and 𝒪(N) GR: do we want to talk about buckets at all JM: yes, good to highlight that bucket construction might be a significant cost HH: suggest we take the suggestion to split 𝒪(n+N) to 𝒪(n)+𝒪(N) and move to Tentatively Ready GR: 23.2.1p2 says all complexity requirements are stated solely in terms of the number of operations on the contained object, so we shouldn't be stating complexity in terms of the hash table initialization HH: channeling Pete, there's an implicit "unless otherwise specified" everywhere. VV: seem to be requesting modifications that render this not Tentatively Ready GR: I think it can't be T/R AM: make the editorial recommendation, consider fixing 23.2.1/3 to give us permission to state complexity in terms of bucket initialization HH: only set it to Review after we get new wording to review[2015-02 Cologne]
Update wording, revisit later.
Proposed resolution:
Modify the following rows in Table 103 — Unordered associative container requirements to add the explicit bucket allocation overhead of some constructions. As editorial recommendation it is suggested not to shorten the sum 𝒪(n) + 𝒪(N) to 𝒪(n + N), because two different work units are involved.
Table 103 — Unordered associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity … X(i, j, n, hf, eq)
X a(i, j, n, hf, eq)X …
Effects: Constructs an empty container with at least n
buckets, using hf as the hash function and eq as the key
equality predicate, and inserts elements from [i, j) into it.Average case 𝒪(n + N) (N is distance(i, j)),
worst case 𝒪(n) + 𝒪(N2)X(i, j, n, hf)
X a(i, j, n, hf)X …
Effects: Constructs an empty container with at least n
buckets, using hf as the hash function and key_equal() as the key
equality predicate, and inserts elements from [i, j) into it.Average case 𝒪(n + N) (N is distance(i, j)),
worst case 𝒪(n + N2)X(i, j, n)
X a(i, j, n)X …
Effects: Constructs an empty container with at least n
buckets, using hasher() as the hash function and key_equal() as the key
equality predicate, and inserts elements from [i, j) into it.Average case 𝒪(n + N) (N is distance(i, j)),
worst case 𝒪(n + N2)…
Modify 23.5.4.2 [unord.map.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):
explicit unordered_map(size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());1 Effects: Constructs an empty unordered_map using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecified
impldefdefault number of buckets in unordered_map. max_load_factor() returns 1.0.2 Complexity:
ConstantLinear in the number of buckets.
template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());3 Effects: Constructs an empty unordered_map using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecified
impldefdefault number of buckets in unordered_map. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.4 Complexity:
Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to number of elements in the range [f,l).
Modify 23.5.5.2 [unord.multimap.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):
explicit unordered_multimap(size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());1 Effects: Constructs an empty unordered_multimap using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecified
impldefdefault number of buckets in unordered_multimap. max_load_factor() returns 1.0.2 Complexity:
ConstantLinear in the number of buckets.
template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());3 Effects: Constructs an empty unordered_multimap using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecified
impldefdefault number of buckets in unordered_multimap. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.4 Complexity:
Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to number of elements in the range [f,l).
Modify 23.5.6.2 [unord.set.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):
explicit unordered_set(size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());1 Effects: Constructs an empty unordered_set using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecified
impldefdefault number of buckets in unordered_set. max_load_factor() returns 1.0.2 Complexity:
ConstantLinear in the number of buckets.
template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());3 Effects: Constructs an empty unordered_set using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecified
impldefdefault number of buckets in unordered_set. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.4 Complexity:
Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to number of elements in the range [f,l).
Modify 23.5.7.2 [unord.multiset.cnstr] p. 1-4 as indicated (The edits of p. 1 and p. 3 attempt to fix some editorial oversight.):
explicit unordered_multiset(size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());1 Effects: Constructs an empty unordered_multiset using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecified
impldefdefault number of buckets in unordered_multiset. max_load_factor() returns 1.0.2 Complexity:
ConstantLinear in the number of buckets.
template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());3 Effects: Constructs an empty unordered_multiset using the specified hash function, key equality function, and allocator, and using at least n buckets. If n is not provided, the number of buckets is unspecified
impldefdefault number of buckets in unordered_multiset. Then inserts elements from the range [f, l). max_load_factor() returns 1.0.4 Complexity:
Average case linear, worst case quadraticLinear in the number of buckets. In the average case linear in N and in the worst case quadratic in N to insert the elements, where N is equal to number of elements in the range [f,l).
Section: 24.2 [iterator.requirements] Status: Open Submitter: Daniel Krügler Opened: 2009-09-19 Last modified: 2016-02-12
Priority: 4
View other active issues in [iterator.requirements].
View all other issues in [iterator.requirements].
View all issues with Open 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.10.10 [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 99 [allocator.concepts.members] of N2914 we find:
pointer X::allocate(size_type n);11 Returns: a pointer to the allocated memory. [Note: if n == 0, the return value is unspecified. —end note]
[..]
void X::deallocate(pointer p, size_type n);Preconditions: p shall be a non-singular pointer value obtained from a call to allocate() on this allocator or one that compares equal to it.
If singular pointer value would include null pointers this make the preconditions unclear if the pointer value is a result of allocate(0): Since the return value is unspecified, it could be a null pointer. Does that mean that programmers need to check the pointer value for a null value before calling deallocate?
[ 2010-11-09 Daniel comments: ]
A later paper is in preparation.
[ 2010 Batavia: ]
Doesn't need to be resolved for Ox
[2014-02-20 Re-open Deferred issues as Priority 4]
Consider to await the paper.
Proposed resolution:
Section: 24.2.4 [output.iterators] Status: Open Submitter: Daniel Krügler Opened: 2011-02-27 Last modified: 2016-02-12
Priority: 3
View other active issues in [output.iterators].
View all other issues in [output.iterators].
View all issues with Open status.
Discussion:
During the Pittsburgh meeting the proposal N3066 became accepted because it fixed several severe issues related to the iterator specification. But the current working draft (N3225) does not reflect all these changes. Since I'm unaware whether every correction can be done editorial, this issue is submitted to take care of that. To give one example: All expressions of Table 108 — "Output iterator requirements" have a post-condition that the iterator is incrementable. This is impossible, because it would exclude any finite sequence that is accessed by an output iterator, such as a pointer to a C array. The N3066 wording changes did not have these effects.
[2011-03-01: Daniel comments:]
This issue has some overlap with the issue 2038 and I would prefer if we could solve both at one location. I suggest the following approach:
The terms dereferencable and incrementable could be defined in a more general way not restricted to iterators (similar to the concepts HasDereference and HasPreincrement from working draft N2914). But on the other hand, all current usages of dereferencable and incrementable are involved with types that satisfy iterator requirements. Thus, I believe that it is sufficient for C++0x to add corresponding definitions to 24.2.1 [iterator.requirements.general] and to let all previous usages of these terms refer to this sub-clause. Since the same problem occurs with the past-the-end iterator, this proposal suggest providing similar references to usages that precede its definition as well.
We also need to ensure that all iterator expressions get either an operational semantics in terms of others or we need to add missing pre- and post-conditions. E.g. we have the following ones without semantics:
*r++ = o // output iterator *r-- // bidirectional iterator
According to the SGI specification these correspond to
{ *r = o; ++r; } // output iterator { reference tmp = *r; --r; return tmp; } // bidirectional iterator
respectively. Please note especially the latter expression for bidirectional iterator. It fixes a problem that we have for forward iterator as well: Both these iterator categories provide stronger guarantees than input iterator, because the result of the dereference operation is reference, and not only convertible to the value type (The exact form from the SGI documentation does not correctly refer to reference).
[2011-03-14: Daniel comments and updates the suggested wording]
In addition to the before mentioned necessary changes there is another one need, which became obvious due to issue 2042: forward_list<>::before_begin() returns an iterator value which is not dereferencable, but obviously the intention is that it should be incrementable. This leads to the conclusion that imposing dereferencable as a requirement for the expressions ++r is wrong: We only need the iterator to be incrementable. A similar conclusion applies to the expression --r of bidirectional iterators.
[ 2011 Bloomington ]
Consensus this is the correct direction, but there are (potentially) missing incrementable preconditions on some table rows, and the Remarks on when an output iterator becomes dereferencable are probably better handled outside the table, in a manner similar to the way we word for input iterators.
There was some concern about redundant pre-conditions when the operational semantic is defined in terms of operations that have preconditions, and a similar level of concern over dropping such redundancies vs. applying a consistent level of redundant specification in all the iterator tables. Wording clean-up in either direction would be welcome.
[2011-08-18: Daniel adapts the proposed resolution to honor the Bloomington request]
There is only a small number of further changes suggested to get rid of superfluous requirements and essentially non-normative assertions. Operations should not have extra pre-conditions, if defined by "in-terms-of" semantics, see e.g. a != b or a->m for Table 107. Further, some remarks, that do not impose anything or say nothing new have been removed, because I could not find anything helpful they provide. E.g. consider the remarks for Table 108 for the operations dereference-assignment and preincrement: They don't provide additional information say nothing surprising. With the new pre-conditions and post-conditions it is implied what the remarks intend to say.
[ 2011-11-03: Some observations from Alexander Stepanov via c++std-lib-31405 ]
The following sentence is dropped from the standard section on OutputIterators:
"In particular, the following two conditions should hold: first, any iterator value should be assigned through before it is incremented (this is, for an output iterator i, i++; i++; is not a valid code sequence); second, any value of an output iterator may have at most one active copy at any given time (for example, i = j; *++i = a; *j = b; is not a valid code sequence)."[ 2011-11-04: Daniel comments and improves the wording ]
In regard to the first part of the comment, the intention of the newly proposed wording was to make clear that for the expression
*r = o
we have the precondition dereferenceable and the post-condition incrementable. And for the expression
++r
we have the precondition incrementable and the post-condition dereferenceable or past-the-end. This should not allow for a sequence like i++; i++; but I agree that it doesn't exactly say that.
In regard to the second point: To make this point clearer, I suggest to add a similar additional wording as we already have for input iterator to the "Assertion/note" column of the expression ++r: "Post: any copies of the previous value of r are no longer required to be dereferenceable or incrementable." The proposed has been updated to honor the observations of Alexander Stepanov.[2015-02 Cologne]
The matter is complicated, Daniel volunteers to write a paper.
Proposed resolution:
Add a reference to 24.2.1 [iterator.requirements.general] to the following parts of the library preceding Clause 24 Iterators library: (I stopped from 23.2.5 [unord.req] on, because the remaining references are the concrete containers)
17.6.3.2 [swappable.requirements] p5:
-5- A type X satisfying any of the iterator requirements (24.2) is ValueSwappable if, for any dereferenceable (24.2.1 [iterator.requirements.general]) object x of type X, *x is swappable.
17.6.3.5 [allocator.requirements], Table 27 — "Descriptive variable definitions", row with the expression c:
a dereferenceable (24.2.1 [iterator.requirements.general]) pointer of type C*
20.10.3.2 [pointer.traits.functions]:
Returns: The first template function returns a dereferenceable (24.2.1 [iterator.requirements.general]) pointer to r obtained by calling Ptr::pointer_to(r); […]
21.3.1.3 [string.iterators] p. 2:
Returns: An iterator which is the past-the-end value (24.2.1 [iterator.requirements.general]).
22.4.5.1.2 [locale.time.get.virtuals] p. 11:
iter_type do_get(iter_type s, iter_type end, ios_base& f, ios_base::iostate& err, tm *t, char format, char modifier) const;Requires: t shall be dereferenceable (24.2.1 [iterator.requirements.general]).
23.2.1 [container.requirements.general] p. 6:
[…] end() returns an iterator which is the past-the-end (24.2.1 [iterator.requirements.general]) value for the container. […]
23.2.3 [sequence.reqmts] p. 3:
[…] q denotes a valid dereferenceable (24.2.1 [iterator.requirements.general]) const iterator to a, […]
23.2.4 [associative.reqmts] p. 8 (I omit intentionally one further reference in the same sub-clause):
[…] q denotes a valid dereferenceable (24.2.1 [iterator.requirements.general]) const iterator to a, […]
23.2.5 [unord.req] p. 10 (I omit intentionally one further reference in the same sub-clause):
[…] q and q1 are valid dereferenceable (24.2.1 [iterator.requirements.general]) const iterators to a, […]
Edit 24.2.1 [iterator.requirements.general] p. 5 as indicated (The intent is to properly define incrementable and to ensure some further library guarantee related to past-the-end iterator values):
-5- Just as a regular pointer to an array guarantees that there is a pointer value pointing past the last element of the array, so for any iterator type there is an iterator value that points past the last element of a corresponding sequence. These values are called past-the-end values. Values of an iterator i for which the expression *i is defined are called dereferenceable. Values of an iterator i for which the expression ++i is defined are called incrementable. The library never assumes that past-the-end values are dereferenceable or incrementable. Iterators can also have singular values that are not associated with any sequence. […]
Modify the column contents of Table 106 — "Iterator requirements", 24.2.2 [iterator.iterators], as indicated:
Table 106 — Iterator requirements Expression Return type Operational semantics Assertion/note
pre-/post-condition*r reference pre: r is dereferenceable. ++r X& pre: r is incrementable.
Modify the column contents of Table 107 — "Input iterator requirements", 24.2.3 [input.iterators], as indicated [Rationale: The wording changes attempt to define a minimal "independent" set of operations, namely *a and ++r, and to specify the semantics of the remaining ones. This approach seems to be in agreement with the original SGI specification — end rationale]:
Table 107 — Input iterator requirements (in addition to Iterator) Expression Return type Operational semantics Assertion/note
pre-/post-conditiona != b contextually
convertible to bool!(a == b) pre: (a, b) is in the domain
of ==.*a convertible to T pre: a is dereferenceable.
The expression
(void)*a, *a is equivalent
to *a.
If a == b and (a,b) is in
the domain of == then *a is
equivalent to *b.a->m (*a).m pre: a is dereferenceable.++r X& pre: r is dereferenceableincrementable.
post: r is dereferenceable or
r is past-the-end.
post: any copies of the
previous value of r are no
longer required either to be
dereferenceable, incrementable,
or to be in the domain of ==.(void)r++ (void)++r equivalent to (void)++r*r++ convertible to T { T tmp = *r;
++r;
return tmp; }
Modify the column contents of Table 108 — "Output iterator requirements", 24.2.4 [output.iterators], as indicated [Rationale: The wording changes attempt to define a minimal "independent" set of operations, namely *r = o and ++r, and to specify the semantics of the remaining ones. This approach seems to be in agreement with the original SGI specification — end rationale]:
Table 108 — Output iterator requirements (in addition to Iterator) Expression Return type Operational semantics Assertion/note
pre-/post-condition*r = o result is not used pre: r is dereferenceable.
Remark: After this operation
r is not required to be
dereferenceable and any copies of
the previous value of r are no
longer required to be dereferenceable
or incrementable.
post: r is incrementable.++r X& pre: r is incrementable.
&r == &++r.
Remark: After this operationRemark: After this operation
r is not required to be
dereferenceable.
r is not required to be
incrementable and any copies of
the previous value of r are no
longer required to be dereferenceable
or incrementable.
post: r is dereferenceable
or r is past-the-endincrementable.
r++ convertible to const X& { X tmp = r;
++r;
return tmp; }Remark: After this operation
r is not required to be
dereferenceable.
post: r is incrementable.*r++ = o result is not used { *r = o; ++r; } Remark: After this operation
r is not required to be
dereferenceable.
post: r is incrementable.
Modify the column contents of Table 109 — "Forward iterator requirements", 24.2.5 [forward.iterators], as indicated [Rationale: Since the return type of the expression *r++ is now guaranteed to be type reference, the implied operational semantics from input iterator based on value copies is wrong — end rationale]
Table 109 — Forward iterator requirements (in addition to input iterator) Expression Return type Operational semantics Assertion/note
pre-/post-conditionr++ convertible to const X& { X tmp = r;
++r;
return tmp; }*r++ reference { reference tmp = *r;
++r;
return tmp; }
Modify the column contents of Table 110 — "Bidirectional iterator requirements", 24.2.6 [bidirectional.iterators], as indicated:
Table 110 — Bidirectional iterator requirements (in addition to forward iterator) Expression Return type Operational semantics Assertion/note
pre-/post-condition--r X& pre: there exists s such that
r == ++s.
post: r isdereferenceableincrementable.
--(++r) == r.
--r == --s implies r == s.
&r == &--r.r-- convertible to const X& { X tmp = r;
--r;
return tmp; }*r-- reference { reference tmp = *r;
--r;
return tmp; }
Section: 24.2.4 [output.iterators] Status: Open Submitter: Pete Becker Opened: 2011-02-27 Last modified: 2016-02-12
Priority: 3
View other active issues in [output.iterators].
View all other issues in [output.iterators].
View all issues with Open status.
Discussion:
In comp.lang.c++, Vicente Botet raises the following questions:
"In "24.2.4 Output iterators" there are 3 uses of incrementable. I've not found the definition. Could some one point me where it is defined?
Something similar occurs with dereferenceable. While the definition is given in "24.2.1 In general" it is used several times before. Shouldn't these definitions be moved to some previous section?"
He's right: both terms are used without being properly defined.
There is no definition of "incrementable". While there is a definition of "dereferenceable", it is, in fact, a definition of "dereferenceable iterator". "dereferenceable" is used throughout Clause 23 (Containers) before its definition in Clause 24. In almost all cases it's referring to iterators, but in 17.6.3.2 [swappable.requirements] there is a mention of "dereferenceable object"; in 17.6.3.5 [allocator.requirements] the table of Descriptive variable definitions refers to a "dereferenceable pointer"; 20.10.3.2 [pointer.traits.functions] refers to a "dereferenceable pointer"; in 22.4.5.1.2 [locale.time.get.virtuals]/11 (do_get) there is a requirement that a pointer "shall be dereferenceable". In those specific cases it is not defined.[2011-03-02: Daniel comments:]
I believe that the currently proposed resolution of issue 2035 solves this issue as well.
[ 2011 Bloomington ]
Agree with Daniel, this will be handled by the resolution of 2035.
Proposed resolution:
Section: 20.14.12.2 [func.wrap.func], 20.14.12.2.2 [func.wrap.func.mod] Status: Open Submitter: Daniel Krügler Opened: 2011-05-28 Last modified: 2016-02-12
Priority: 2
View other active issues in [func.wrap.func].
View all other issues in [func.wrap.func].
View all issues with Open status.
Discussion:
Howard Hinnant observed in reflector message c++std-lib-30841 that 20.14.12.2 [func.wrap.func] makes the member swap noexcept, even though the non-member swap is not noexcept.
The latter was an outcome of the discussions during the Batavia meeting and the Madrid meeting involving LWG 1349, which seems to indicate that the remaining noexcept specifier at the member swap is incorrect and should be removed. But if we allow for a potentially throwing member swap of std::function, this causes another conflict with the exception specification for the following member function:template<class F> function& operator=(reference_wrapper<F> f) noexcept;
Effects: function(f).swap(*this);
Note that in this example the sub-expression function(f) does not cause any problems, because of the nothrow-guarantee given in 20.14.12.2.1 [func.wrap.func.con] p. 10. The problem is located in the usage of the swap which could potentially throw given the general latitude.
So, either the Madrid meeting decision need to be revised (and both member and free swap of std::function should be noexcept), or this function needs to be adapted as well, e.g. by taking the exception-specification away or by changing the semantics. One argument for "swap-may-throw" would be to allow for small-object optimization techniques where the copy of the target may throw. But given the fact that the swap function has been guaranteed to be "Throws: Nothing" from TR1 on, it seems to me that that there would still be opportunities to perform small-object optimizations just restricted to the set of target copies that cannot throw. In my opinion member swap of std::function has always been intended to be no-throw, because otherwise there would be no good technical reason to specify the effects of several member functions in terms of the "construct-swap" idiom (There are three functions that are defined this way), which provides the strong exception safety in this case. I suggest to enforce that both member swap and non-member swap of std::function are nothrow functions as it had been guaranteed since TR1 on.[ 2011 Bloomington ]
Dietmar: May not be swappable in the first place.
Alisdair: This is wide contact. Then we should be taking noexcept off instead of putting it on. This is preferred resolution.
Pablo: This is bigger issue. Specification of assignment in terms of swap is suspect to begin with. It is over specification. How this was applied to string is a better example to work from.
Pablo: Two problems: inconsistency that should be fixed (neither should have noexcept), the other issues is that assignment should not be specified in terms of swap. There are cases where assignment should succeed where swap would fail. This is easier with string as it should follow container rules.
Action Item (Alisdair): There are a few more issues found to file.
Dave: This is because of allocators? The allocator makes this not work.
Howard: There is a type erased allocator in shared_ptr. There is a noexcept allocator in shared_ptr.
Pablo: shared_ptr is a different case. There are shared semantics and the allocator does move around. A function does not have shared semantics.
Alisdair: Function objects think they have unique ownership.
Howard: In function we specify semantics with copy construction and swap.
Action Item (Pablo): Write this up better (why assignment should not be defined in terms of swap)
Howard: Not having trouble making function constructor no throw.
Dietmar: Function must allocate memory.
Howard: Does not put stuff that will throw on copy or swap in small object optimization. Put those on heap. Storing allocator, but has to be no throw copy constructable.
Pablo: Are you allowed to or required to swap or move allocators in case or swap or move.
Dave: An allocator that is type erased should be different...
Pablo: it is
Dave: Do you need to know something about allocator types? But only at construction time.
Pablo: You could have allocators that are different types.
Dave: Swap is two ended operation.
Pablo: Opinion is that both have to say propagate on swap for them to swap.
John: It is not arbitrary. If one person says no. No is no.
Howard: Find noexcept swap to be very useful. Would like to move in that direction and bring container design along.
Dave: If you have something were allocator must not propagate you can detect that at construction time.
...
Pablo: Need to leave this open and discuss in smaller group.
Alisdair: Tried to add boost::any as TR2 proposal and ran into this issue. Only the first place where we run into issues with type erased allocators. Suggest we move it to open.
Action Item: Move to open.
Action Item (Pablo works with Howard and Daniel): Address the more fundamental issue (which may be multiple issues) and write up findings.
[ Original resolution: ]
This wording is relative to the FDIS.
Modify the header <functional> synopsis in 20.14 [function.objects] as indicated:
namespace std { […] template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept; […] }
Modify the class template function synopsis in 20.14.12.2 [func.wrap.func] as indicated:
namespace std { […] // [func.wrap.func.alg], specialized algorithms: template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept; […] }
Modify 20.14.12.2.7 [func.wrap.func.alg] as indicated:
template<class R, class... ArgTypes> void swap(function<R(ArgTypes...)>& f1, function<R(ArgTypes...)>& f2) noexcept;-1- Effects: f1.swap(f2);
[2014-02-28 (Post Issaquah), Pablo provides more information]
For cross-referencing purposes: The resolution of this issue should be harmonized with any resolution to LWG 2370, which addresses inappropriate noexcepts in some function constructors.
We have the following choices:
swap() does not throw
Discussion: This definition is desirable, and allows assignment to be implemented with the strong exception guarantee, but it does have consequences: The implementation cannot use the small-object optimization for a function-object F unless F is NothrowMovable (nothrow-swappable is unimportant because F is not swapped with another F). Note that many functors written before C++11 will not have move constructors decorated with noexcept, so this limitation could affect a lot of code.
It is not clear what other implementation restrictions might be needed. Allocators are required not to throw on move or copy. Is that sufficient?
swap() can throw
Discussion: This definition gives maximum latitude to implementation to use small-object optimization. However, the strong guarantee on assignment is difficult to achieve. Should we consider giving up on the strong guarantee? How much are we willing to pessimize code for exceptions?
swap() will not throw if both functions have NoThrowMoveable functors
Discussion: This definition is similar to option 2, but gives slightly stronger guarantees. Here, swap() can throw, but the programmer can theoretically prevent that from happening. This should be straight-forward to implement and gives the implementation a lot of latitude for optimization. However, because this is a dynamic decision, the program is not as easy to reason about. Also, the strong guarantee for assignment is compromized as in option 2.
Proposed resolution:
Section: 20.11.2.2.6 [util.smartptr.shared.create] Status: Open Submitter: Jonathan Wakely Opened: 2011-07-11 Last modified: 2016-03-08
Priority: 2
View other active issues in [util.smartptr.shared.create].
View all other issues in [util.smartptr.shared.create].
View all issues with Open status.
Discussion:
20.11.2.2.6 [util.smartptr.shared.create] says:
-2- Effects: Allocates memory suitable for an object of type T and constructs an object in that memory via the placement new expression ::new (pv) T(std::forward<Args>(args)...). The template allocate_shared uses a copy of a to allocate memory. If an exception is thrown, the functions have no effect.
This explicitly requires placement new rather than using allocator_traits<A>::construct(a, (T*)pv, std::forward<Args>(args)...) In most cases that would result in the same placement new expression, but would allow more control over how the object is constructed e.g. using scoped_allocator_adaptor to do uses-allocator construction, or using an allocator declared as a friend to construct objects with no public constructors.
[2011-08-16 Bloomington:]
Agreed to fix in principle, but believe that make_shared and allocate_shared have now diverged enough that their descriptions should be separated. Pablo and Stefanus to provide revised wording.
Daniel's (old) proposed resolution:
This wording is relative to the FDIS.
Change the following paragraphs of 20.11.2.2.6 [util.smartptr.shared.create] as indicated (The suggested removal of the last sentence of p1 is not strictly required to resolve this issue, but is still recommended, because it does not say anything new but may give the impression that it says something new):
template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args); template<class T, class A, class... Args> shared_ptr<T> allocate_shared(const A& a, Args&&... args);-1- Requires: For the template make_shared, t
-2- Effects: Allocates memory suitable for an object of type T and constructs an object in that memory. The template make_shared constructs the object via the placement new expression ::new (pv) T(std::forward<Args>(args)...). The template allocate_shared uses a copy of a to allocate memory and constructs the object by calling allocator_traits<A>::construct(a, pt, std::forward<Args>(args)...). If an exception is thrown, the functions have no effect. -3- Returns: A shared_ptr instance that stores and owns the address of the newly constructed object of type T. -4- Postconditions: get() != 0 && use_count() == 1 -5- Throws: bad_alloc, or, for the template make_shared, an exception thrown from the constructor of T, or, for the template allocate_shared, an exception thrown from A::allocate or from allocator_traits<A>::constructThe expression ::new (pv) T(std::forward<Args>(args)...), where pv has type void* and points to storage suitable to hold an object of type T, shall be well formed. For the template allocate_shared, the expression allocator_traits<A>::construct(a, pt, std::forward<Args>(args)...), where pt has type T* and points to storage suitable to hold an object of type T, shall be well formed. A shall be an allocator ([allocator.requirements]).The copy constructor and destructor of A shall not throw exceptions.from the constructor of T. -6- Remarks: Implementations are encouraged, but not required, to perform no more than one memory allocation. [ Note: This provides efficiency equivalent to an intrusive smart pointer. — end note ] -7- [ Note: These functions will typically allocate more memory than sizeof(T) to allow for internal bookkeeping structures such as the reference counts. — end note ]
[2011-12-04: Jonathan and Daniel improve wording]
See also c++std-lib-31796
[2013-10-13, Ville]
This issue is related to 2089.
[2014-02-15 post-Issaquah session : move to Tentatively NAD]
STL: This takes an allocator, but then ignores its construct. That's squirrely.
Alisdair: The convention is when you take an allocator, you use its construct.
STL: 23.2.1 [container.requirements.general]/3, argh! This fills me with despair, but I understand it now.
STL: Ok, this is some cleanup.
STL: You're requiring b to be of type A and not being rebound, is that an overspecification?
Pablo: Good point. Hmm, that's only a requirement on what must be well-formed.
STL: If it's just a well-formed requirement, then why not just use a directly?
Pablo: Yeah, the well-formed requirement is overly complex. It's not a real call, we could just use a directly. It makes it harder to read.
Alisdair: b should be an allocator in the same family as a.
Pablo: This is a well-formed requirement, I wonder if it's the capital A that's the problem here. It doesn't matter here, this is way too much wording.
Alisdair: It's trying to tie the constructor arguments into the allocator requirements.
Pablo: b could be struck, that's a runtime quality. The construct will work with anything that's in the family of A.
Alisdair: The important part is the forward of Args.
Pablo: A must be an allocator, and forward Args must work with that.
Alisdair: First let's nail down A.
Pablo: Then replace b with a, and strike the rest.
STL: You need pt's type, at least.
Pablo: There's nothing to be said about runtime constraints here, this function doesn't even take a pt.
STL: Looking at the Effects, I believe b is similarly messed up, we can use a2 to construct an object.
Alisdair: Or any allocator in the family of a.
STL: We say this stuff for the deallocate too, it should be lifted up.
STL: "owns the address" is weird.
Alisdair: shared_ptr owns pointers, although it does sound funky.
Walter: "to destruct" is ungrammatical.
STL: "When ownership is given up" is not what we usually say.
Alisdair: I think the Returns clause is the right place to say this.
STL: The right place to say this is shared_ptr's dtor, we don't want to use Core's "come from" convention.
Alisdair: I'm on the hook to draft cleaner wording.
[2015-10, Kona Saturday afternoon]
AM: I was going to clean up the wording, but haven't done it yet.
Defer until we have new wording.
[2016-03, Jacksonville]
Alisdair: we need to figure out whether we should call construct or not; major implementation divergence
STL: this does not grant friendship, does it?
Jonathan: some people want it.
Thomas: scoped allocator adapter should be supported, so placement new doesn't work
Alisdair: this makes the make_ functions impossible
Thomas: you don't want to use those though.
Alisdair: but people use that today, at Bloomberg
Alisdair: and what do we do about fancy pointers?
Jonathan: we constrain it to only non-fancy pointers.
STL: shared_ptr has never attempted to support fancy pointers; seems like a paper is needed.
Poll: call construct:6 operator new: 0 don't care: 4
Poll: should we support fancy pointers? Yes: 1 No: 4 don't care: 4
STL: 20.8.2.2.6p2: 'and pv->~T()' is bogus for void
STL: 20.8.2.2.6p4: is this true even if we're going to allocate a bit more?
Alisdair: yes
Alisdair: coming up with new wording
Proposed resolution:
This wording is relative to the FDIS.
Change the following paragraphs of 20.11.2.2.6 [util.smartptr.shared.create] as indicated:
template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);template<class T, class A, class... Args> shared_ptr<T> allocate_shared(const A& a, Args&&... args);
-1- Requires: The expression ::new (pv) T(std::forward<Args>(args)...), where pv
has type void* and points to storage suitable to hold an object of type T, shall be well
formed. A shall be an allocator (17.6.3.5 [allocator.requirements]). The copy constructor
and destructor of A shall not throw exceptions.
return allocate_shared<T>(allocator<T>(), std::forward<Args>(args)...);
Allocates memory suitable for an object of type T
and constructs an object in that memory via the placement new expression
::new (pv) T(std::forward<Args>(args)...). The template allocate_shared uses a copy
of a to allocate memory. If an exception is thrown, the functions have no effect.
Add the following set of new paragraphs immediately following the previous paragraph 7 of 20.11.2.2.6 [util.smartptr.shared.create]:
template<class T, class A, class... Args> shared_ptr<T> allocate_shared(const A& a, Args&&... args);
-?- Requires: The expressions allocator_traits<A>::construct(b, pt, std::forward<Args>(args)...) and allocator_traits<A>::destroy(b, pt) shall be well-formed and well-defined, where b has type A and is a copy of a and where pt has type T* and points to storage suitable to hold an object of type T. A shall meet the allocator requirements (17.6.3.5 [allocator.requirements]).
-?- Effects: Uses an object a2 of type allocator_traits<A>::rebind_alloc<unspecified> that compares equal to a to allocate memory suitable for an object of type T. Uses a copy b of type A from a to construct an object of type T in that memory by calling allocator_traits<A>::construct(b, pt, std::forward<Args>(args)...). If an exception is thrown, the function has no effect. -?- Returns: A shared_ptr instance that stores and owns the address of the newly constructed object of type T. When ownership is given up, the effects are as follows: Uses a copy b2 of type A from a to destruct an object of type T by calling allocator_traits<A>::destroy(b2, pt2) where pt2 has type T* and refers to the newly constructed object. Then uses an object of type allocator_traits<A>::rebind_alloc<unspecified> that compares equal to a to deallocate the allocated memory. -?- Postconditions: get() != 0 && use_count() == 1 -?- Throws: Nothing unless memory allocation or allocator_traits<A>::construct throws an exception. -?- Remarks: Implementations are encouraged, but not required, to perform no more than one memory allocation. [Note: Such an implementation provides efficiency equivalent to an intrusive smart pointer. — end note] -?- [Note: This function will typically allocate more memory than sizeof(T) to allow for internal bookkeeping structures such as the reference counts. — end note]Section: 20.15.4.3 [meta.unary.prop] Status: Open Submitter: Daniel Krügler Opened: 2011-08-20 Last modified: 2016-02-12
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with Open status.
Discussion:
The currently agreed on proposed wording for 2015 using remove_all_extents<T>::type instead of the "an array of unknown bound" terminology in the precondition should be extended to some further entries especially in Table 49, notably the is_*constructible, is_*assignable, and is_*destructible entries. To prevent ODR violations, incomplete element types of arrays must be excluded for value-initialization and destruction for example. Construction and assignment has to be honored, when we have array-to-pointer conversions or pointer conversions of incomplete pointees in effect.
[2012, Kona]
The issue is that in three type traits, we are accidentally saying that in certain circumstances the type must give a specified answer when given an incomplete type. (Specifically: an array of unknown bound of incomplete type.) The issue asserts that there's an ODR violation, since the trait returns false in that case but might return a different version when the trait is completed.
Howard argues: no, there is no risk of an ODR violation. is_constructible<A[]> must return false regardless of whether A is complete, so there's no reason to forbid an array of unknown bound of incomplete types. Same argument applies to is_assignable. General agreement with Howard's reasoning.
There may be a real issue for is_destructible. None of us are sure what is_destructible is supposed to mean for an array of unknown bound (regardless of whether its type is complete), and the standard doesn't make it clear. The middle column doesn't say what it's supposed to do for incomplete types.
In at least one implementation, is_destructible<A[]> does return true if A is complete, which would result in ODR violation unless we forbid it for incomplete types.
Move to open. We believe there is no issue for is_constructible or is_assignable, but that there is a real issue for is_destructible.
Proposed resolution:
Section: 18.8.4 [exception.terminate] Status: Open Submitter: Daniel Krügler Opened: 2011-09-25 Last modified: 2016-02-12
Priority: 3
View all issues with Open status.
Discussion:
Andrzej Krzemienski reported the following on comp.std.c++:
In N3290, which is to become the official standard, in 18.8.4.4 [terminate], paragraph 1 reads
Remarks: Called by the implementation when exception handling must be abandoned for any of several reasons (15.5.1), in effect immediately after evaluating the throw-expression (18.8.3.1). May also be called directly by the program.
It is not clear what is "in effect". It was clear in previous drafts where paragraphs 1 and 2 read:
Called by the implementation when exception handling must be abandoned for any of several reasons (15.5.1). May also be called directly by the program.
Effects: Calls the terminate_handler function in effect immediately after evaluating the throw-expression (18.8.3.1), if called by the implementation, or calls the current terminate_handler function, if called by the program.It was changed by N3189. The same applies to function unexpected (D. 11.4, paragraph 1).
Assuming the previous wording is still intended, the wording can be read "unless std::terminate is called by the program, we will use the handler that was in effect immediately after evaluating the throw-expression". This assumes that there is some throw-expression connected to every situation that triggers the call to std::terminate. But this is not the case:
- In case std::thread is assigned to or destroyed while being joinable there is no throw-expression involved.
- In case std::unexpected is called by the program, std::terminate is triggered by the implementation - no throw-expression involved.
- In case a destructor throws during stack unwinding we have two throw-expressions involved.
Which one is referred to?
In case std::nested_exception::rethrow_nested is called for an object that has captured no exception, there is no throw-expression involved directly (and may no throw be involved even indirectly). Next, 18.8.4.1 [terminate.handler], paragraph 2 saysRequired behavior: A terminate_handler shall terminate execution of the program without returning to the caller.
This seems to allow that the function may exit by throwing an exception (because word "return" implies a normal return).
One could argue that words "terminate execution of the program" are sufficient, but then why "without returning to the caller" would be mentioned. In case such handler throws, noexcept specification in function std::terminate is violated, and std::terminate would be called recursively - should std::abort not be called in case of recursive std::terminate call? On the other hand some controlled recursion could be useful, like in the following technique.
The here mentioned wording changes by N3189 in regard to 18.8.4.4 [terminate] p1 were done for a better separation of effects (Effects element) and additional normative wording explanations (Remarks element), there was no meaning change intended. Further, there was already a defect existing in the previous wording, which was not updated when further situations where defined, when std::terminate where supposed to be called by the implementation.
The part "in effect immediately after evaluating the throw-expression" should be removed and the quoted reference to 18.8.4.1 [terminate.handler] need to be part of the effects element where it refers to the current terminate_handler function, so should be moved just after "Effects: Calls the current terminate_handler function." It seems ok to allow a termination handler to exit via an exception, but the suggested idiom should better be replaced by a more simpler one based on evaluating the current exception pointer in the terminate handler, e.g.void our_terminate (void) { std::exception_ptr p = std::current_exception(); if (p) { ... // OK to rethrow and to determine it's nature } else { ... // Do something else } }
[2011-12-09: Daniel comments]
A related issue is 2111.
[2012, Kona]
Move to Open.
There is an interaction with Core issues in this area that Jens is already supplying wording for. Review this issue again once Jens wording is available.
Alisdair to review clause 15.5 (per Jens suggestion) and recommend any changes, then integrate Jens wording into this issue.
Proposed resolution:
Section: 20.10.9.1 [allocator.members] Status: EWG Submitter: David Krauss Opened: 2011-10-07 Last modified: 2016-02-12
Priority: 2
View all other issues in [allocator.members].
View all issues with EWG status.
Discussion:
When the EmplaceConstructible (23.2.1 [container.requirements.general]/13) requirement is used to initialize an object, direct-initialization occurs. Initializing an aggregate or using a std::initializer_list constructor with emplace requires naming the initialized type and moving a temporary. This is a result of std::allocator::construct using direct-initialization, not list-initialization (sometimes called "uniform initialization") syntax.
Altering std::allocator<T>::construct to use list-initialization would, among other things, give preference to std::initializer_list constructor overloads, breaking valid code in an unintuitive and unfixable way — there would be no way for emplace_back to access a constructor preempted by std::initializer_list without essentially reimplementing push_back.std::vector<std::vector<int>> v; v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization
The proposed compromise is to use SFINAE with std::is_constructible, which tests whether direct-initialization is well formed. If is_constructible is false, then an alternative std::allocator::construct overload is chosen which uses list-initialization. Since list-initialization always falls back on direct-initialization, the user will see diagnostic messages as if list-initialization (uniform-initialization) were always being used, because the direct-initialization overload cannot fail.
I can see two corner cases that expose gaps in this scheme. One occurs when arguments intended for std::initializer_list satisfy a constructor, such as trying to emplace-insert a value of {3, 4} in the above example. The workaround is to explicitly specify the std::initializer_list type, as in v.emplace_back(std::initializer_list<int>(3, 4)). Since this matches the semantics as if std::initializer_list were deduced, there seems to be no real problem here. The other case is when arguments intended for aggregate initialization satisfy a constructor. Since aggregates cannot have user-defined constructors, this requires that the first nonstatic data member of the aggregate be implicitly convertible from the aggregate type, and that the initializer list have one element. The workaround is to supply an initializer for the second member. It remains impossible to in-place construct an aggregate with only one nonstatic data member by conversion from a type convertible to the aggregate's own type. This seems like an acceptably small hole. The change is quite small because EmplaceConstructible is defined in terms of whatever allocator is specified, and there is no need to explicitly mention SFINAE in the normative text.[2012, Kona]
Move to Open.
There appears to be a real concern with initializing aggregates, that can be performed only using brace-initialization. There is little interest in the rest of the issue, given the existence of 'emplace' methods in C++11.
Move to Open, to find an acceptable solution for intializing aggregates. There is the potential that EWG may have an interest in this area of language consistency as well.
[2013-10-13, Ville]
This issue is related to 2070.
[2015-02 Cologne]
Move to EWG, Ville to write a paper.
[2015-09, Telecom]
Ville: N4462 reviewed in Lenexa. EWG discussion to continue in Kona.
Proposed resolution:
This wording is relative to the FDIS.
Change 20.10.9.1 [allocator.members] p12 as indicated:
template <class U, class... Args> void construct(U* p, Args&&... args);12 Effects: ::new((void *)p) U(std::forward<Args>(args)...) if is_constructible<U, Args...>::value is true, else ::new((void *)p) U{std::forward<Args>(args)...}
Section: 30.6.5 [futures.promise], 30.6.9 [futures.task] Status: Open Submitter: Jonathan Wakely Opened: 2011-11-01 Last modified: 2016-02-12
Priority: 4
View other active issues in [futures.promise].
View all other issues in [futures.promise].
View all issues with Open status.
Discussion:
This example is ill-formed according to C++11 because uses_allocator<promise<R>, A>::value is true, but is_constructible<promise<R>, A, promise<R>&&>::value is false. Similarly for packaged_task.
#include <future> #include <memory> #include <tuple> using namespace std; typedef packaged_task<void()> task; typedef promise<void> prom; allocator<task> a; tuple<task, prom> t1{ allocator_arg, a }; tuple<task, prom> t2{ allocator_arg, a, task{}, prom{} };
[2012, Portland]
This is an allocator issue, and should be dealt with directly by LWG.
[2013-03-06]
Jonathan suggests to make the new constructors non-explicit and makes some representational improvements.
[2013-09 Chicago]
Move to deferred.
This issue has much in common with similar problems with std::function that are being addressed by the polymorphic allocators proposal currently under evaluation in LEWG. Defer further discussion on this topic until the final outcome of that paper and its proposed resolution is known.
[2014-02-20 Re-open Deferred issues as Priority 4]
Proposed resolution:
[This wording is relative to the FDIS.]
Add to 30.6.5 [futures.promise], class template promise synopsis, as indicated:
namespace std { template <class R> class promise { public: promise(); template <class Allocator> promise(allocator_arg_t, const Allocator& a); template <class Allocator> promise(allocator_arg_t, const Allocator& a, promise&& rhs) noexcept; promise(promise&& rhs) noexcept; promise(const promise& rhs) = delete; ~promise(); […] }; […] }
Change 30.6.5 [futures.promise] as indicated:
promise(promise&& rhs) noexcept; template <class Allocator> promise(allocator_arg_t, const Allocator& a, promise&& rhs) noexcept;-5- Effects: constructs a new promise object and transfers ownership of the shared state of rhs (if any) to the newly-constructed object.
-6- Postcondition: rhs has no shared state. -?- [Note: a is not used — end note]
Add to 30.6.9 [futures.task], class template packaged_task synopsis, as indicated:
namespace std { template<class> class packaged_task; // undefined template<class R, class... ArgTypes> class packaged_task<R(ArgTypes...)> { public: // construction and destruction packaged_task() noexcept; template <class Allocator> packaged_task(allocator_arg_t, const Allocator& a) noexcept; template <class F> explicit packaged_task(F&& f); template <class F, class Allocator> explicit packaged_task(allocator_arg_t, const Allocator& a, F&& f); ~packaged_task(); // no copy packaged_task(const packaged_task&) = delete; template<class Allocator> packaged_task(allocator_arg_t, const Allocator& a, const packaged_task&) = delete; packaged_task& operator=(const packaged_task&) = delete; // move support packaged_task(packaged_task&& rhs) noexcept; template <class Allocator> packaged_task(allocator_arg_t, const Allocator& a, packaged_task&& rhs) noexcept; packaged_task& operator=(packaged_task&& rhs) noexcept; void swap(packaged_task& other) noexcept; […] }; […] }
Change 30.6.9.1 [futures.task.members] as indicated:
packaged_task() noexcept; template <class Allocator> packaged_task(allocator_arg_t, const Allocator& a) noexcept;-1- Effects: constructs a packaged_task object with no shared state and no stored task.
-?- [Note: a is not used — end note]
[…]
packaged_task(packaged_task&& rhs) noexcept; template <class Allocator> packaged_task(allocator_arg_t, const Allocator& a, packaged_task&& rhs) noexcept;-5- Effects: constructs a new packaged_task object and transfers ownership of rhs's shared state to *this, leaving rhs with no shared state. Moves the stored task from rhs to *this.
-6- Postcondition: rhs has no shared state. -?- [Note: a is not used — end note]
Section: 17.6.3.3 [nullablepointer.requirements], 24.2.3 [input.iterators], 24.2.7 [random.access.iterators], 25.1 [algorithms.general], 25.5 [alg.sorting], 30.2.1 [thread.req.paramname] Status: Open Submitter: Daniel Krügler Opened: 2011-12-09 Last modified: 2016-02-29
Priority: 3
View all issues with Open status.
Discussion:
As of 17.6.3.1 [utility.arg.requirements] Table 17/18, the return types of the expressions
a == b
or
a < b
for types satisfying the EqualityComparable or LessThanComparable types, respectively, are required to be "convertible to bool" which corresponds to a copy-initialization context. But several newer parts of the library that refer to such contexts have lowered the requirements taking advantage of the new terminology of "contextually convertible to bool" instead, which corresponds to a direct-initialization context (In addition to "normal" direct-initialization constructions, operands of logical operations as well as if or switch conditions also belong to this special context).
One example for these new requirements are input iterators which satisfy EqualityComparable but also specify that the expressiona != b
shall be just "contextually convertible to bool". The same discrepancy exists for requirement set NullablePointer in regard to several equality-related expressions.
For random access iterators we havea < b contextually convertible to bool
as well as for all derived comparison functions, so strictly speaking we could have a random access iterator that does not satisfy the LessThanComparable requirements, which looks like an artifact to me.
Even if we keep with the existing requirements based on LessThanComparable or EqualityComparable we still would have the problem that some current specifications are actually based on the assumption of implicit convertibility instead of "explicit convertibility", e.g. 20.11.1.5 [unique.ptr.special] p3:template <class T1, class D1, class T2, class D2> bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);-3- Returns: x.get() != y.get().
Similar examples exist in 20.11.1.2.2 [unique.ptr.single.dtor] p2, 20.11.1.2.3 [unique.ptr.single.asgn] p9, 20.11.1.2.4 [unique.ptr.single.observers] p1+3+8, etc.
In all these places the expressions involving comparison functions (but not those of the conversion of a NullablePointer to bool!) assume to be "convertible to bool". I think this is a very natural assumption and all delegations of the comparison functions of some type X to some other API type Y in third-party code does so assuming that copy-initialization semantics will just work. The actual reason for using the newer terminology can be rooted back to LWG 556. My hypotheses is that the resolution of that issue also needs a slight correction. Why so? The reason for opening that issue were worries based on the previous "convertible to bool" wording. An expressions like "!pred(a, b)" might not be well-formed in those situations, because operator! might not be accessible or might have an unusual semantics (and similarly for other logical operations). This can indeed happen with unusual proxy return types, so the idea was that the evaluation of Predicate, BinaryPredicate (25.1 [algorithms.general] p8+9), and Compare (25.5 [alg.sorting] p2) should be defined based on contextual conversion to bool. Unfortunately this alone is not sufficient: In addition, I think, we also want the predicates to be (implicitly) convertible to bool! Without this wording, several conditions are plain wrong, e.g. 25.3.5 [alg.find] p2, which talks about "pred(*i) != false" (find_if) and "pred(*i) == false" (find_if_not). These expressions are not within a boolean context! While we could simply fix all these places by proper wording to be considered in a "contextual conversion to bool", I think that this is not the correct solution: Many third-party libraries already refer to the previous C++03 Predicate definition — it actually predates C++98 and is as old as the SGI specification. It seems to be a high price to pay to switch to direct initialization here instead of fixing a completely different specification problem. A final observation is that we have another definition for a Predicate in 30.2.1 [thread.req.paramname] p2:If a parameter is Predicate, operator() applied to the actual template argument shall return a value that is convertible to bool.
The problem here is not that we have two different definitions of Predicate in the standard — this is confusing, but this fact alone is not a defect. The first (minor) problem is that this definition does not properly apply to function objects that are function pointers, because operator() is not defined in a strict sense. But the actually worse second problem is that this wording has the very same problem that has originally lead to LWG 556! We only need to look at 30.5.1 [thread.condition.condvar] p15 to recognice this:
while (!pred()) wait(lock);
The negation expression here looks very familiar to the example provided in LWG 556 and is sensitive to the same "unusual proxy" problem. Changing the 30.2.1 [thread.req.paramname] wording to a corresponding "contextual conversion to bool" wouldn't work either, because existing specifications rely on "convertible to bool", e.g. 30.5.1 [thread.condition.condvar] p32+33+42 or 30.5.2 [thread.condition.condvarany] p25+26+32+33.
To summarize: I believe that LWG 556 was not completely resolved. A pessimistic interpretation is, that even with the current wording based on "contextually convertible to bool" the actual problem of that issue has not been fixed. What actually needs to be required here is some normative wording that basically expresses something along the lines of:The semantics of any contextual conversion to bool shall be equivalent to the semantics of any implicit conversion to bool.
This is still not complete without having concepts, but it seems to be a better approximation. Another way of solving this issue would be to define a minimum requirements table with equivalent semantics. The proposed wording is a bit simpler but attempts to express the same thing.
[2012, Kona]
Agree with Daniel that we potentially broke some C++03 user code, accept the changes striking "contextually" from tables. Stefan to provide revised wording for section 25, and figure out changes to section 30.
Move to open, and then to Review when updated wording from Stefan is available.
[2012-10-12, STL comments]
The current proposed resolution still isn't completely satisfying. It would certainly be possible for the Standard to require these various expressions to be implicitly and contextually convertible to bool, but that would have a subtle consequence (which, I will argue, is undesirable - regardless of the fact that it dates all the way back to C++98/03). It would allow users to provide really wacky types to the Standard Library, with one of two effects:
Standard Library implementations would have to go to great lengths to respect such wacky types, essentially using static_cast<bool> when invoking any predicates or comparators.
Otherwise, such wacky types would be de facto nonportable, because they would make Standard Library implementations explode.
Effect B is the status quo we're living with today. What Standard Library implementations want to do with pred(args) goes beyond "if (pred(args))" (C++03), contextually converting pred(args) to bool (C++11), or implicitly and contextually converting pred(args) to bool (the current proposed resolution). Implementations want to say things like:
if (pred(args)) if (!pred(args)) if (cond && pred(args)) if (cond && !pred(args))
These are real examples taken from Dinkumware's implementation. There are others that would be realistic ("pred(args) && cond", "cond || pred(args)", etc.)
Although negation was mentioned in this issue's Discussion section, and in LWG 556's, the current proposed resolution doesn't fix this problem. Requiring pred(args) to be implicitly and contextually convertible to bool doesn't prevent operator!() from being overloaded and returning std::string (as a wacky example). More ominously, it doesn't prevent operator&&() and operator||() from being overloaded and destroying short-circuiting.I would like LWG input before working on Standardese for a new proposed resolution. Here's an outline of what I'd like to do:
Introduce a new "concept" in 17.6.3 [utility.requirements], which I would call BooleanTestable in the absence of better ideas.
Centralize things and reduce verbosity by having everything simply refer to BooleanTestable when necessary. I believe that the tables could say "Return type: BooleanTestable", while Predicate/BinaryPredicate/Compare would need the incantation "shall satisfy the requirements of BooleanTestable".
Resolve the tug-of-war between users (who occasionally want to do weird things) and implementers (who don't want to have to contort their code) by requiring that:
Given a BooleanTestable x, x is both implicitly and contextually convertible to bool.
Given a BooleanTestable x, !x is BooleanTestable. (This is intentionally "recursive".)
Given a BooleanTestable x, bool t = x, t2(x), f = !x; has the postcondition t == t2 && t != f.
Given a BooleanTestable x and a BooleanTestable y of possibly different types, "x && y" and "x || y" invoke the built-in operator&&() and operator||(), triggering short-circuiting.
bool is BooleanTestable.
I believe that this simultaneously gives users great latitude to use types other than bool, while allowing implementers to write reasonable code in order to get their jobs done. (If I'm forgetting anything that implementers would want to say, please let me know.)
About requirement (I): As Daniel patiently explained to me, we need to talk about both implicit conversions and contextual conversions, because it's possible for a devious type to have both "explicit operator bool()" and "operator int()", which might behave differently (or be deleted, etc.).
About requirement (IV): This is kind of tricky. What we'd like to say is, "BooleanTestable can't ever trigger an overloaded logical operator". However, given a perfectly reasonable type Nice - perhaps even bool itself! - other code (perhaps a third-party library) could overload operator&&(Nice, Evil). Therefore, I believe that the requirement should be "no first use" - the Standard Library will ask for various BooleanTestable types from users (for example, the result of "first != last" and the result of "pred(args)"), and as long as they don't trigger overloaded logical operators with each other, everything is awesome.
About requirement (V): This is possibly redundant, but it's trivial to specify, makes it easier for users to understand what they need to do ("oh, I can always achieve this with bool"), and provides a "base case" for requirement (IV) that may or may not be necessary. Since bool is BooleanTestable, overloading operator&&(bool, Other) (etc.) clearly makes the Other type non-BooleanTestable.
This wording is relative to the FDIS.
Change Table 25 — "NullablePointer requirements" in 17.6.3.3 [nullablepointer.requirements] as indicated:
Table 25 — NullablePointer requirements Expression Return type Operational semantics […] a != b contextuallyconvertible to bool!(a == b) a == np
np == acontextuallyconvertible to boola == P() a != np
np != acontextuallyconvertible to bool!(a == np) Change Table 107 — "Input iterator requirements" in 24.2.3 [input.iterators] as indicated:
Table 107 — Input iterator requirements (in addition to Iterator) Expression Return type Operational semantics Assertion/note
pre-/post-conditiona != b contextuallyconvertible to bool!(a == b) pre: (a, b) is in the domain of ==. […] Change Table 111 — "Random access iterator requirements" in 24.2.7 [random.access.iterators] as indicated:
Table 111 — Random access iterator requirements (in addition to bidirectional iterator) Expression Return type Operational semantics Assertion/note
pre-/post-condition[…] a < b contextuallyconvertible to boolb - a > 0 < is a total ordering relation a > b contextuallyconvertible to boolb < a > is a total ordering relation opposite to <. a >= b contextuallyconvertible to bool!(a < b) a <= b contextuallyconvertible to bool!(a > b) Change 25.1 [algorithms.general] p8+9 as indicated:
-8- The Predicate parameter is used whenever an algorithm expects a function object (20.14 [function.objects]) that, when applied to the result of dereferencing the corresponding iterator, returns a value testable as true. In other words, if an algorithm takes Predicate pred as its argument and first as its iterator argument, it should work correctly in the construct pred(*first) implicitly or contextually converted to bool (Clause 4 [conv]). The function object pred shall not apply any non-constant function through the dereferenced iterator.
-9- The BinaryPredicate parameter is used whenever an algorithm expects a function object that when applied to the result of dereferencing two corresponding iterators or to dereferencing an iterator and type T when T is part of the signature returns a value testable as true. In other words, if an algorithm takes BinaryPredicate binary_pred as its argument and first1 and first2 as its iterator arguments, it should work correctly in the construct binary_pred(*first1, *first2) implicitly or contextually converted to bool (Clause 4 [conv]). BinaryPredicate always takes the first iterator's value_type as its first argument, that is, in those cases when T value is part of the signature, it should work correctly in the construct binary_pred(*first1, value) implicitly or contextually converted to bool (Clause 4 [conv]). binary_pred shall not apply any non-constant function through the dereferenced iterators.Change 25.5 [alg.sorting] p2 as indicated:
-2- Compare is a function object type (20.14 [function.objects]). The return value of the function call operation applied to an object of type Compare, when implicitly or contextually converted to bool (4 [conv]), yields true if the first argument of the call is less than the second, and false otherwise. Compare comp is used throughout for algorithms assuming an ordering relation. It is assumed that comp will not apply any non-constant function through the dereferenced iterator.
Change 30.2.1 [thread.req.paramname] p2 as indicated:
-2-
If a parameter is Predicate, operator() applied to the actual template argument shall return a value that is convertible to boolPredicate is a function object type (20.14 [function.objects]). The return value of the function call operation applied to an object of type Predicate, when implicitly or contextually converted to bool (4 [conv]), yields true if the corresponding test condition is satisfied, and false otherwise.
[2014-05-20, Daniel suggests concrete wording based on STL's proposal]
The presented wording follows relatively closely STL's outline with the following notable exceptions:
A reference to BooleanTestable in table "Return Type" specifications seemed very unusual to me and I found no "prior art" for this in the Standard. Instead I decided to follow the usual style to add a symbol with a specific meaning to a specific paragraph that specifies symbols and their meanings.
STL's requirement IV suggested to directly refer to built-in operators && and ||. In my opinion this concrete requirement isn't needed if we simply require that two BooleanTestable operands behave equivalently to two those operands after conversion to bool (each of them).
I couldn't find a good reason to require normatively that type bool meets the requirements of BooleanTestable: My assertion is that after having defined them, the result simply falls out of this. But to make this a bit clearer, I added also a non-normative note to these effects.
[2014-06-10, STL comments]
In the current wording I would like to see changed the suggested changes described by bullet #6:
In 23.2.1 [container.requirements.general] p4 undo the suggested change
Then change the 7 occurrences of "convertible to bool" in the denoted tables to "bool".
[2015-05-05 Lenexa]
STL: Alisdair wanted to do something here, but Daniel gave us updated wording.
[2015-07 Telecom]
Alisdair: Should specify we don't break short circuiting.
Ville: Looks already specified because that's the way it works for bool.
Geoffrey: Maybe add a note about the short circuiting.
B2/P2 is somewhat ambiguous. It implies that B has to be both implicitly convertible to bool and contextually convertible to bool.
We like this, just have nits.
Status stays Open.
Marshall to ping Daniel with feedback.
[2016-02-27, Daniel updates wording]
The revised wording has been updated from N3936 to N4567.
To satisfy the Kona 2015 committee comments, the wording in [booleantestable.requirements] has been improved to better separate the two different requirements of "can be contextually converted to bool" and "can be implicitly converted to bool. Both are necessary because it is possible to define a type that has the latter property but not the former, such as the following one:
using Bool = int; struct OddBoolean { explicit OddBoolean(bool) = delete; OddBoolean(Bool){} }; OddBoolean b2 = true; // OK OddBoolean b1(true); // Error
In [booleantestable.requirements] a note has been added to ensure that an implementation is not allowed to break any short-circuiting semantics.
I decided to separate LWG 2587/2588 from this issue. Both these issues aren't exactly the same but depending on the committee's position, their resolution might benefit from the new vocabulary introduced here.
Proposed resolution:
This wording is relative to N4567.
Change 17.6.3.1 [utility.arg.requirements] p1, Table 17 — "EqualityComparable requirements", and Table 18 — "LessThanComparable requirements" as indicated:
-1- […] In these tables, T is an object or reference type to be supplied by a C++ program instantiating a template; a, b, and c are values of type (possibly const) T; s and t are modifiable lvalues of type T; u denotes an identifier; rv is an rvalue of type T;
[…]andv is an lvalue of type (possibly const) T or an rvalue of type const T; and BT denotes a type that meets the BooleanTestable requirements ([booleantestable.requirements]).
Table 17 — EqualityComparable requirements [equalitycomparable] Expression Return type Requirement a == b convertible toBT
bool== is an equivalence relation, that is, it has the following properties: […] […]
Table 18 — LessThanComparable requirements [lessthancomparable] Expression Return type Requirement a < b convertible toBT
bool< is a strict weak ordering relation (25.5 [alg.sorting])
Between 17.6.3.2 [swappable.requirements] and 17.6.3.3 [nullablepointer.requirements] insert a new sub-clause as indicated:
?.?.?.? BooleanTestable requirements [booleantestable.requirements]-?- A BooleanTestable type is a boolean-like type that also supports conversions to bool. A type B meets the BooleanTestable requirements if the expressions described in Table ?? are valid and have the indicated semantics, and if B also satisfies all the other requirements of this sub-clause [booleantestable.requirements].
An object b of type B can be implicitly converted to bool and in addition can be contextually converted to bool (Clause 4). The result values of both kinds of conversions shall be equivalent. [Example: The types bool, std::true_type, and std::bitset<>::reference are BooleanTestable types. — end example] For the purpose of Table ??, let B2 and Bn denote types (possibly both equal to B or to each other) that meet the BooleanTestable requirements, let b1 denote a (possibly const) value of B, let b2 denote a (possibly const) value of B2, and let t1 denote a value of type bool. [Note: These rules ensure what an implementation can rely on but doesn't grant it license to break short-circuiting behavior of a BooleanTestable type. — end note]
Somewhere within the new sub-clause [booleantestable.requirements] insert the following new Table (?? denotes the assigned table number):
Table ?? — BooleanTestable requirements [booleantestable] Expression Return type Operational semantics bool(b1) bool Remarks: bool(b1) == t1 for every value
b1 implicitly converted to t1.!b1 Bn Remarks: bool(b1) == !bool(!b1) for
every value b1.b1 && b2 bool bool(b1) && bool(b2) b1 || b2 bool bool(b1) || bool(b2)
Change 17.6.3.3 [nullablepointer.requirements] p5 and Table 25 — "NullablePointer requirements" as indicated:
[…]
-5- In Table 25, u denotes an identifier, t denotes a non-const lvalue of type P, a and b denote values of type (possibly const) P,andnp denotes a value of type (possibly const) std::nullptr_t, and BT denotes a type that meets the BooleanTestable requirements ([booleantestable.requirements]). […]
Table 25 — NullablePointer requirements [nullablepointer] Expression Return type Operational semantics … a != b contextually convertible to boolBT[…] a == np
np == acontextually convertible to boolBT[…] a != np
np != acontextually convertible to boolBT[…]
Change 20.5.2.8 [tuple.rel] as indicated;
template<class... TTypes, class... UTypes> constexpr bool operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u);-1- Requires: For all i, where 0 <= i and i < sizeof...(TTypes), get<i>(t) == get<i>(u) is a valid expression returning a type that
[…]is convertible to boolmeets the BooleanTestable requirements ([booleantestable.requirements]). sizeof...(TTypes) == sizeof...(UTypes).template<class... TTypes, class... UTypes> constexpr bool operator<(const tuple<TTypes...>& t, const tuple<UTypes...>& u);-4- Requires: For all i, where 0 <= i and i < sizeof...(TTypes), get<i>(t) < get<i>(u) and get<i>(u) < get<i>(t) are valid expressions returning types that
[…]are convertible to boolmeet the BooleanTestable requirements ([booleantestable.requirements]). sizeof...(TTypes) == sizeof...(UTypes).
Change 23.2.1 [container.requirements.general], Table 95 — "Container requirements", and Table 97 — "Optional container operations" as indicated:
-4- In Tables 95, 96, and 97 X denotes a container class containing objects of type T, a and b denote values of type X, u denotes an identifier, r denotes a non-const value of type X,
andrv denotes a non-const rvalue of type X, and BT denotes a type that meets the BooleanTestable requirements ([booleantestable.requirements]).
Table 95 — Container requirements Expression Return type […] … a == b convertible toBT
bool[…] a != b convertible toBT
bool[…] … a.empty() convertible toBT
bool[…] […]
Table 97 — Optional container requirements Expression Return type […] … a < b convertible toBT
bool[…] a > b convertible toBT
bool[…] a <= b convertible toBT
bool[…] a >= b convertible toBT
bool[…]
Change 24.2.1 [iterator.requirements.general], Table 106 — "Input iterator requirements", and Table 110 — "Random access iterator requirements" as indicated:
-12- In the following sections, a and b denote values of type X or const X, difference_type and reference refer to the types iterator_traits<X>::difference_type and iterator_traits<X>::reference, respectively, n denotes a value of difference_type, u, tmp, and m denote identifiers, r denotes a value of X&, t denotes a value of value type T, o denotes a value of some type that is writable to the output iterator, and BT denotes a type that meets the BooleanTestable requirements ([booleantestable.requirements]).
Table 106 — Input iterator requirements Expression Return type […] a != b contextually convertible toBT
bool[…] […]
Table 110 — Random access iterator requirements Expression Return type […] … a < b contextually convertible toBT
bool[…] a > b contextually convertible toBT
bool[…] a >= b contextually convertible toBT
bool[…] a <= b contextually convertible toBT
bool[…]
Change 25.1 [algorithms.general] p8+p9 as indicated:
[Drafting note: The wording changes below also fix (a) unusual wording forms used ("should work") which are unclear in which sense they are imposing normative requirements and (b) the problem, that the current wording seems to allow that the predicate may mutate a call argument, if that is not a dereferenced iterator. Upon applying the new wording it became obvious that the both the previous and the new wording has the effect that currently algorithms such as adjacent_find, search_n, unique, and unique_copy are not correctly described (because they have no iterator argument named first1), which could give raise to a new library issue. — end drafting note]
-8- The Predicate parameter is used whenever an algorithm expects a function object (20.9) that, when applied to the result of dereferencing the corresponding iterator, returns a value testable as true.
-9- The BinaryPredicate parameter is used whenever an algorithm expects a function object that when applied to the result of dereferencing two corresponding iterators or to dereferencing an iterator and type T when T is part of the signature returns a value testable as true.In other words, iIf an algorithm takes Predicate pred as its argument and first as its iterator argument,it should work correctly in the construct pred(*first) contextually converted to bool (Clause 4)the expression pred(*first) shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). The function object pred shall not apply any non-constant function throughthe dereferenced iteratorits argument.In other words, iIf an algorithm takes BinaryPredicate binary_pred as its argument and first1 and first2 as its iterator arguments,it should work correctly in the construct binary_pred(*first1, *first2) contextually converted to bool (Clause 4)the expression binary_pred(*first1, *first2) shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). BinaryPredicate always takes the first iterator's value_type as its first argument, that is, in those cases when T value is part of the signature,it should work correctly in the construct binary_pred(*first1, value) contextually converted to bool (Clause 4)the expression binary_pred(*first1, value) shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). binary_pred shall not apply any non-constant function throughthe dereferenced iteratorsany of its arguments.
Change 25.5 [alg.sorting] p2 as indicated:
[…]
-2- Compare is a function object type (20.9).The return value of the function call operation applied to an object of type Compare, when contextually converted to bool(Clause 4), yields true if the first argument of the call is less than the second, and false otherwise.Compare comp is used throughout for algorithms assuming an ordering relation. Let a and b denote two argument values whose types depend on the corresponding algorithm. Then the expression comp(a, b) shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). The return value of comp(a, b), converted to bool, yields true if the first argument a is less than the second argument b, and false otherwise. It is assumed that comp will not apply any non-constant function throughthe dereferenced iteratorany of its arguments. […]
Change 27.5.4.2 [fpos.operations] and Table 126 — "Position type requirements" as indicated:
-1- Operations specified in Table 126 are permitted. In that table,
P refers to an instance of fpos,
[…]
o refers to a value of type streamoff,
BT refers to a type that meets the BooleanTestable requirements ([booleantestable.requirements]),
[…]
Table 126 — Position type requirements Expression Return type […] … p == q convertible to boolBT[…] p != q convertible to boolBT[…]
Change 30.2.1 [thread.req.paramname] p1 as indicated:
-1- Throughout this Clause, the names of template parameters are used to express type requirements.
If a template parameter is named Predicate, operator() applied to the template argument shall return a value that is convertible to boolPredicate is a function object type (20.14 [function.objects]). Let pred denote an lvalue of type Predicate. Then the expression pred() shall have a type that meets the BooleanTestable requirements ( [booleantestable.requirements]). The return value of pred(), converted to bool, yields true if the corresponding test condition is satisfied, and false otherwise.
Section: 26.7.8 [template.mask.array] Status: Open Submitter: Thomas Plum Opened: 2011-12-10 Last modified: 2016-02-12
Priority: 4
View all issues with Open status.
Discussion:
Recently I received a Service Request (SR) alleging that one of our testcases causes an undefined behavior. The complaint is that 26.7.8 [template.mask.array] in C++11 (and the corresponding subclause in C++03) are interpreted by some people to require that in an assignment "a[mask] = b", the subscript mask and the rhs b must have the same number of elements.
IMHO, if that is the intended requirement, it should be stated explicitly. In any event, there is a tiny editorial cleanup that could be made: In C++11, 26.7.8.1 [template.mask.array.overview] para 2 mentions"the expression a[mask] = b;"
but the semicolon cannot be part of an expression. The correction could omit the semicolon, or change the word "expression" to "assignment" or "statement".
Here is the text of the SR, slightly modified for publication:Subject: SR01174 LVS _26322Y31 has undefined behavior [open]
[Client:]
The test case t263.dir/_26322Y31.cpp seems to be illegal as it has an undefined behaviour. I searched into the SRs but found SRs were not related to the topic explained in this mail (SR00324, SR00595, SR00838).const char vl[] = {"abcdefghijklmnopqrstuvwxyz"}; const char vu[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZ"}; const std::valarray<char> v0(vl, 27), vm5(vu, 5), vm6(vu, 6); std::valarray<char> x = v0; […] const bool vb[] = {false, false, true, true, false, true}; const std::valarray<bool> vmask(vb, 6); x = v0; x[vmask] = vm5; // ***** HERE.... steq(&x[0], "abABeCghijklmnopqrstuvwxyz"); x2 = x[vmask]; // ***** ....AND HERE […]This problem has already been discussed between [experts]: See thread http://gcc.gnu.org/ml/libstdc++/2009-11/threads.html#00051 Conclusion http://gcc.gnu.org/ml/libstdc++/2009-11/msg00099.html
[Plum Hall:]
Before I log this as an SR, I need to check one detail with you. I did read the email thread you mentioned, and I did find a citation (see INCITS ISO/IEC 14882-2003 Section 26.3.2.6 on valarray computed assignments): Quote: "If the array and the argument array do not have the same length, the behavior is undefined", But this applies to computed assignment (*=, +=, etc), not to simple assignment. Here is the C++03 citation re simple assignment: 26.3.2.2 valarray assignment [lib.valarray.assign]valarray<T>& operator=(const valarray<T>&);1 Each element of the *this array is assigned the value of the corresponding element of the argument array. The resulting behavior is undefined if the length of the argument array is not equal to the length of the *this array.
In the new C++11 (N3291), we find ...
26.6.2.3 valarray assignment [valarray.assign]valarray<T>& operator=(const valarray<T>& v);1 Each element of the *this array is assigned the value of the corresponding element of the argument array. If the length of v is not equal to the length of *this, resizes *this to make the two arrays the same length, as if by calling resize(v.size()), before performing the assignment.
So it looks like the testcase might be valid for C++11 but not for C++03; what do you think?
[Client:]
I quite agree with you but the two problems I mentioned:x[vmask] = vm5; // ***** HERE.... […] x2 = x[vmask]; // ***** ....AND HERErefer to mask_array assignment hence target the C++03 26.3.8 paragraph. Correct?
[Plum Hall:]
I mentioned the contrast between C++03 26.3.2.2 para 1 versus C++11 26.6.2.3 para 1. But in C++03 26.3.8, I don't find any corresponding restriction. Could you quote the specific requirement you're writing about? [Client:]
I do notice the difference between c++03 26.3.2.2 and c++11 26.6.2.3 about assignments between different sized valarray and I perfectly agree with you. But, as already stated, this is not a simple valarray assignment but a mask_array assignment (c++03 26.3.8 / c++11 26.6.8). See c++11 quote below: 26.6.8 Class template mask_array
26.6.8.1 Class template mask_array overview
[....]
This template is a helper template used by the mask subscript operator: mask_array<T> valarray<T>::operator[](const valarray<bool>&).
It has reference semantics to a subset of an array specified by a boolean mask. Thus, the expression a[mask] = b; has the effect of assigning the elements of b to the masked elements in a (those for which the corresponding element in mask is true.)
26.6.8.2 mask_array assignment
void operator=(const valarray<T>&) const; const mask_array& operator=(const mask_array&) const;1 These assignment operators have reference semantics, assigning the values of the argument array elements to selected elements of the valarray<T> object to which it refers.
In particular, [one of the WG21 experts] insisted on the piece "the elements of b".
That is why I reported the test t263.dir/_26322Y31.cpp having an undefined behaviour. [Plum Hall:]
OK, I can see that I will have to ask WG21; I will file an appropriate issue with the Library subgroup. In the meantime, I will mark this testcase as "DISPUTED" so that it is not required for conformance testing, until we get a definitive opinion.
[2012, Kona]
Moved to Open.
There appears to be a real need for clarification in the standard, and implementations differ in their current interpretation. This will need some research by implementers and a proposed resolution before further discussion is likely to be fruitful.
Proposed resolution:
Section: 20.15.4.3 [meta.unary.prop] Status: Open Submitter: Dave Abrahams Opened: 2011-12-09 Last modified: 2016-02-12
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with Open status.
Discussion:
IMO if we specified is_[nothrow_]constructible in terms of a variable declaration whose validity requires destructibility, it is clearly a bug in our specification and a failure to realize the actual original intent. The specification should have been in terms of placement-new.
Daniel:A defaulted copy/move constructor for a class X is defined as deleted (8.4.3 [dcl.fct.def.delete]) if X has:
[…]
— any direct or virtual base class or non-static data member of a type with a destructor that is deleted or inaccessible from the defaulted constructor,
[…]
Dave:
This is about is_nothrow_constructible in particular. The fact that it is
foiled by not having a noexcept dtor is a defect.
[2012, Kona]
Move to Open.
is_nothrow_constructible is defined in terms of is_constructible, which is defined by looking at a hypothetical variable and asking whether the variable definition is known not to throw exceptions. The issue claims that this also examines the type's destructor, given the context, and thus will return false if the destructor can potentially throw. At least one implementation (Howard's) does return false if the constructor is noexcept(true) and the destructor is noexcept(false). So that's not a strained interpretation. The issue is asking for this to be defined in terms of placement new, instead of in terms of a temporary object, to make it clearer that is_nothrow_constructible looks at the noexcept status of only the constructor, and not the destructor.
Sketch of what the wording would look like:
require is_constructible, and then also require that a placement new operation does not throw. (Remembering the title of this issue... What does this imply for swap?
If we accept this resolution, do we need any changes to swap?
STL argues: no, because you are already forbidden from passing anything with a throwing desturctor to swap.
Dietmar argues: no, not true. Maybe statically the destructor can conceivably throw for some values, but maybe there are some values known not to throw. In that case, it's correct to pass those values to swap.
Proposed resolution:
Section: 22.4.2.2.2 [facet.num.put.virtuals], 27.5.3.1.2 [ios::fmtflags], 27.5.6.1 [fmtflags.manip] Status: Open Submitter: Benjamin Kosnik Opened: 2011-12-15 Last modified: 2016-04-15
Priority: 3
View other active issues in [facet.num.put.virtuals].
View all other issues in [facet.num.put.virtuals].
View all issues with Open status.
Discussion:
Iostreams should include a manipulator to toggle grouping on/off for locales that support grouped digits. This has come up repeatedly and been deferred. See LWG 826 for the previous attempt.
If one is using a locale that supports grouped digits, then output will always include the generated grouping characters. However, very plausible scenarios exist where one might want to output the number, un-grouped. This is similar to existing manipulators that toggle on/off the decimal point, numeric base, or positive sign. See some user commentary here.[21012, Kona]
Move to Open.
This is a feature request.
Walter is slightly uncomfortable with processing feature requests through the issues lists.
Alisdair says this is far from the first feature request that has come in from the issues list.
STL: The fact that you can turn off grouping on hex output is compelling.
Marshall: if we add this flag, we'll need to update tables 87-91 as well.
STL: If it has been implemented somewhere, and it works, we'd be glad to add it.
Howard: We need to say what the default is.
Alisdair sumarizes:
(1) We want clear wording that says what the effect is of turning the flag off;
(2) what the default values are, and
(3) how this fits into tables 87-90. (and 128)
[Issaquah 2014-02-10-12: Move to LEWG]
Since this issue was filed, we have grown a new working group that is better placed to handle feature requests.
We will track such issues with an LEWG status until we get feedback from the Library Evolution Working Group.
[Issaquah 2014-02-12: LEWG discussion]
SF | F | N | A | SA |
2 | 4 | 1 | 0 | 0 |
Think about the ABI break for adding a flag. But this could be mitigated by putting the data into an iword instead of a flag.
This needs to change Stage 2 in [facet.num.put.virtuals].
Previous resolution, which needs the above corrections:
This wording is relative to the FDIS.
Insert in 22.4.2.2.2 [facet.num.put.virtuals] paragraph 5:
Stage 1: The first action of stage 1 is to determine a conversion specifier. The tables that describe this determination use the following local variables
fmtflags flags = str.flags() ; fmtflags basefield = (flags & (ios_base::basefield)); fmtflags uppercase = (flags & (ios_base::uppercase)); fmtflags floatfield = (flags & (ios_base::floatfield)); fmtflags showpos = (flags & (ios_base::showpos)); fmtflags showbase = (flags & (ios_base::showbase)); fmtflags showgrouping = (flags & (ios_base::showgrouping));Change header <ios> synopsis, 27.5.1 [iostreams.base.overview] as indicated:
#include <iosfwd> namespace std { […] // 27.5.6, manipulators: […] ios_base& showpoint (ios_base& str); ios_base& noshowpoint (ios_base& str); ios_base& showgrouping (ios_base& str); ios_base& noshowgrouping(ios_base& str); ios_base& showpos (ios_base& str); ios_base& noshowpos (ios_base& str); […] }Change class ios_base synopsis, 27.5.3 [ios.base] as indicated:
namespace std { class ios_base { public: class failure; // 27.5.3.1.2 fmtflags typedef T1 fmtflags; […] static constexpr fmtflags showpoint = unspecified ; static constexpr fmtflags showgrouping = unspecified ; static constexpr fmtflags showpos = unspecified ; […] }; }Add a new entry to Table 122 — "fmtflags effects" as indicated:
Table 122 — fmtflags effects Element Effect(s) if set […] showpoint generates a decimal-point character unconditionally in generated floatingpoint output showgrouping generates grouping characters unconditionally in generated output […] After 27.5.3.1.2 [ios::fmtflags] p12 insert the following:
ios_base& showgrouping(ios_base& str);-?- Effects: Calls str.setf(ios_base::showgrouping).
-?- Returns: str.ios_base& noshowgrouping(ios_base& str);-?- Effects: Calls str.unsetf(ios_base::showgrouping).
-?- Returns: str.
Proposed resolution:
Section: 27.8.5.1 [stringstream.cons] Status: New Submitter: Nicolai Josuttis Opened: 2012-01-15 Last modified: 2016-02-12
Priority: 3
View all issues with New status.
Discussion:
This issue was raised while discussing issue 1448.
Note the following program:string s("s1: 123456789"); ostringstream s1(s, ios_base::out|ios_base::app); s1 << "hello"; cout << s1.str() << endl;
With g++4.x it prints:
s1: 123456789hello
With VisualC++10 it prints:
hello23456789
From my intuitive understanding the flag "app" should result in the output of g++4.x. I also would read that from 27.5.3.1.4 [ios::openmode] claiming:
app seek to end before each write
However in issue 1448 P.J.Plauger comments:
I think we should say nothing special about app at construction time (thus leaving the write pointer at the beginning of the buffer). Leave implementers wiggle room to ensure subsequent append writes as they see fit, but don't change existing rules for initial seek position.
Note that the flag ate on both platforms appends "hello" to s.
Proposed resolution:
Section: 17.5.1 [structure] Status: Open Submitter: Jens Maurer Opened: 2012-03-08 Last modified: 2016-02-12
Priority: 3
View all issues with Open status.
Discussion:
The front matter in clause 17 should clarify that postconditions will not hold if a standard library function exits via an exception. Postconditions or guarantees that apply when an exception is thrown (beyond the basic guarantee) are described in an "Exception safety" section.
[ 2012-10 Portland: Move to Open ]
Consensus that we do not clearly say this, and that we probably should. A likely location to describe the guarantees of postconditions could well be a new sub-clause following 17.6.4.11 [res.on.required] which serves the same purpose for requires clauses. However, we need such wording before we can make progress.
Also, see 2137 for a suggestion that we want to see a paper resolving both issues together.
[2015-05-06 Lenexa: EirkWF to write paper addressing 2136 and 2137]
MC: Idea is to replace all such "If no exception" postconditions with "Exception safety" sections.
Proposed resolution:
Section: 28.8.3 [re.regex.assign] Status: Open Submitter: Jonathan Wakely Opened: 2012-03-08 Last modified: 2016-02-12
Priority: 3
View all other issues in [re.regex.assign].
View all issues with Open status.
Discussion:
The post-conditions of basic_regex<>::assign 28.8.3 [re.regex.assign] p16 say:
If no exception is thrown, flags() returns f and mark_count() returns the number of marked sub-expressions within the expression.
The default expectation in the library is that post-conditions only hold, if there is no failure (see also 2136), therefore the initial condition should be removed to prevent any misunderstanding.
[ 2012-10 Portland: Move to Open ]
A favorable resolution clearly depends on a favorable resolution to 2136. There is also a concern that this is just one example of where we would want to apply such a wording clean-up, and which is really needed to resolve both this issue and 2136 is a paper providing the clause 17 wording that gives the guarantee for postcondition paragaraphs, and then reviews clauses 18-30 to apply that guarantee consistently. We do not want to pick up these issues piecemeal, as we risk openning many issues in an ongoing process.
[2015-05-06 Lenexa: EirkWF to write paper addressing 2136 and 2137]
Proposed resolution:
This wording is relative to N3376.
template <class string_traits, class A> basic_regex& assign(const basic_string<charT, string_traits, A>& s, flag_type f = regex_constants::ECMAScript);[…]
-15- Effects: Assigns the regular expression contained in the string s, interpreted according the flags specified in f. If an exception is thrown, *this is unchanged. -16- Postconditions:If no exception is thrown,flags() returns f and mark_count() returns the number of marked sub-expressions within the expression.
Section: 17.6.4.2.1 [namespace.std], 19.5 [syserr], 20.10.7.1 [allocator.uses.trait], 20.14.10.1 [func.bind.isbind], 20.14.10.2 [func.bind.isplace], 20.14.14 [unord.hash], 20.15.7.6 [meta.trans.other], 22.3.1 [locale], 22.4.1.4 [locale.codecvt], 28.12.1.4 [re.regiter.incr] Status: Open Submitter: Loïc Joly Opened: 2012-03-08 Last modified: 2016-02-12
Priority: 4
View all other issues in [namespace.std].
View all issues with Open status.
Discussion:
The expression "user-defined type" is used in several places in the standard, but I'm not sure what it means. More specifically, is a type defined in the standard library a user-defined type?
From my understanding of English, it is not. From most of the uses of this term in the standard, it seem to be considered as user defined. In some places, I'm hesitant, e.g. 17.6.4.2.1 [namespace.std] p1:A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.
Does it mean we are allowed to add in the namespace std a specialization for std::vector<std::pair<T, U>>, for instance?
Additional remarks from the reflector discussion: The traditional meaning of user-defined types refers to class types and enum types, but the library actually means here user-defined types that are not (purely) library-provided. Presumably a new term - like user-provided type - should be introduced and properly defined.[ 2012-10 Portland: Move to Deferred ]
The issue is real, in that we never define this term and rely on a "know it when I see it" intuition. However, there is a fear that any attempt to pin down a definition is more likely to introduce bugs than solve them - getting the wording for this precisely correct is likely far more work than we are able to give it.
There is unease at simple closing as NAD, but not real enthusiasm to provide wording either. Move to Deferred as we are not opposed to some motivated individual coming back with full wording to review, but do not want to go out of our way to encourage someone to work on this in preference to other issues.
[2014-02-20 Re-open Deferred issues as Priority 4]
[2015-03-05 Jonathan suggests wording]
I dislike the suggestion to change to "user-provided" type because I already find the difference between user-declared / user-provided confusing for special member functions, so I think it would be better to use a completely different term. The core language uses "user-defined conversion sequence" and "user-defined literal" and similar terms for things which the library provides, so I think we should not refer to "user" at all to distinguish entities defined outside the implementation from things provided by the implementation.
I propose "program-defined type" (and "program-defined specialization"), defined below. The P/R below demonstrates the scope of the changes required, even if this name isn't adopted. I haven't proposed a change for "User-defined facets" in [locale].[Lenexa 2015-05-06]
RS, HT: The core language uses "user-defined" in a specific way, including library things but excluding core language things, let's use a different term.
MC: Agree.
RS: "which" should be "that", x2
RS: Is std::vector<MyType> a "program-defined type"?
MC: I think it should be.
TK: std::vector<int> seems to take the same path.
JW: std::vector<MyType> isn't program-defined, we don't need it to be, anything that depends on that also depends on =MyType.
TK: The type defined by an "explicit template specialization" should be a program-defined type.
RS: An implicit instantiation of a "program-defined partial specialization" should also be a program-defined type.
JY: This definition formatting is horrible and ugly, can we do better?
RS: Checking ISO directives.
RS: Define "program-defined type" and "program-defined specialization" instead, to get rid of the angle brackets.
JW redrafting.
Proposed resolution:
This wording is relative to N4296.
Add a new sub-clause to 17.3 [definitions]:
17.3.? [defns.program.defined]
program-defined
<type> a class type or enumeration type which is not part of the C++ standard library and not defined by the implementation. [Note: Types defined by the implementation include extensions (1.4 [intro.compliance]) and internal types used by the library. — end note]program-defined
<specialization> an explicit template specialization or partial specialization which is not part of the C++ standard library and not defined by the implementation.Change 17.6.4.2.1 [namespace.std] paragraph 1+2:
-1- The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a
namespace within namespace std unless otherwise specified. A program may add a template specialization
for any standard library template to namespace std only if the declaration depends on a
userprogram-defined type and the specialization meets the standard library requirements for the
original template and is not explicitly prohibited.
Change 19.5 [syserr] paragraph 4:
-4- The is_error_code_enum and is_error_condition_enum may be specialized for
userprogram-defined types to indicate that such types are eligible for class error_code
and class error_condition automatic conversions, respectively.
Change 20.10.7.1 [allocator.uses.trait] paragraph 1:
-1- Remarks: automatically detects […]. A program may specialize this template to derive from
true_type for a userprogram-defined type T that does not have a nested
allocator_type but nonetheless can be constructed with an allocator where either: […]
Change 20.14.10.1 [func.bind.isbind] paragraph 2:
-2- Instantiations of the is_bind_expression template […]. A program may specialize
this template for a userprogram-defined type T to have a BaseCharacteristic
of true_type to indicate that T should be treated as a subexpression in a bind call.
Change 20.14.10.2 [func.bind.isplace] paragraph 2:
-2- Instantiations of the is_placeholder template […]. A program may specialize this template for a
userprogram-defined type T to have a BaseCharacteristic of
integral_constant<int, N> with N > 0 to indicate that T should be
treated as a placeholder type.
Change 20.14.14 [unord.hash] paragraph 1:
The unordered associative containers defined in 23.5 use specializations of the class template hash […], the instantiation hash<Key> shall:
[…]
[…]
[…]
[…]
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 exception unless
hash<Key> is a userprogram-defined specialization that depends on at least one
userprogram-defined type.
Change 20.15.7.5 [meta.trans.ptr] Table 57 (common_type row):
Table 57 — Other transformations Template Condition Comments … template <class... T>
struct common_type;The member typedef type shall be
defined or omitted as specified below.
[…]. A program may
specialize this trait if at least one
template parameter in the
specialization is auserprogram-defined type.
[…]…
Change 22.4.1.4 [locale.codecvt] paragraph 3:
-3- The specializations required in Table 81 (22.3.1.1.1) […]. Other encodings can be converted
by specializing on a userprogram-defined stateT type.[…]
Change 28.12.1.4 [re.regiter.incr] paragraph 8:
-8- [Note: This means that a compiler may call an implementation-specific search function, in which case
a userprogram-defined specialization of regex_search will not be called. —
end note]
Section: 17.6.3.1 [utility.arg.requirements] Status: Open Submitter: Nikolay Ivchenkov Opened: 2012-03-23 Last modified: 2016-06-21
Priority: 2
View all other issues in [utility.arg.requirements].
View all issues with Open status.
Discussion:
According to 17.6.3.1 [utility.arg.requirements] p1
The template definitions in the C++ standard library refer to various named requirements whose details are set out in tables 17-24. In these tables, T is an object or reference type to be supplied by a C++ program instantiating a template; a, b, and c are values of type (possibly const) T; s and t are modifiable lvalues of type T; u denotes an identifier; rv is an rvalue of type T; and v is an lvalue of type (possibly const) T or an rvalue of type const T.
Is it really intended that T may be a reference type? If so, what should a, b, c, s, t, u, rv, and v mean? For example, are "int &" and "int &&" MoveConstructible?
As far as I understand, we can explicitly specify template arguments for std::swap and std::for_each. Can we use reference types there?
#include <iostream>
#include <utility>
int main()
{
int x = 1;
int y = 2;
std::swap<int &&>(x, y); // undefined?
std::cout << x << " " << y << std::endl;
}
#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>
struct F
{
void operator()(int n)
{
std::cout << n << std::endl;
++count;
}
int count;
} f;
int main()
{
int arr[] = { 1, 2, 3 };
auto&& result = std::for_each<int *, F &&>( // undefined?
std::begin(arr),
std::end(arr),
std::move(f));
std::cout << "count: " << result.count << std::endl;
}
Are these forms of usage well-defined?
Let's also consider the following constructor of std::thread:template <class F, class ...Args> explicit thread(F&& f, Args&&... args);Requires: F and each Ti in Args shall satisfy the MoveConstructible requirements.
When the first argument of this constructor is an lvalue (e.g. a name of a global function), template argument for F is deduced to be lvalue reference type. What should "MoveConstructible" mean with regard to an lvalue reference type? Maybe the wording should say that std::decay<F>::type and each std::decay<Ti>::type (where Ti is an arbitrary item in Args) shall satisfy the MoveConstructible requirements?
[2013-03-15 Issues Teleconference]
Moved to Open.
The questions raised by the issue are real, and should have a clear answer.
[2015-10, Kona Saturday afternoon]
STL: std::thread needs to be fixed, and anything behaving like it needs to be fixed, rather than reference types. std::bind gets this right. We need to survey this. GR: That doesn't sound small to me. STL: Seach for CopyConstructible etc. It may be a long change, but not a hard one.
MC: It seems that we don't have a PR. Does anyone have one? Is anyone interested in doing a survey?
[2016-03, Jacksonville]
Casey volunteers to make a survey
[2016-06, Oulu]
During an independent survey performed by Daniel as part of the analysis of LWG 2716, some overlap was found between these two issues. Daniel suggested to take responsibility for surveying LWG 2146 and opined that the P/R of LWG 2716 should restrict to forwarding references, where the deduction to lvalue references can happen without providing an explicit template argument just by providing an lvalue function argument.
Proposed resolution:
Section: 21.3.1.1 [string.require] Status: Open Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2016-02-12
Priority: 3
View all other issues in [string.require].
View all issues with Open status.
Discussion:
In C++11, basic_string is not described as a "container", and is not governed by the allocator-aware container semantics described in sub-clause 23.2 [container.requirements]; as a result, and requirements or contracts for the basic_string interface must be documented in Clause 21 [strings].
Sub-clause 21.3.1.6.8 [string::swap] defines the swap member function with no requirements, and with guarantees to execute in constant time without throwing. Fulfilling such a contract is not reasonable in the presence of unequal non-propagating allocators. In contrast, 23.2.1 [container.requirements.general] p7 declares the behavior of member swap for containers with unequal non-propagating allocators to be undefined. Resolution proposal: Additional language from Clause 23 [containers] should probably be copied to Clause 21 [strings]. I will refrain from an exactly recommendation, however, as I am raising further issues related to the language in Clause 23 [containers].[2013-03-15 Issues Teleconference]
Moved to Open.
Alisdair has offered to provide wording.
Telecon notes that 23.2.1 [container.requirements.general]p13 says that string is an allocator-aware container.
Proposed resolution:
Section: 17.6.3.2 [swappable.requirements], 23.2.1 [container.requirements.general] Status: Open Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2016-03-08
Priority: 2
View all other issues in [swappable.requirements].
View all issues with Open status.
Discussion:
Sub-clause 17.6.3.2 [swappable.requirements] defines two notions of swappability: a binary version defining when two objects are swappable with one another, and a unary notion defining whether an object is swappable (without qualification), with the latter definition requiring that the object satisfy the former with respect to all values of the same type.
Let T be a container type based on a non-propagating allocator whose instances do not necessarily compare equal. Then sub-clause 23.2.1 [container.requirements.general] p7 implies that no object t of type T is swappable (by the unary definition). Throughout the standard it is the unary definition of "swappable" that is listed as a requirement (with the exceptions of 20.2.2 [utility.swap] p4, 20.4.2 [pairs.pair] p31, 20.5.2.3 [tuple.swap] p2, 25.4.3 [alg.swap] p2, and 25.4.3 [alg.swap] p6, which use the binary definition). This renders many of the mutating sequence algorithms of sub-clause 25.4 [alg.modifying.operations], for example, inapplicable to sequences of standard container types, even where every element of the sequence is swappable with every other. Note that this concern extends beyond standard containers to all future allocator-based types. Resolution proposal: I see two distinct straightforward solutions:I favor the latter solution, for reasons detailed in the following issue.
[ 2012-10 Portland: Move to Open ]
The issue is broader than containers with stateful allocotors, although they are the most obvious example contained within the standard itself. The basic problem is that once you have a stateful allocator, that does not propagate_on_swap, then whether two objects of this type can be swapped with well defined behavior is a run-time property (the allocators compare equal) rather than a simple compile-time property that can be deduced from the type. Strictly speaking, any type where the nature of swap is a runtime property does not meet the swappable requirements of C++11, although typical sequences of such types are going to have elements that are all swappable with any other element in the sequence (using our other term of art for specifying requirements) as the common case is a container of elements who all share the same allocator.
The heart of the problem is that the swappable requirments demand that any two objects of the same type be swappable with each other, so if any two such objects would not be swappable with each other, then the whole type is never swappable. Many algorithms in clause 25 are specified in terms of swappable which is essentially an overspecification as all they actually need is that any element in the sequence is swappable with any other element in the sequence.
At this point Howard joins the discussion and points out that the intent of introducing the two swap-related terms was to support vector<bool>::reference types, and we are reading something into the wording that was never intended. Consuses is that regardless of the intent, that is what the words today say.
There is some support to see a paper reviewing the whole of clause 25 for this issue, and other select clauses as may be necessary.
There was some consideration to introducing a note into the front of clause 25 to indicate swappable requirements in the clause should be interpreted to allow such awkward types, but ultimately no real enthusiasm for introducing a swappable for clause 25 requirement term, especially if it confusingly had the same name as a term used with a subtly different meaning through the rest of the standard.
There was no enthusiasm for the alternate resolution of requiring containers with unequal allocators that do not propagate provide a well-defined swap behavior, as it is not believed to be possible without giving swap linear complexity for such values, and even then would require adding the constraint that the container element types are CopyConstructible.
Final conclusion: move to open pending a paper from a party with a strong interest in stateful allocators.
[2016-03 Jacksonville]
Alisdair says that his paper P0178 addresses this.
Proposed resolution:
Section: 20.2.2 [utility.swap], 17.6.3.2 [swappable.requirements], 23.2.1 [container.requirements.general] Status: Open Submitter: Robert Shearer Opened: 2012-04-13 Last modified: 2016-03-08
Priority: 2
View all other issues in [utility.swap].
View all issues with Open status.
Discussion:
Sub-clause 20.2.2 [utility.swap] defines a non-member 'swap' function with defined behavior for all MoveConstructible and MoveAssignable types. It does not guarantee constant-time complexity or noexcept in general, however this definition does render all objects of MoveConstructible and MoveAssignable type swappable (by the unary definition of sub-clause 17.6.3.2 [swappable.requirements]) in the absence of specializations or overloads.
The overload of the non-member swap function defined in Table 96, however, defines semantics incompatible with the generic non-member swap function, since it is defined to call a member swap function whose semantics are undefined for some values of MoveConstructible and MoveAssignable types. The obvious (perhaps naive) interpretation of sub-clause 17.6.3.2 [swappable.requirements] is as a guide to the "right" semantics to provide for a non-member swap function (called in the context defined by 17.6.3.2 [swappable.requirements] p3) in order to provide interoperable user-defined types for generic programming. The standard container types don't follow these guidelines. More generally, the design in the standard represents a classic example of "contract narrowing". It is entirely reasonable for the contract of a particular swap overload to provide more guarantees, such as constant-time execution and noexcept, than are provided by the swap that is provided for any MoveConstructible and MoveAssignable types, but it is not reasonable for such an overload to fail to live up to the guarantees it provides for general types when it is applied to more specific types. Such an overload or specialization in generic programming is akin to an override of an inherited virtual function in OO programming: violating a superclass contract in a subclass may be legal from the point of view of the language, but it is poor design and can easily lead to errors. While we cannot prevent user code from providing overloads that violate the more general swap contract, we can avoid doing so within the library itself. My proposed resolution is to draw a sharp distinction between member swap functions, which provide optimal performance but idiosyncratic contracts, and non-member swap functions, which should always fulfill at least the contract of 20.2.2 [utility.swap] and thus render objects swappable. The member swap for containers with non-propagating allocators, for example, would offer constant-time guarantees and noexcept but would only offer defined behavior for values with allocators that compare equal; non-member swap would test allocator equality and then dispatch to either member swap or std::swap depending on the result, providing defined behavior for all values (and rendering the type "swappable"), but offering neither the constant-time nor the noexcept guarantees.[2013-03-15 Issues Teleconference]
Moved to Open.
This topic deserves more attention than can be given in the telocon, and there is no proposed resolution.
[2013-03-15 Issues Teleconference]
Moved to Open.
This topic deserves more attention than can be given in the telocon, and there is no proposed resolution.
[2016-03 Jacksonville]
Alisdair says that his paper P0178 addresses this.
Proposed resolution:
Section: 26.6.1.3 [rand.req.urng] Status: New Submitter: John Salmon Opened: 2012-04-26 Last modified: 2016-02-12
Priority: 4
View all other issues in [rand.req.urng].
View all issues with New status.
Discussion:
The expressions G::min() and G::max() in Table 116 in 26.6.1.3 [rand.req.urng] are specified as having "compile-time" complexity.
It is not clear what, exactly, this requirement implies. If a URNG has a method:static int min();
then is the method required to have a constexpr qualifier? I believe the standard would benefit from clarification of this point.
Proposed resolution:
Section: 18.10 [support.runtime] Status: Open Submitter: Thomas Plum Opened: 2012-04-30 Last modified: 2016-02-12
Priority: 4
View other active issues in [support.runtime].
View all other issues in [support.runtime].
View all issues with Open status.
Discussion:
Since C99, the C standard describes a macro named __bool_true_false_are_defined.
In the process of harmonizing C++11 with C99, this name became part of the C++ standard. I propose that all mention of this name should be removed from the C and C++ standards. Here's the problem: The name was originally proposed as a transition tool, so that the headers for a project could contain lines like the following.#if !defined(__bool_true_false_are_defined) #define bool int /* or whatever */ #define true 1 #define false 0 #endif
Then when the project was compiled by a "new" compiler that implemented bool as defined by the evolving C++98 or C99 standards, those lines would be skipped; but when compiled by an "old" compiler that didn't yet provide bool, true, and false, then the #define's would provide a simulation that worked for most purposes.
It turns out that there is an unfortunate ambiguity in the name. One interpretation is as shown above, but a different reading says "bool, true, and false are #define'd", i.e. that the meaning of the macro is to assert that these names are macros (not built-in) ... which is true in C, but not in C++. In C++11, the name appears in parentheses followed by a stray period, so some editorial change is needed in any event: 18.10 [support.runtime] para 1:Headers <csetjmp> (nonlocal jumps), <csignal> (signal handling), <cstdalign> (alignment), <cstdarg> (variable arguments), <cstdbool> (__bool_true_false_are_defined). <cstdlib> (runtime environment getenv(), system()), and <ctime> (system clock clock(), time()) provide further compatibility with C code.
However, para 2 says
"The contents of these headers are the same as the Standard C library headers <setjmp.h>, <signal.h>, <stdalign.h>, <stdarg.h>, <stdbool.h>, <stdlib.h>, and <time.h>, respectively, with the following changes:",
and para 8 says
"The header <cstdbool> and the header <stdbool.h> shall not define macros named bool, true, or false."
Thus para 8 doesn't exempt the C++ implementation from the arguably clear requirement of the C standard, to provide a macro named __bool_true_false_are_defined defined to be 1.
Real implementations of the C++ library differ, so the user cannot count upon any consistency; furthermore, the usefulness of the transition tool has faded long ago. That's why my suggestion is that both C and C++ standards should eliminate any mention of __bool_true_false_are_defined. In that case, the name belongs to implementers to provide, or not, as they choose.[2013-03-15 Issues Teleconference]
Moved to Open.
While not strictly necessary, the clean-up look good.
We would like to hear from our C liaison before moving on this issue though.
[2015-05 Lenexa]
LWG agrees. Jonathan provides wording.
Proposed resolution:
This wording is relative to N4296.
Edit the footnote on 17.6.1.2 [headers] p7:
176) In particular, including any of the standard headers <stdbool.h>, <cstdbool>, <iso646.h> or <ciso646> has no effect.
Edit 18.10 [support.runtime] p1 as indicated (and remove the index entry for __bool_true_false_are_defined):
-1- Headers <csetjmp> (nonlocal jumps), <csignal> (signal handling), <cstdalign> (alignment), <cstdarg> (variable arguments), <cstdbool>,
(__bool_true_false_are_defined).<cstdlib> (runtime environment getenv(), system()), and <ctime> (system clock clock(), time()) provide further compatibility with C code.
Remove Table 38 — Header <cstdbool> synopsis [tab:support.hdr.cstdbool] from 18.10 [support.runtime]
Table 38 — Header <cstdbool> synopsis Type Name(s) Macro: __bool_true_false_are_defined
Section: 23.3.7.8 [array.zero] Status: Open Submitter: Daryle Walker Opened: 2012-05-08 Last modified: 2016-02-12
Priority: 3
View all other issues in [array.zero].
View all issues with Open status.
Discussion:
Objects of std::array<T,N> are supposed to be initialized with aggregate initialization (when not the destination of a copy or move). This clearly works when N is positive. What happens when N is zero? To continue using an (inner) set of braces for initialization, a std::array<T,0> implementation must have an array member of at least one element, and let default initialization take care of those secret elements. This cannot work when T has a set of constructors and the default constructor is deleted from that set. Solution: Add a new paragraph in 23.3.7.8 [array.zero]:
The unspecified internal structure of array for this case shall allow initializations like:
array<T, 0> a = { };and said initializations must be valid even when T is not default-constructible.
[2012, Portland: Move to Open]
Some discussion to understand the issue, which is that implementations currently have freedom to implement an empty array by holding a dummy element, and so might not support value initialization, which is surprising when trying to construct an empty container. However, this is not mandated, it is an unspecified implementation detail.
Jeffrey points out that the implication of 23.3.7.1 [array.overview] is that this initialization syntax must be supported by empty array objects already. This is a surprising inference that was not obvious to the room, but consensus is that the reading is accurate, so the proposed resolution is not necessary, although the increased clarity may be useful.
Further observation is that the same clause effectively implies that T must always be DefaultConstructible, regardless of N for the same reasons - as an initializer-list may not supply enough values, and the remaining elements must all be value initialized.
Concern that we are dancing angels on the head of pin, and that relying on such subtle implications in wording is not helpful. We need a clarification of the text in this area, and await wording.
[2015-02 Cologne]
DK: What was the outcome of Portland? AM: Initially we thought we already had the intended behaviour. We concluded that T must always be DefaultConstructible, but I'm not sure why. GR: It's p2 in std::array, "up to N". AM: That wording already implies that "{}" has to work when N is zero. But the wording of p2 needs to be fixed to make clear that it does not imply that T must be DefaultConstructible.
Conclusion: Update wording, revisit later.[2015-10, Kona Saturday afternoon]
MC: How important is this? Can you not just use default construction for empty arrays?
TK: It needs to degenerate properly from a pack. STL agrees.
JW: Yes, this is important, and we have to make it work.
MC: I hate the words "initialization like".
JW: I'll reword this.
WEB: Can I ask that once JW has reworded this we move it to Review rather than Open?
MC: We'll try to review it in a telecon and hopefully get it to tentatively ready.
STL: Double braces must also work: array<T, 0> a = {{}};.
Jonathan to reword.
Proposed resolution:
This wording is relative to N3376.
Add the following new paragraph between the current 23.3.7.8 [array.zero] p1 and p2:
-1- array shall provide support for the special case N == 0.
-?- The unspecified internal structure of array for this case shall allow initializations like:array<T, 0> a = { };and said initializations must be valid even when T is not default-constructible.
-2- In the case that N == 0, begin() == end() == unique value. The return value of data() is unspecified. -3- The effect of calling front() or back() for a zero-sized array is undefined. -4- Member function swap() shall have a noexcept-specification which is equivalent to noexcept(true).
Section: 23.3.11.3 [vector.capacity] Status: New Submitter: Nikolay Ivchenkov Opened: 2012-05-08 Last modified: 2016-02-12
Priority: 2
View other active issues in [vector.capacity].
View all other issues in [vector.capacity].
View all issues with New status.
Discussion:
There are various operations on std::vector that can cause elements of the vector to be moved from one location to another. A move operation can use either rvalue or const lvalue as argument; the choice depends on the value of !is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value, where T is the element type. Thus, some operations on std::vector (e.g. 'resize' with single parameter, 'reserve', 'emplace_back') should have conditional requirements. For example, let's consider the requirement for 'reserve' in N3376 – 23.3.11.3 [vector.capacity]/2:
Requires: T shall be MoveInsertable into *this.
This requirement is not sufficient if an implementation is free to select copy constructor when !is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value evaluates to true. Unfortunately, is_copy_constructible cannot reliably determine whether T is really copy-constructible. A class may contain public non-deleted copy constructor whose definition does not exist or cannot be instantiated successfully (e.g., std::vector<std::unique_ptr<int>> has copy constructor, but this type is not copy-constructible). Thus, the actual requirements should be:
if !is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value then T shall be CopyInsertable into *this;
otherwise T shall be MoveInsertable into *this.
Maybe it would be useful to introduce a new name for such conditional requirement (in addition to "CopyInsertable" and "MoveInsertable").
Proposed resolution:
Section: 23.4 [associative], 23.5 [unord] Status: New Submitter: Bjarne Stroustrup Opened: 2012-06-18 Last modified: 2016-02-12
Priority: 2
View other active issues in [associative].
View all other issues in [associative].
View all issues with New status.
Discussion:
As described in the reflector discussion c++std-core-21860 consider the following example:
map<const int, int> mci{}; map<int, int> mi = mci; // ?? mci[1] = 2; mi[1] = 2;
Should it be required that the marked initialization is well-formed? As a possible solution this could be realized by an alias template:
template <class K, class T> struct OriginalMap { […] }; template <class K, class T> using ImprovedMap = OriginalMap<const K, T>;
Proposed resolution:
Section: 23.3.11.5 [vector.modifiers], 23.2 [container.requirements] Status: Open Submitter: Howard Hinnant Opened: 2012-07-07 Last modified: 2016-02-12
Priority: 2
View other active issues in [vector.modifiers].
View all other issues in [vector.modifiers].
View all issues with Open status.
Discussion:
Nikolay Ivchenkov recently brought the following example on the std-discussion newsgroup, asking whether the following program well-defined:
#include <iostream> #include <vector> int main() { std::vector<int> v; v.reserve(4); v = { 1, 2, 3 }; v.emplace(v.begin(), v.back()); for (int x : v) std::cout << x << std::endl; }
Nikolay Ivchenkov:
I think that an implementation of vector's 'emplace' should initialize an intermediate object with v.back() before any shifts take place, then perform all necessary shifts and finally replace the value pointed to by v.begin() with the value of the intermediate object. So, I would expect the following output:3 1 2 3
GNU C++ 4.7.1 and GNU C++ 4.8.0 produce other results:
2 1 2 3
Howard Hinnant:
I believe Nikolay is correct that vector should initialize an intermediate object with v.back() before any shifts take place. As Nikolay pointed out in another email, this appears to be the only way to satisfy the strong exception guarantee when an exception is not thrown by T's copy constructor, move constructor, copy assignment operator, or move assignment operator as specified by 23.3.11.5 [vector.modifiers]/p1. I.e. if the emplace construction throws, the vector must remain unaltered. That leads to an implementation that tolerates objects bound to the function parameter pack of the emplace member function may be elements or sub-objects of elements of the container. My position is that the standard is correct as written, but needs a clarification in this area. Self-referencing emplace should be legal and give the result Nikolay expects. The proposed resolution of LWG 760 is not correct.[2015-02 Cologne]
LWG agrees with the analysis including the assessment of LWG 760 and would appreciate a concrete wording proposal.
[2015-04-07 dyp comments]
The Standard currently does not require that creation of such intermediate objects is legal. 23.2.3 [sequence.reqmts] Table 100 — "Sequence container requirements" currently specifies:
Table 100 — Sequence container requirements Expression Return type Assertion/note
pre-/post-condition… a.emplace(p, args); iterator Requires: T is EmplaceConstructible into X from args. For vector and deque, T is also MoveInsertable into X and MoveAssignable. […] …
The EmplaceConstructible concept is defined via allocator_traits<A>::construct in 23.2.1 [container.requirements.general] p15.5 That's surprising to me since the related concepts use the suffix Insertable if they refer to the allocator. An additional requirement such as std::is_constructible<T, Args...> is necessary to allow creation of intermediate objects.
The creation of intermediate objects also affects other functions, such as vector.insert. Since aliasing the vector is only allowed for the single-element forms of insert and emplace (see 526), the range-forms are not affected. Similarly, aliasing is not allowed for the rvalue-reference overload. See also LWG 2266.
There might be a problem with a requirement of std::is_constructible<T, Args...> related to the issues described in LWG 2461. For example, a scoped allocator adapter passes additional arguments to the constructor of the value type. This is currently not done in recent implementations of libstdc++ and libc++ when creating the intermediate objects, they simply create the intermediate object by perfectly forwarding the arguments. If such an intermediate object is then moved to its final destination in the vector, a change of the allocator instance might be required — potentially leading to an expensive copy. One can also imagine worse problems, such as run-time errors (allocators not comparing equal at run-time) or compile-time errors (if the value type cannot be created without the additional arguments). I have not looked in detail into this issue, but I'd be reluctant adding a requirement such as std::is_constructible<T, Args...> without further investigation.
It should be noted that the creation of intermediate objects currently is inconsistent in libstdc++ vs libc++. For example, libstdc++ creates an intermediate object for vector.insert, but not vector.emplace, whereas libc++ does the exact opposite in this respect.
A live demo of the inconsistent creation of intermediate objects can be found here.
[2015-10, Kona Saturday afternoon]
HH: If it were easy, it'd have wording. Over the decades I have flipped 180 degrees on this. My current position is that it should work even if the element is in the same container.
TK: What's the implentation status? JW: Broken in GCC. STL: Broken in MSVS. Users complain about this every year.
MC: 526 says push_back has to work.
HH: I think you have to make a copy of the element anyway for reasons of exception safety. [Discussion of exception guarantees]
STL: vector has strong exception guarantees. Could we not just provide the Basic guarantee here.
HH: It would terrify me to relax that guarantee. It'd be an ugly, imperceptible runtime error.
HH: I agree if we had a clean slate that strong exception safety is costing us here, and we shouldn't provide it if it costs us.
STL: I have a mail here, "how can vector provide the strong guarantee when inserting in the middle".
HH: The crucial point is that you only get the strong guarantee if the exception is thrown by something other than the copy and move operations that are used to make the hole.
STL: I think we need to clean up the wording. But it does mandate currently that the self-emplacement must work, because nothings says that you can't do it. TK clarifies that a) self-emplacement must work, and b) you get the strong guarantee only if the operations for making the hole don't throw, otherwise basic. HH agrees. STL wants this to be clear in the Standard.
STL: Should it work for deque, too? HH: Yes.
HH: I will attempt wording for this.
TK: Maybe mail this to the reflector, and maybe someone has a good idea?
JW: I will definitely not come up with anything better, but I can critique wording.
Moved to Open; Howard to provide wording, with feedback from Jonathan.
Proposed resolution:
Section: 25.5.6 [alg.heap.operations] Status: New Submitter: Peter Sommerlad Opened: 2012-07-09 Last modified: 2016-02-12
Priority: 3
View all other issues in [alg.heap.operations].
View all issues with New status.
Discussion:
Another similar issue to the operator< vs greater in nth_element but not as direct occurs in 25.5.6 [alg.heap.operations]:
-1- A heap is a particular organization of elements in a range between two random access iterators [a,b). Its two key properties are:
- There is no element greater than *a in the range and
- *a may be removed by pop_heap(), or a new element added by push_heap(), in O(log(N)) time.
As noted by Richard Smith, it seems that the first bullet should read:
*a is not less than any element in the range
Even better the heap condition could be stated here directly, instead of leaving it unspecified, i.e.,
Each element at (a+2*i+1) and (a+2*i+2) is less than the element at (a+i), if those elements exist, for i>=0.
But may be that was may be intentional to allow other heap organizations?
See also follow-up discussion of c++std-lib-32780.Proposed resolution:
Section: 25 [algorithms] Status: New Submitter: Nikolay Ivchenkov Opened: 2012-08-01 Last modified: 2016-02-12
Priority: 4
View other active issues in [algorithms].
View all other issues in [algorithms].
View all issues with New status.
Discussion:
According to 25.1 [algorithms.general]/12,
In the description of the algorithms operators + and - are used for some of the iterator categories for which they do not have to be defined. In these cases the semantics of a+n is the same as that of
X tmp = a; advance(tmp, n); return tmp;
There are several places where such operator + is applied to an output iterator — for example, see the description of std::copy:
template<class InputIterator, class OutputIterator> OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result);-1- Effects: Copies elements in the range [first,last) into the range [result,result + (last - first)) starting from first and proceeding to last. For each non-negative integer n < (last - first), performs *(result + n) = *(first + n).
std::advance is not supposed to be applicable to output iterators, so we need a different method of description.
See also message c++std-lib-32908.[2014-06-07 Daniel comments and provides wording]
The specification for output iterators is somewhat tricky, because here a sequence of increments is required to be combined with intervening assignments to the dereferenced iterator. I tried to respect this fact by using a conceptual assignment operation as part of the specification.
Another problem in the provided as-if-code is the question which requirements are imposed on n. Unfortunately, the corresponding function advance is completely underspecified in this regard, so I couldn't borrow wording from it. We cannot even assume here that n is the difference type of the iterator, because for output iterators there is no requirements for this associated type to be defined. The presented wording attempts to minimize assumptions, but still can be considered as controversial.Proposed resolution:
This wording is relative to N3936.
Change 25.1 [algorithms.general] around p12 as indicated:
-12- In the description of the algorithms operators + and - are used for some of the iterator categories for which they do not have to be defined. In these cases the semantics of a+n is the same as that of
X tmp = a; advance(tmp, n); return tmp;when X meets the input iterator requirements (24.2.3 [input.iterators]), otherwise it is the same as that of
X tmp = a; for (auto i = n; i; ++tmp, (void) --i) *tmp = Expr(i); return tmp;where Expr(i) denotes the n-i-th expression that is assigned to for the corresponding algorithm; and that of b-a is the same as of
return distance(a, b);
Section: 17.6.3.5 [allocator.requirements], C.5 [diff.library] Status: Open Submitter: Nevin Liber Opened: 2012-08-14 Last modified: 2016-02-12
Priority: 3
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with Open status.
Discussion:
Given that a number of things were removed from the allocator requirements (reference, const_reference, address() in 17.6.3.5 [allocator.requirements]), it seems that these incompatible changes should be mentioned in Annex C.5 [diff.library], more specifically in [diff.cpp03].
[ 2012-10 Portland: Move to Open ]
It was clearly pointed out by Bill during the C++11 process that our change to allocator requirements potentially broke 3rd party user containers written to expect C++03 allocators, or rather, an allocator written to the minimal requirements of C++11 might not be guaranteed to work with a container written to the previous rules. This was a trade-off in making allocaters easier to write by use of the allocator_traits framework.
This probably does merit a write-up in Annex C, and we look forward to seeing wording. Until then, the best we can do is move the issue to Open.
Proposed resolution:
Section: 28.10.1 [re.results.const], 28.10.6 [re.results.all] Status: New Submitter: Pete Becker Opened: 2012-08-29 Last modified: 2016-02-12
Priority: 3
View other active issues in [re.results.const].
View all other issues in [re.results.const].
View all issues with New status.
Discussion:
28.10.1 [re.results.const] p1 says:
In all match_results constructors, a copy of the Allocator argument shall be used for any memory allocation performed by the constructor or member functions during the lifetime of the object.
There are three constructors:
match_results(const Allocator& = Allocator()); match_results(const match_results& m); match_results(match_results&& m) noexcept;
The second and third constructors do no have an Allocator argument, so despite the "all match_results constructors", it is not possible to use "the Allocator argument" for the second and third constructors.
The requirements for those two constructors also does not give any guidance. The second constructor has no language about allocators, and the third states that the stored Allocator value is move constructed from m.get_allocator(), but doesn't require using that allocator to allocate memory. The same basic problem recurs in 28.10.6 [re.results.all], which gives the required return value for get_allocator():Returns: A copy of the Allocator that was passed to the object's constructor or, if that allocator has been replaced, a copy of the most recent replacement.
Again, the second and third constructors do not take an Allocator, so there is nothing that meets this requirement when those constructors are used.
Proposed resolution:
Section: 28.10.1 [re.results.const], 28.10.6 [re.results.all] Status: New Submitter: Pete Becker Opened: 2012-08-29 Last modified: 2016-02-12
Priority: 3
View other active issues in [re.results.const].
View all other issues in [re.results.const].
View all issues with New status.
Discussion:
The effects of the two assignment operators are specified in Table 141. Table 141 makes no mention of allocators, so, presumably, they don't touch the target object's allocator. That's okay, but it leaves the question: match_results::get_allocator() is supposed to return "A copy of the Allocator that was passed to the object's constructor or, if that allocator has been replaced, a copy of the most recent replacement"; if assignment doesn't replace the allocator, how can the allocator be replaced?
Proposed resolution:
Section: 23.2.5.1 [unord.req.except] Status: Open Submitter: Alisdair Meredith Opened: 2012-09-23 Last modified: 2016-02-12
Priority: 3
View all issues with Open status.
Discussion:
The hash functor and key-comparison functor of unordered containers are allowed to throw on swap.
23.2.5.1 [unord.req.except]p3 "For unordered associative containers, no swap function throws an exception unless that exception is thrown by the swap of the container's Hash or Pred object (if any)."
In such a case we must offer the basic exception safety guarantee, where both objects are left in valid but unspecified states, and no resources are leaked. This yields a corrupt, un-usable container if the first swap succeeds, but the second fails by throwing, as the functors form a matched pair.
So our basic scenario is first, swap the allocators if the allocators propagate on swap, according to allocator_traits. Next we swap the pointers to our internal hash table data structures, so that they match the allocators that allocated them. (Typically, this operation cannot throw). Now our containers are back in a safely destructible state if an exception follows.
Next, let's say we swap the hash functor, and that throws. We have a corrupt data structure, in that the buckets are not correctly indexed by the correct functors, lookups will give unpredicatable results etc. We can safely restore a usable state by forcibly clearing each container - which does not leak resources and leaves us with two (empty but) usable containers.
Now let us assume that the hasher swap succeeds. Next we swap the equality comparator functor, and this too could throw. The important point to bear in mind is that these two functors form an important pairing - two objects that compare equal by the equality functor must also hash to the same value. If we swap one without the other, we most likely leave the container in an unusable state, even if we clear out all elements.
1. A colleague pointed out that the solution for this is to dynamically allocate the two functors, and then we need only swap pointers, which is not a throwing operation. And if we don't want to allocate on default construction (a common QoI request), we might consider moving to a dynamically allocated functors whenever swap is called, or on first insertion. Of course, allocating memory in swap is a whole new can of worms, but this does not really sound like the design we had intended.
2. The simplest option is to say that we do not support hasher or equality functors that throw on ADL swap. Note that the requirement is simply to not throw, rather than to be explicitly marked as noexcept. Throwing functors are allowed, so long as we never use values that would actually manifest a throw when used in an unordered container.
Pablo went on to give me several more options, to be sure we have a full set to consider:
3. Disallow one or the other functor from throwing. In that case, the possibly-throwing functor must be swapped first, then the other functor, the allocator, and the data pointer(s) afterwards (in any order -- there was a TC that allocator assignment and swap may not throw if the corresponding propagation trait is true.). Of course, the question becomes: which functor is allowed to throw and which one is not?
4. Require that any successful functor swap be reliably reversible. This is very inventive. I know of no other place in the standard where such a requirement is stated, though I have occasionally wanted such a guarantee.
5. Allow a failed swap to leave the containers in a state where future insertions may fail for reasons other than is currently allowed. Specifically, if the hash and equality functors are out of sync, all insertions will fail. Presumably some "incompletely swapped" exception would be thrown. This is "slightly" inventive, although people have been discussing "radioactive" states for a while.
[2013-03-15 Issues Teleconference]
Moved to Open.
Proposed resolution:
Section: 28.10.1 [re.results.const] Status: New Submitter: Pete Becker Opened: 2012-10-02 Last modified: 2016-02-12
Priority: 4
View other active issues in [re.results.const].
View all other issues in [re.results.const].
View all issues with New status.
Discussion:
28.10.1 [re.results.const]/3: "Move-constructs an object of class match_results satisfying the same postconditions as Table 141."
Table 141 lists various member functions and says that their results should be the results of the corresponding member function calls on m. But m has been moved from, so the actual requirement ought to be based on the value that m had before the move construction, not on m itself.
In addition to that, the requirements for the copy constructor should refer to Table 141.
Ganesh: Also, the requirements for move-assignment should refer to Table 141. Further it seems as if in Table 141 all phrases of "for all integers n < m.size()" should be replaced by "for all unsigned integers n < m.size()".Proposed resolution:
Section: 28.10 [re.results] Status: Open Submitter: Daniel Krügler Opened: 2012-10-06 Last modified: 2016-02-12
Priority: 3
View other active issues in [re.results].
View all other issues in [re.results].
View all issues with Open status.
Discussion:
The requirement expressed in 28.10 [re.results] p2
The class template match_results shall satisfy the requirements of an allocator-aware container and of a sequence container, as specified in 23.2.3 [sequence.reqmts], except that only operations defined for const-qualified sequence containers are supported.
can be read to require the existence of the described constructors from as well, but they do not exist in the synopsis.
The missing sequence constructors are:match_results(initializer_list<value_type>); match_results(size_type, const value_type&); template<class InputIterator> match_results(InputIterator, InputIterator);
The missing allocator-aware container constructors are:
match_results(const match_results&, const Allocator&); match_results(match_results&&, const Allocator&);
It should be clarified, whether (a) constructors are an exception of above mentioned operations or (b) whether at least some of them (like those accepting a match_results value and an allocator) should be added.
As visible in several places of the standard (including the core language), constructors seem usually to be considered as "operations" and they certainly can be invoked for const-qualified objects. The below given proposed resolution applies only the minimum necessary fix, i.e. it excludes constructors from above requirement.[2013-04-20, Bristol]
Check current implementations to see what they do and, possibly, write a paper.
[2013-09 Chicago]
Ask Daniel to update the proposed wording to include the allocator copy and move constructors.
[2014-01-18 Daniel changes proposed resolution]
Previous resolution from Daniel [SUPERSEDED]:
Change 28.10 [re.results] p2 as indicated:
The class template match_results shall satisfy the requirements of an allocator-aware container and of a sequence container, as specified in 23.2.3 [sequence.reqmts], except that only operations defined for const-qualified sequence containers that are not constructors are supported.
[2015-05-06 Lenexa]
MC passes important knowledge to EF.
VV, RP: Looks good.
TK: Second form should be conditionally noexcept
JY: Sequence constructors are not here, but mentioned in the issue writeup. Why?
TK: That would have been fixed by the superseded wording.
JW: How does this interact with Mike Spertus' allocator-aware regexes? [...] Perhaps it doesn't.
JW: Can't create match_results, want both old and new resolution.
JY: It's problematic that users can't create these, but not this issue.
VV: Why conditional noexcept?
MC: Allocator move might throw.
JW: Update superseded wording to "only non-constructor operations that are"?
MC: Only keep superseded, but append "and the means of constructing match_results are limited to [...]"?
JY: Bullet 4 paragraph 2 needs to address the allocator constructor.
Assigned to JW for drafting.
[2015-10, Kona Saturday afternoon]
STL: I want Mike Spertus to be aware of this issue.
Proposed resolution:
This wording is relative to N3936.
Change 28.10 [re.results] p4, class template match_results synopsis, as indicated:
[…] // 28.10.1, construct/copy/destroy: explicit match_results(const Allocator& a = Allocator()); match_results(const match_results& m); match_results(const match_results& m, const Allocator& a); match_results(match_results&& m) noexcept; match_results(match_results&& m, const Allocator& a) noexcept; […]
Change 28.10.1 [re.results.const] as indicated: [Drafting note: Paragraph 6 as currently written, makes not much sense, because the noexcept does not allow any exception to propagate. Further-on, the allocator requirements do not allow for throwing move constructors. Deleting it seems to be near to editorial — end drafting note]
match_results(const match_results& m); match_results(const match_results& m, const Allocator& a);-4- Effects: Constructs an object of class match_results, as a copy of m.
match_results(match_results&& m) noexcept; match_results(match_results&& m, const Allocator& a) noexcept;-5- Effects: Move-constructs an object of class match_results from m satisfying the same postconditions as Table 142.
AdditionallyFor the first form, the stored Allocator value is move constructed from m.get_allocator().-6- Throws: Nothing if the allocator's move constructor throws nothing.
Section: 23.2.5 [unord.req] Status: Open Submitter: Alisdair Meredith Opened: 2012-10-09 Last modified: 2016-02-12
Priority: 3
View other active issues in [unord.req].
View all other issues in [unord.req].
View all issues with Open status.
Discussion:
The user cannot specify a max_load_factor for their unordered container at construction, it must be supplied after the event, when the container is potentially not empty. The contract for this method is deliberately vague, not guaranteeing to use the value supplied by the user, and any value actually used will be used as a ceiling that the container will attempt to respect.
The only guarantee we have is that, if user requests a max_load_factor that is less than the current load_factor, then the operation will take constant time, thus outlawing an implementation that chooses to rehash and so preserve as a class invariant that load_factor < max_load_factor.
Reasonable options conforming to the standard include ignoring the user's request if the requested value is too low, or deferring the rehash to the next insert operation and allowing the container to have a strange state (wrt max_load_factor) until then - and there is still the question of rehashing if the next insert is for a duplicate key in a unique container.
Given the deliberate vagueness of the current wording, to support a range of reasonable (but not perfect) behaviors, it is not clear why the equally reasonable rehash to restore the constraint should be outlawed. It is not thought that this is a performance critical operation, where users will be repeatedly setting low load factors on populated containers, in a tight or (less unlikely) an instant response scenario.
[2013-03-15 Issues Teleconference]
Moved to Open.
Alisdair to provide wording.
Proposed resolution:
Section: 23.2.5 [unord.req] Status: Open Submitter: Alisdair Meredith Opened: 2012-10-09 Last modified: 2016-02-12
Priority: 3
View other active issues in [unord.req].
View all other issues in [unord.req].
View all issues with Open status.
Discussion:
The default constructor, allocator-aware constructor, and range-based constructors for the unordered containers do not offer a means to control the initial max_load_factor, so the standard mandates the value 1.0. This seems overly restrictive, as there is plenty of research suggesting a value between 0.5 and 1.0 is more often optimal for unique-key containers, and perhaps a slightly higher value might be appropriate for multi-containers.
Rather than guess at the appropriate max_load_factor, it seems reasonable that the standard should allow vendors to pick a value at their discretion, with perhaps a note of advice. It is less clear whether the default value should be implementation-defined or unspecified, given the ease of a user determining this by querying this attribute immediately after construction.
[2013-03-15 Issues Teleconference]
Moved to Open.
Alisdair to provide wording.
Marshall: It seems to me that what you really want is to be able to pass a max load factor in the constructor, but that's a different issue.
Alisdair agrees in principle, but concerned with adding yet more constructors to these classes.
Proposed resolution:
Section: C.5 [diff.library] Status: New Submitter: Kevin McCarty Opened: 2012-02-03 Last modified: 2016-02-12
Priority: 2
View all other issues in [diff.library].
View all issues with New status.
Discussion:
It seems that in C.5 [diff.library], Table 150 the following macros from 18.3.3 [c.limits], Table 31 are missing:
LLONG_MIN LLONG_MAX ULLONG_MAX
In addition in C.5 [diff.library], Table 150 the following macros from 18.3.3 [c.limits], Table 32 are missing:
DECIMAL_DIG FLT_EVAL_METHOD
Furtheron it seems that in C.5 [diff.library], Table 149/150 further macros are missing as well, e.g. HUGE_VALF, INFINITY, etc.
[2014-02 Issaquah:]
This is an issue, all of C has not been updated for C99, C99 functions are missing, whole section needs to be overhauled.
The issue needs to be updated for functions and other missing items and when that happens the issue title is wrong and needs to be adapted.Proposed resolution:
Section: 30.6.8 [futures.async] Status: Deferred Submitter: Detlef Vollmann Opened: 2012-10-19 Last modified: 2016-02-12
Priority: 4
View all other issues in [futures.async].
Discussion:
promise, packaged_task, and async are the only places where a shared state is actually supposed to be allocated. Accordingly, promise and packaged_task are "allocator-aware". But function template async provides no way to provide an allocator.
[2013-09 Chicago]
Matt: deprecate async
Nico: read my paper Alisdair: defer issues to wait for polymorphic allocators Alisdair: defer, active topic of research Deferred[2014-02-20 Re-open Deferred issues as Priority 4]
[2015-05 Lenexa, SG1 response]
We want whatever status approximates: "will not fix; we're working on a replacement facility and don't want to add features to a broken one"
Proposed resolution:
Section: 23.2.3 [sequence.reqmts], 23.2.4 [associative.reqmts], 23.2.5 [unord.req], 26.6.1.2 [rand.req.seedseq] Status: Open Submitter: Jeffrey Yasskin Opened: 2012-10-21 Last modified: 2016-02-12
Priority: 3
View other active issues in [sequence.reqmts].
View all other issues in [sequence.reqmts].
View all issues with Open status.
Discussion:
In 23.2.3 [sequence.reqmts] p3, we have "il designates an object of type initializer_list<value_type>", and then several functions that take 'il' as an argument. However, an expression like {1, 2, 'a'} is not an object of type initializer_list<int> unless it's used to initialize an explicitly-typed variable of that type. I believe we want:
std::vector<int> v; v = {1, 2, 'a'};
to compile portably, so we should say something different when defining 'il'. The same phrasing happens in 23.2.4 [associative.reqmts], 23.2.5 [unord.req], and 26.6.1.2 [rand.req.seedseq].
This may just be an editorial issue because the actual class synopses declare the functions to take initializer_list<exact_type>.[2013-03-15 Issues Teleconference]
Moved to Open.
This is definitely not NAD
Should copy the suggested wording as the proposed resolution.
Proposed resolution:
Section: 27.5.5.2 [basic.ios.cons] Status: Open Submitter: Andrey Semashev Opened: 2012-11-09 Last modified: 2016-02-12
Priority: 4
View all other issues in [basic.ios.cons].
View all issues with Open status.
Discussion:
There is an ambiguity in how std::basic_ios::init method (27.5.5.2 [basic.ios.cons]) can be used in the derived class. The Standard only specify the state of the basic_ios object after the call completes. However, in basic_ios default constructor description (27.5.5.2 [basic.ios.cons]) there is this sentence:
Effects: Constructs an object of class basic_ios (27.5.3.7 [ios.base.cons]) leaving its member objects uninitialized. The object shall be initialized by calling basic_ios::init before its first use or before it is destroyed, whichever comes first; otherwise the behavior is undefined.
This restriction hints that basic_ios::init should be called exactly once before the object can be used or destroyed, because basic_ios::init may not know whether it was called before or not (i.e. whether its members are actually uninitialized or are initialized by the previous call to basic_ios::init). There is no such restriction in the basic_ios::init preconditions so it is not clear whether it is allowed to call basic_ios::init multiple times or not.
This problem has already affected publicly available implementations. For example, Microsoft Visual C++ STL introduces a memory leak if basic_ios::init is called multiple times, while GCC 4.7 and STLPort reinitialize the basic_ios object correctly without memory leak or any other undesired effects. There was a discussion of this issue on Boost developers mailing list, and there is a test case that reproduces the problem. The test case is actually a bug report for my Boost.Log library, which attempts to cache basic_ostream-derived objects internally to avoid expensive construction and destruction. My stream objects allowed resetting the stream buffer pointers the stream is attached to, without requiring to destroy and construct the stream. My personal view of the problem and proposed resolution follows. While apparently the intent of basic_ios::init is to provide a way to initialize basic_ios after default construction, I see no reason to forbid it from being called multiple times to reinitialize the stream. Furthermore, it is possible to implement a conforming basic_ios that does not have this restriction. The quoted above section of the Standard that describes the effects of the default constructor is misleading. The Standard does not mandate any data members of basic_ios or ios_base (27.5.3 [ios.base]), which it derives from. This means that the implementation is allowed to use non-POD data members with default constructors that initialize the members with particular default values. For example, in the case of Microsoft Visual C++ STL the leaked memory is an std::locale instance that is dynamically allocated during basic_ios::init, a raw pointer to which is stored within ios_base. It is possible to store e.g. an unique_ptr instead of a raw pointer as a member of ios_base, the smart pointer will default initialize the underlying raw pointer on default construction and automatically destroy the allocated object upon being reset or destroyed, which would eliminate the leak and allow basic_ios::init to be called multiple times. This leads to conclusion that the default constructor of basic_ios cannot leave "its member objects uninitialized" but instead performs default initialization of the member objects, which would mean the same thing in case of POD types. However, I feel that restricting ios_base and basic_ios members to non-POD types is not acceptable. Since multiple calls to basic_ios::init are not forbidden by the Standard, I propose to correct the basic_ios default constructor description so that it is allowed to destroy basic_ios object without calling basic_ios::init. This would imply that any raw members of basic_ios and ios_base should be initialized to values suitable for destruction (essentially, this means only initializing raw pointers to NULL). The new wording could look like this:Effects: Constructs an object of class basic_ios (27.5.3.7 [ios.base.cons]) initializing its member objects to unspecified state, only suitable for basic_ios destruction. The object shall be initialized by calling basic_ios::init before its first use; otherwise the behavior is undefined.
This would remove the hint that basic_ios::init must be called exactly once. Also, this would remove the requirement for basic_ios::init to be called at all before the destruction. This is also an important issue because the derived stream constructor may throw an exception before it manages to call basic_ios::init (for example, if the streambuf constructor throws), and in this case the basic_ios destructor has undefined behavior.
To my mind, the described modification is sufficient to resolve the issue. But to emphasize the possibility to call basic_ios::init multiple times, a remark or a footnote for basic_ios::init postconditions could be added to explicitly state the semantics of calling it multiple times. The note could read as follows:The function can be called multiple times during the object lifetime. Each subsequent call reinitializes the object to the described in postconditions initial state.
[2013-04-20, Bristol]
Alisdair: The current wording is unclear but the proposed resolution is wrong
Solution: Clarify that init must be called once and only once. Move then to review.Proposed resolution:
This wording is relative to N3485.
Edit 27.5.5.2 [basic.ios.cons] as indicated:
basic_ios();-2- Effects: Constructs an object of class basic_ios (27.5.3.7 [ios.base.cons])
leaving its member objects uninitializedinitializing its member objects to unspecified state, only suitable for basic_ios destruction. The object shall be initialized by calling basic_ios::init before its first useor before it is destroyed, whichever comes first; otherwise the behavior is undefined.void init(basic_streambuf<charT,traits>* sb);Postconditions: The postconditions of this function are indicated in Table 128.
-?- Remarks: The function can be called multiple times during the object lifetime. Each subsequent call reinitializes the object to the described in postconditions initial state.
Section: 23.2.4 [associative.reqmts], 23.2.5 [unord.req] Status: Open Submitter: Alisdair Meredith Opened: 2012-11-14 Last modified: 2015-10-23
Priority: 3
View other active issues in [associative.reqmts].
View all other issues in [associative.reqmts].
View all issues with Open status.
Discussion:
The requirements on the functors used to arrange elements in the various associative and unordered containers are given by a set of expressions in tables 102 — Associative container requirements, and 103 — Unordered associative container requirements. In keeping with Library convention these expressions make the minimal requirements necessary on their types. For example, we have the following 3 row extracts for the unordered containers:
Expression | Assertion/note pre-/post-condition |
X(n, hf, eq) X a(n, hf, eq) |
Requires: hasher and key_equal are CopyConstructible. |
X(n, hf) X a(n, hf) |
Requires: hasher is CopyConstructible and key_equal is DefaultConstructible. |
X(n) X a(n) |
Requires: hasher and key_equal are DefaultConstructible. |
However, the signature for each class template requires that the functors must effectively be CopyConstructible for each of these expressions:
template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > class unordered_map { ... // construct/destroy/copy explicit unordered_map(size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); ... }
The letter of the standard can be honored as long as implementors recognize their freedom to split this one signature into multiple overloads, so that the documented default arguments (requiring a CopyConstructible functor) are not actually passed as default arguments.
As we look into the requirements for the copy constructor and copy-assignment operator, the requirements are even more vague, as the explicit requirements on the functors are not called out, other than saying that the functors are copied.
Must the functors be CopyAssignable? Or is CopyConstructible sufficient in this case? Do we require that the functors be Swappable so that the copy-swap idiom can be deployed here? Note that a type that is both CopyConstructible and CopyAssignable is still not guaranteed to be Swappable as the user may delete the swap function for their type in their own namespace, which would be found via ADL.
Some clean-up of the requirements table looks necessary, to at least document the assignment behavior. In addition, we should have clear guidance on whether these functors should always be CopyConstructible, as suggested by the class template definitions, or if the requirement tables are correct and we should explicitly split up the constructors in the (unordered) associative containers to no longer use default (function) arguments to obtain their defaulted functors.
I recommend the simplest solution would be to always require that the functors for (unordered) associative containers be CopyConstructible, above the requirements tables themselves, so that the issue need not be addressed within the tables. I suggest that the assignment operators for these containers add the requirement that the functors be Swappable, rather than forwarding the corresponding Assignable requirement.
[2013-03-15 Issues Teleconference]
Moved to Open.
Alisdair to propose wording.
[2014-06-08, Daniel comments]
The area of this issue partially overlaps what LWG 2227 addresses.
[2015-10-20, Daniel comments]
The revised resolution of LWG 2227 should resolve this issue as well. It follows the recommendations of the submitter to require CopyConstructible requirements for the function objects owned by containers, but it does not impose any further fundamental requirements.
Proposed resolution:
See the resolution of LWG 2227.
Section: 28.11.4 [re.alg.replace] Status: New Submitter: Jeffrey Yasskin Opened: 2012-11-26 Last modified: 2016-02-12
Priority: 3
View all other issues in [re.alg.replace].
View all issues with New status.
Discussion:
template <class traits, class charT, class ST, class SA> basic_string<charT, ST, SA> regex_replace(const basic_string<charT, ST, SA>& s, const basic_regex<charT, traits>& e, const charT* fmt, regex_constants::match_flag_type flags = regex_constants::match_default);
and friends are documented as
Constructs an empty string result of type basic_string<charT, ST, SA> and calls regex_replace(back_inserter(result), s.begin(), s.end(), e, fmt, flags).
This appears to require the result to have a default-constructed allocator, which isn't even possible for all allocator types. I suspect the allocator should be copied from 's' instead. Possibly there should be an additional defaulted argument to override the allocator of the result.
Proposed resolution:
Section: 28.12.2.2 [re.tokiter.comp] Status: New Submitter: Pete Becker Opened: 2012-11-21 Last modified: 2016-02-12
Priority: 2
View all issues with New status.
Discussion:
Consider the following example:
std::string str0("x"); std::regex rg0("a"); std::regex_token_iterator it0(str0.begin(), str0.end(), rg0, -1); // points at "x" in str0 std::string str1("x"); std::regex rg1("b"); std::regex_token_iterator it1(str1.begin(), str1.end(), rg1, -1); // points at "x" in str1
28.12.2.2 [re.tokiter.comp] p1 says that it0.operator==(it1) returns true "if *this and right are both suffix iterators and suffix == right.suffix"; both conditions are satisfied in this example. It does not say that they must both be iterators into the same sequence, nor does it say (as general iterator requirements do) that they must both be in the domain of == in order for the comparison to be meaningful. It's a simple statement: they're equal if the strings they point at compare equal. Given this being a valid comparison, the obtained result of "true" looks odd.
The problem is that for iterator values prior to the suffix iterator, equality means the same regular expression and the same matched sequence (both uses of "same" refer to identity, not equality); for the suffix iterator, equality means that the matched sequences compare equal.Proposed resolution:
Section: 27.7.3 [output.streams] Status: New Submitter: Matt Austern Opened: 2012-12-07 Last modified: 2016-02-12
Priority: 3
View all issues with New status.
Discussion:
When I write
std::cout << nullptr << std::endl;
I get a compilation error, "ambiguous overload for 'operator<<' in 'std::cout << nullptr'". As far as I can tell, the compiler is right to issue that error. There are inserters for const void*, const char*, const signed char*, and const unsigned char*, and none for nullptr_t, so the expression really is ambiguous.
Proposed wording: The obvious library solution is to add a nullptr_t overload, which would be defined something liketemplate<class C, class T> basic_ostream<C, T>& operator<<(basic_ostream<C, T>& os, nullptr_t) { return os << (void*) nullptr; }
We might also consider addressing this at a core level: add a special-case language rule that addresses all cases where you write f(nullptr) and f is overloaded on multiple pointer types. (Perhaps a tiebreaker saying that void* is preferred in such cases.)
[2016-01-18, comments from Mike and Ville collected by Walter Brown]
Mike Miller: "Changing overload resolution sounds like something that should be considered by EWG before CWG […]"
Ville: "Agreed, such a change would be Evolutionary. Personally, I think it would also be wrong, because I don't see how void* is the right choice to prefer in the case of code that is currently ambiguous. Sure, it would solve this particular library issue, but it seemingly has wider repercussions. If LWG really wants to, EWG can certainly discuss this issue, but I would recommend solving it on the LWG side (which doesn't mean that the standard necessarily needs to change, I wouldn't call it far-fetched to NAD it)."Proposed resolution:
Section: 23.3.11.3 [vector.capacity] Status: Open Submitter: Juan Soulie Opened: 2012-12-17 Last modified: 2016-02-12
Priority: 2
View other active issues in [vector.capacity].
View all other issues in [vector.capacity].
View all issues with Open status.
Discussion:
After the additions by 2033, it appears clear that the intended effect includes a reallocation and thus the potential effect on iterators should be explicitly added to the text in order to not contradict 23.2.1 [container.requirements.general]/11, or at the very least, explicitly state that a reallocation may happen.
Taking consistency with "reserve" into consideration, I propose:that the current "Remarks" are made its "Effect" instead, inserting "Reallocation happens at this point if and only if the function effectively reduces the capacity." after the note on non-bindingness.
adding a "Remarks" paragraph, similar to that of reserve: "Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence."
BTW, while we are at it, I believe the effect on iterators should also be explicitly stated in the other instance a reallocation may happen: 23.3.11.5 [vector.modifiers]/1 — even if obvious, it only contradicts 23.2.1 [container.requirements.general]/11 implicitly.
I propose to also insert "Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence." at the appropriate location in its "Remarks".[2012-12-19: Jonathan Wakely comments]
The described problem also affects std::basic_string and std::deque.
[2013-03-15 Issues Teleconference]
Moved to Review.
[2013-04-18, Bristol]
Daniel extends the P/R.
Rationale:
The wording in 21.3.1.4 [string.capacity] combined with 21.3.1.1 [string.require] seems to say the necessary things. We cannot impose all requirements as we do for vector, because we want to allow the short-string-optimization.
[2014-02-15 post-Issaquah session]
STL: I think that shrink_to_fit should be a no-op when called twice.
STL: Do we ever define reallocation for deque? Nope, all mentions of "reallocation" are in vector. We define what it means in vector::reserve(), but not for deque.
STL: Oh duh, they define reallocate in the PR. But I think we can do better here.
STL: Optimally, deque shrinking just allocates a new map of pointers, and drops empty blocks, but preserves pointers/references to elements.
Alisdair: That's like unordered containers, invalidating only iterators.
Pablo: It doesn't make sense to reduce capacity() to size(), because deque doesn't have capacity!
STL: For vector, "effectively reduces the capacity" is unnecessary, the capacity there is observable.
STL: There is a strong reason to provide an optimal shrink to fit for deque, since only the library implementer can do this.
STL: The other thing I don't like the repeated definition of reallocation for vector, we define it once and use it in a bunch of places. At most we can lift it up to the vector synopsis.
STL: I'll write new wording.
[2014-10-01, STL adds discussion and provides new wording]
Compared to the previous proposed resolution:
I'm changing basic_string's wording because (1) we should guarantee that capacity won't increase, (2) we should mention that it's linear complexity, and (3) we can provide a better invalidation guarantee than 21.3.1.1 [string.require]/5. (As previously noted, we already have the strong exception guarantee.) This introduces the term "reallocation" into basic_string, but immediately explains what it means for iterator validity. As far as I can tell, the Small String Optimization doesn't complicate the wording here; it's a reason why an implementation might not honor the request, but if the capacity is reduced, we are definitely reallocating buffers and will invalidate everything (including when the destination is the small buffer).
Between N3485 and N3936, deque's wording was updated to avoid talking about capacity() which it doesn't have. Since the container's capacity is unobservable, I'm saying that invalidation is unconditional.
In vector's wording, I'm also guaranteeing that capacity won't increase, and that iterators/etc. remain valid if the capacity is unchanged.
My wording doesn't directly say that shrink_to_fit() should be a no-op when called twice in a row. (Indirectly, if the first call reduces capacity() to size(), the second call must preserve iterators/etc.) I considered rewording the complexity to say "linear if reallocation happens", but that's potentially problematic (what if we copy almost all N elements, then one throws and we have to unwind? There are no effects, so reallocation didn't happen, yet we took longer than constant time). Implementers can always do better than the stated complexity bounds.
I chose not to modify deque's requirements, so implementations remain free to reallocate the elements themselves. I didn't attempt to centralize vector's reallocation wording. That can be done editorially, if someone is sufficiently motivated.Previous resolution from Juan Soulie/Daniel [SUPERSEDED]:
This wording is relative to N3485.
Keep 21.3.1.4 [string.capacity] around p14 unchanged, because we don't speak about reallocations and we give the strong exception guarantee in 21.3.1.1 [string.require] (Invalidation specification also at that place):
void shrink_to_fit();-14- Remarks: shrink_to_fit is a non-binding request to reduce capacity() to size(). [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ].
Edit 23.3.8.3 [deque.capacity] around p7 as indicated:
void shrink_to_fit();-5- Requires: T shall be MoveInsertable into *this.
-?- Effects: shrink_to_fit is a non-binding request to reduce capacity() to size(). [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ] Reallocation happens at this point if and only if the function effectively reduces the capacity. If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects. -6- Complexity: Linear in the size of the sequence. -7- Remarks:shrink_to_fit is a non-binding request to reduce capacity() to size(). [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ] If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence.Edit 23.3.11.3 [vector.capacity] around p7 as indicated:
void shrink_to_fit();-7- Requires: T shall be MoveInsertable into *this.
-?- Effects: shrink_to_fit is a non-binding request to reduce capacity() to size(). [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ] Reallocation happens at this point if and only if the function effectively reduces the capacity. If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects. -8- Complexity: Linear in the size of the sequence. -9- Remarks:shrink_to_fit is a non-binding request to reduce capacity() to size(). [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note ] If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence.Edit 23.3.11.5 [vector.modifiers] p1 as indicated:
iterator insert(const_iterator position, const T& x); iterator insert(const_iterator position, T&& x); iterator insert(const_iterator position, size_type n, const T& x); template <class InputIterator> iterator insert(const_iterator position, InputIterator first, InputIterator last); iterator insert(const_iterator position, initializer_list<T>); template <class... Args> void emplace_back(Args&&... args); template <class... Args> iterator emplace(const_iterator position, Args&&... args); void push_back(const T& x); void push_back(T&& x);-1- Remarks: Causes reallocation if the new size is greater than the old capacity. Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. If no reallocation happens, all the iterators and references before the insertion point remain valid. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects. If an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.
[2015-02, Cologne]
GR: I'm concerned that shrink_to_fit may cause reallocation without changing the capacity. […] It's about correctness. The statement about invalidation is useless if I cannot detect whether reallocation has happened?
AM: It seems like the logic goes the other way round: It's the capacity change that causes reallocation, so if there's no capacity change, there's no reallocation. But that's not quite how I'd like to say it... maybe this, : "If capacity does not change, no reallocation occurs." GR: Where does it actually say that reserve() invalidates? AM: It should say that in the container requirements. VV: vector specifies in reserve that there's reallocation if and only if the capacity changes. GR: I can't find anything in the container requirements about reserve. DK: No, it's specified for every container separately. GR: It isn't specified for string. GR: I'm noticing that the issue touches on shrink_to_fit for a bunch of containers. Anyway, I think the reserve issue [re string] is in scope for this issue. This change is touching on a lot of members. AM: Landing this change will provide clarity for what we should do with basic_string. GR: We're already asking for changes; we should fix string as well. AM: If one of the changes is ready before the other, I'd like to land the finished part first, but if both are ready for Lenexa, I'm equally happy to fix them in one go. DK will reword this. Conclusion: Update wording, revisit in Lenexa.Proposed resolution:
This wording is relative to N3936.
Change 21.3.1.4 [string.capacity] p14 as depicted:
void shrink_to_fit();-14-
-?- Complexity: Linear in the size of the sequence. -?- Remarks: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. If no reallocation happens, they remain valid.RemarksEffects: shrink_to_fit is a non-binding request to reduce capacity() to size(). [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] It does not increase capacity(), but may reduce capacity() by causing reallocation.
Change 23.3.8.3 [deque.capacity] p5-p7 as depicted:
void shrink_to_fit();-5- Requires: T shall be MoveInsertable into *this.
-?- Effects: shrink_to_fit is a non-binding request to reduce memory use but does not change the size of the sequence. [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects. -6- Complexity: Linear in the size of the sequence. -7- Remarks:shrink_to_fit is a non-binding request to reduce memory use but does not change the size of the sequence. [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note]shrink_to_fit invalidates all the references, pointers, and iterators referring to the elements in the sequence.
Change 23.3.11.3 [vector.capacity] p7-p9 as depicted:
void shrink_to_fit();-7- Requires: T shall be MoveInsertable into *this.
-?- Effects: shrink_to_fit is a non-binding request to reduce capacity() to size(). [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] It does not increase capacity(), but may reduce capacity() by causing reallocation. If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects. -8- Complexity: Linear in the size of the sequence. -9- Remarks:shrink_to_fit is a non-binding request to reduce capacity() to size(). [Note: The request is non-binding to allow latitude for implementation-specific optimizations. — end note] If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. If no reallocation happens, they remain valid.
Change 23.3.11.5 [vector.modifiers] p1 as depicted:
-1- Remarks: Causes reallocation if the new size is greater than the old capacity. Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. If no reallocation happens, all the iterators and references before the insertion point remain valid. […]
Section: 23.2.4 [associative.reqmts] Status: Open Submitter: Juan Soulie Opened: 2012-12-19 Last modified: 2015-10-23
Priority: 3
View other active issues in [associative.reqmts].
View all other issues in [associative.reqmts].
View all issues with Open status.
Discussion:
Table 102 in 23.2.4 [associative.reqmts]/8 states on expression a.key_comp() that it "returns the comparison object out of which a was constructed". At the same time, 23.2.1 [container.requirements.general]/8 states (starting in the third line) that "...Any Compare, Pred, or Hash objects belonging to a and b shall be swappable and shall be exchanged by unqualified calls to non-member swap...". This is problematic for any compliant implementation, since once swapped the container cannot return the comparison object out of which it was constructed unless incurring in storing an otherwise needless object.
The simple solution is to correct that statement in Table 102, but I believe this is part of a larger problem of underspecified behavior: The new standard has made an effort in regards to allocators and now fully specifies what happens to stateful allocator objects. It has even specified what happens to stateful hasher and key_equal members of unordered containers (they propagate), but it says nothing about stateful comparison objects of (ordered) associative containers, except for the statement in 23.2.1 [container.requirements.general]/8 referred above and only related to swap. For example, it is unclear to me what is specified to happen on an assignment: should the comparison object be copied/moved along with the elements, or should the left-hand side object keep its own? Maybe this has been intentionally left unspecified with the purpose of compatibility with C++98, which I understand it specified that comparison objects were kept for the entire life of the container (like allocators) — an unfortunate choice. But anyway, the segment of 23.2.1 [container.requirements.general] quoted above seems to break any possible backwards compatibility with C++98 in this regard. Therefore, taking into consideration consistency with how this is dealed with for unordered associative containers, I propose that Table 102 is modified as follows:The row for expression a.key_comp() is changed so that its "assertion/note pre-/post-condition" reads "Returns a's comparison object."
A new row is added at the appropriate location (which I believe would be after "X(il)" row), with:
Table 102 — Associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity X(b)
X a(b)X Copy constructor. In addition to
the requirements of Table 96, copies
the comparison object.Linear in b.size() a = b X& Copy assignment operator. In addition to
the requirements of Table 96, copies the
comparison object.Linear in a.size() and b.size()
[2013-03-15 Issues Teleconference]
Moved to Review.
[2013-04-18, Bristol]
STL: can't believe we don't specify this already. this is totally necessary
Alisdair: how does it do this? copy construction? assignment? Also need it for move. STL: we already specify this for constructing from a comparator, not during copy construction though. Jonathan: don't like wording, should say "key_compare is CopyConstructible. Uses b.key_comp() as a comparison object." STL: we get it right for unordered! Jonathan: can't wordsmith this now, but I think implementations do the right thing. Alisdair: not sure what right thing is for moves. Also we say nothing about propagating allocators to functors.Moved to Open.
[2015-02 Cologne]
TK: There's no need for fine-grained propagate/not-propagate control. If you don't want to propagate the predicate, you can simply construct or insert from an iterator range.
VV: libstdc++ already implements the resolution of this issue. GR: There are a couple of other problems. We don't specify move constructor and move assignment for maps. Those are just general. TK: General container requirements already describe the semantics for {copy,move}-{construction,assignment}, so it doesn't seem that there's room for choice in std::map assignments. unordered_map is different, though. [Note: Check what general container requirements say about container equality.] DK will draft wording. The decision is to unambiguously make all {copy,move}-{construction,assignment} operations endow the LHS with the exact state of the RHS, including all predicates and hash function states. Conclusion: Update wording, revisit later.[2015-05-06 Lenexa: Waiting for updated wording]
Previous resolution [SUPERSEDED]:
This wording is relative to N3485.
Change Table 102 as indicated:
Table 102 — Associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity … X(il) Same as X(il.begin(), il.end()). same as X(il.begin(), il.end()). X(b)
X a(b)Copy constructor. In addition to
the requirements of Table 96, copies
the comparison object.Linear in b.size() a = b X& Copy assignment operator. In addition to
the requirements of Table 96, copies the
comparison object.Linear in a.size() and b.size() … a.key_comp() X::key_compare rReturnsthea's comparison object
out of which a was constructed.constant
[2015-10-19 Daniel comments and provides alternative wording]
The current standard is especially unclear in regard to what effects move operations of unordered/associative containers should have. We have one example that is standardized exactly in this way by looking at 23.6.5.2 [priqueue.cons.alloc] p7:
template <class Alloc> priority_queue(priority_queue&& q, const Alloc& a);-7- Effects: Initializes c with std::move(q.c) as the first argument and a as the second argument, and initializes comp with std::move(q.comp)
A similarly comparable example are the move-operations of std::unique_ptr in regard to the deleter (when this is no a reference), which also respect move-capabilities of that function object.
We have wording from C++98 for associative containers (but not for unordered containers!) that was never adjusted to C++11 move-semantics in 23.2.4 [associative.reqmts] p12:When an associative container is constructed by passing a comparison object the container shall not store a pointer or reference to the passed object, even if that object is passed by reference. When an associative container is copied, either through a copy constructor or an assignment operator, the target container shall then use the comparison object from the container being copied, as if that comparison object had been passed to the target container in its constructor.
The second sentence of this wording is problematic for several reasons:
It only talks about copy operations, not about move operations, except that the term "assignment" without leading "copy" is a bit ambigious (albeit it seems clear in the complete context).
It is not really clear how to interpret "as if that comparison object had been passed to the target container in its constructor" for an assignment operation. A possible but not conclusive interpretation could be that this is wording supporting a "copy-via-swap" idiom.
There does not exist similar wording for unordered containers, except that Table 102 provides entries for copy construction and copy assignment of the containers whose wording just talks of "copies" in either case.
Existing implementations differ already:
Visual Studio 2015 uses copy construction and copy assignment for the two copy operations but uses swap operations for the move operations.
GCC's libstdc++ performs copy construction and copy assignment for the two copy operations and for the two move operations, respectively
clang++'s libc++ performs copy/move construction and copy/move assignment for the corresponding four copy/move operations
The alternative wording provided below attempts to clarify that container copy/move operations perform the corresponding copy/move operations on the owned function objects.
In addition the wording also resolves LWG 2215: I believe that the current wording should require that container function objects should meet the CopyConstructible requirements. Adding this general requirement also fixes the underspecified requirements of the accessor functions key_comp() and value_comp(). I don't think that a general requirement for Swappable is needed, only the member swap function currently requires this. Nonetheless the wording below does support stateful functors that are also moveable or move-assignable, therefore the specified semantics in terms of move operations. I should add the following warning, though: If this proposed wording would be accepted, there is a little chance of code breakage, because the current wording can be read that in general there is no requirement that the container functors are CopyConstructible. The following code example is accepted by gcc + libstd++:#include <map> #include <utility> #include <iostream> struct Cmp { Cmp() = default; Cmp(const Cmp&) = delete; Cmp(Cmp&&) = delete; Cmp& operator=(const Cmp&) = delete; Cmp& operator=(Cmp&&) = delete; template<class T> bool operator()(const T& x, const T& y) const { return x < y; } }; typedef std::map<int, int, Cmp> MyMap; int main() { MyMap m; std::cout << (m.find(12) == m.end()) << std::endl; }
Proposed resolution:
This wording is relative to N4527.
Change 23.2.4 [associative.reqmts] p8 as indicated:
-8- In Table 101, X denotes an associative container class, a denotes a value of type X, b denotes a possibly const value of type X, rv denotes a non-const rvalue of type X, u denotes the name of a variable being declared, […]
Change Table 101 as indicated:
Table 101 — Associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity … X::key_compare Compare Requires: Compare is CopyConstructible.
defaults to less<key_type>compile time X(c)
X u(c);Requires: key_compare is CopyConstructible.Effects: Constructs an empty container.
Uses a copy of c as a comparison object.[…] … X(i,j,c)
X u(i,j,c);Requires: key_compare is CopyConstructible.value_type is EmplaceConstructible into X from *i.
Effects: Constructs an empty container and inserts elements
from the range [i, j) into it; uses c as a comparison object.[…] … X(il) Same as X(il.begin(), il.end()). same as X(il.begin(), il.end()). X(b)
X a(b)(In addition to the requirements of Table 95)
Effects: Copy constructs the comparison object of a from
the comparison object of b.Linear in b.size() X(rv)
X a(rv)(In addition to the requirements of Table 95 and Table 98)
Effects: Move constructs the comparison object of a from
the comparison object of rv.constant a = b X& (In addition to the requirements of Table 95 and Table 98)
Requires: key_compare is CopyAssignable.
Effects: Copy assigns the comparison object of b
to the comparison object of a.Linear in a.size() and b.size() a = rv X& (In addition to the requirements of Table 95 and Table 98)
Requires: key_compare is MoveAssignable.
Effects: Move assigns from the comparison object of rv
to the comparison object of a.Linear … a.key_comp() X::key_compare rReturnsthea's comparison object
out of which a was constructed.constant
Change 23.2.4 [associative.reqmts] p12 as indicated:
-12- When an associative container is constructed by passing a comparison object the container shall not store a pointer or reference to the passed object, even if that object is passed by reference.
When an associative container is copied, either through a copy constructor or an assignment operator, the target container shall then use the comparison object from the container being copied, as if that comparison object had been passed to the target container in its constructor.
Change 23.2.5 [unord.req] p11 as indicated:
-11- In Table 102: X denotes an unordered associative container class, a denotes a value of type X, b denotes a possibly const value of type X, rv denotes a non-const rvalue of type X, […]
Change Table 102 as indicated:
Table 102 — Unordered associative container requirements (in addition to container) Expression Return type Assertion/note pre-/post-condition Complexity … X::hasher Hash Requires: Hash is CopyConstructible.
Hash shall be a unary function object type
such that the expression hf(k) has type std::size_t.compile time X::key_equal Pred Requires: Pred is CopyConstructible.
Pred shall be a binary predicate that takes
two arguments of type Key.
Pred is an equivalence relation.compile time … X(n, hf, eq)
X a(n, hf, eq)X Requires: hasher and key_equal are CopyConstructible.Effects: […][…] X(n, hf)
X a(n, hf)X Requires: hasher is CopyConstructible andkey_equal is DefaultConstructible.
Effects: […][…] … X(i, j, n, hf, eq)
X a(i, j, n, hf, eq)X Requires: hasher and key_equal are CopyConstructible.value_type is EmplaceConstructible into X from *i.
Effects: […][…] X(i, j, n, hf)
X a(i, j, n, hf)X Requires: hasher is CopyConstructible andkey_equal is DefaultConstructible.
value_type is EmplaceConstructible into X from *i.
Effects: […][…] … X(b)
X a(b)X Copy constructor. In addition(In addition to the requirements of Table 95)
to the requirements of Table 95,
copies the hash function,
predicate, and maximum load
factor.
Effects: Copy constructs the hash function, predicate, and maximum load factor
of a from the corresponding objects of b.Average case linear in
b.size(),
worst case quadratic.X(rv)
X a(rv)X (In addition to the requirements of Table 95 and Table 98)
Effects: Move constructs the hash function, predicate, and maximum load factor
of a from the corresponding objects of rv.constant a = b X& Copy assignment operator. In(In addition to the requirements of Table 95 and Table 98)
addition to the requirements of
Table 95, copies the hash
function, predicate, and
maximum load factor.
Requires: hasher and key_equal are CopyAssignable.
Effects: Copy assigns the hash function, predicate, and maximum load factor
of b to the corresponding objects of a.Average case linear in
b.size(),
worst case quadratic.a = rv X& (In addition to the requirements of Table 95 and Table 98)
Requires: hasher and key_equal are MoveAssignable.
Effects: Move assigns the hash function, predicate, and maximum load factor
from rv to the corresponding objects of a.Linear …
Section: 21.5 [c.strings] Status: New Submitter: Jason Merrill Opened: 2013-01-29 Last modified: 2016-02-12
Priority: 4
View other active issues in [c.strings].
View all other issues in [c.strings].
View all issues with New status.
Discussion:
Apparently C1X changes __STDC_UTF_16__ and __STDC_UTF_32__ from macros defined in uchar.h (and reflected in C++ by Table 79) to be predefined by the compiler. Do we want to do the same?
Proposed resolution:
Section: 21.5 [c.strings] Status: Open Submitter: Johannes Schaub Opened: 2013-02-02 Last modified: 2016-02-12
Priority: 3
View other active issues in [c.strings].
View all other issues in [c.strings].
View all issues with Open status.
Discussion:
The non-explicit nature of the iterator-pair constructor of containers, such a
template <class InputIterator> vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
can be selected in unexpected situations, leading to a hard runtime error, as demonstrated by the following example:
#include <vector> void f(std::vector<char> v){ /* ... */} int main() { f({"A", "B"}); }
The actually intended initializer-list constructor isn't feasible here, so the best match is the constructor template
template <class InputIterator> vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
This compiles, but will result in code running amok. The potential trap (that cannot be easily detected by the library implementation) could be reduced by making this constructor explicit. It would still have the effect to be selected here, but the code would be ill-formed, so the programmer gets a clear meassage here.
[2014-06 Rapperswil]
JW: can't fix this, don't want to touch this, Do The Right Thing clause has been a source of tricky issues. only really happens with string literals, that's the only way to create an array that isn't obviously an array
GR: want to see paper AM: is it only string literals, or also UDLs? STL: maybe, but we don't need to deal with that. This is only a problem in a very specific case Leave as Open.Proposed resolution:
Section: 25.4.1 [alg.copy], 20.10.10.4 [uninitialized.copy] Status: New Submitter: Sean Parent Opened: 2013-02-14 Last modified: 2016-02-12
Priority: 2
View other active issues in [alg.copy].
View all other issues in [alg.copy].
View all issues with New status.
Discussion:
copy_n() and uninitialized_copy_n() only return the output iterator, and not the input iterator. Likely the interface was simply copied from the original STL. Unfortunately the interface in the original STL contains a bug.
copy_n() and uninitialized_copy_n() must return the resulting input iterator as well as the output iterator (I would suggest returning a pair). Without this, there is no way to continue reading from an actual input iterator — and if it is really a forward iterator, it will cost n increments to get back to where you were.Proposed resolution:
Section: 27.7.2.3 [istream.unformatted] Status: New Submitter: Juan Soulie Opened: 2013-03-01 Last modified: 2016-02-12
Priority: 3
View all other issues in [istream.unformatted].
View all issues with New status.
Discussion:
In 27.7.2.3 [istream.unformatted] / 34, when describing putback, it says that "rdbuf->sputbackc()" is called. The problem are not the obvious typos in the expression, but the fact that it may lead to different interpretations, since nowhere is specified what the required argument to sputbackc is.
It can be guessed to be "rdbuf()->sputbackc(c)", but "rdbuf()->sputbackc(char_type())" or just anything would be as conforming (or non-confoming) as the first guess.Proposed resolution:
Section: 30.6.9.1 [futures.task.members] Status: Open Submitter: Jonathan Wakely Opened: 2013-03-05 Last modified: 2016-02-12
Priority: 3
View all other issues in [futures.task.members].
View all issues with Open status.
Discussion:
The effects of packaged_task::reset() result in memory allocation, but don't allow a user to provide an allocator.
packaged_task::reset() needs to be overloaded like so:template<class Alloc> void reset(const Alloc&);
Alternatively, the effects of reset() need to require the same allocator is used as at construction, which would require the constructor to store the allocator for later use.
I like to remark that GCC at the moment uses the second option, i.e. the allocator passed to the constructor (if any) is used to create the new shared state, because this didn't require any change to the interface.[2015-02 Cologne]
Handed over to SG1.
[2015-05 Lenexa, SG1 response]
No strong opinions in SG1, and this is really an LWG issue. Back to you.
Proposed resolution:
Section: 18.3.2 [limits] Status: New Submitter: Pete Becker Opened: 2013-03-08 Last modified: 2016-02-12
Priority: 4
View all other issues in [limits].
View all issues with New status.
Discussion:
This member should probably be named "is_ieee754". Or at least the standard should explain that IEC-559 no longer exists, and that it's been superseded by IEEE-754.
Proposed resolution:
Section: 23.3.11.5 [vector.modifiers] Status: New Submitter: Howard Hinnant Opened: 2013-04-29 Last modified: 2016-05-22
Priority: 3
View other active issues in [vector.modifiers].
View all other issues in [vector.modifiers].
View all issues with New status.
Discussion:
23.3.11.5 [vector.modifiers]/p3 says:
iterator erase(const_iterator position); iterator erase(const_iterator first, const_iterator last);Effects: Invalidates iterators and references at or after the point of the erase.
Consider this example:
#include <vector>
#include <cassert>
int main()
{
typedef std::vector<int> C;
C c = {1, 2, 3, 4};
C::iterator i = c.begin() + 1;
C::iterator j = c.end() - 1;
assert(*i == 2);
assert(*j == 4);
c.erase(c.begin());
assert(*i == 3); // Why is this not perfectly fine?!
}
Why has the iterator i been invalidated? It still refers to a perfectly reasonable, fully constructed object. If vector::iterator were to be implemented as a pointer (which is legal), it is not possible for that last line to do anything but run fine.
The iterator j on the other hand now points at end, and any iterators that may now point beyond end(), into uninitialized memory, are clearly invalid. But why do we say that an iterator that must point to a valid object is invalid? This looks to me like we simply got sloppy in our specification.[2016-05 Issues Telecom]
This is related to 2698
Proposed resolution:
Section: 17.6.3.5 [allocator.requirements] Status: Open Submitter: Jonathan Wakely Opened: 2013-05-14 Last modified: 2016-02-12
Priority: 3
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with Open status.
Discussion:
For an allocator A<T> which defines A<T>::pointer to a class type, i.e. not T*, I see no requirement that A<T>::pointer is convertible to A<U>::pointer, even if T* is convertible to U*. Such conversions are needed in containers to convert from e.g. ListNodeBase* to ListNode<T>*.
The obvious way to do such conversions appears to be pointer_traits::pointer_to(), but that's ill-formed if the static member function A<T>::pointer::pointer_to() doesn't exist and the allocator requirements don't mention that function, so you need to cast A<T>::pointer to A<T>::void_pointer then cast that to A<U>::pointer.
Is converting via void_pointer really intended, or are we missing a requirement that pointer_traits<A<T>::pointer>::pointer_to() be well-formed?
Proposed resolution:
Add to the Allocator requirements table the following requirement:
The expression pointer_traits<XX::pointer>::pointer_to(r) is well-defined.
[2013-09 Chicago]
Pablo to come back with proposed wording
[2015-07 Telecom]
Marshall to ping Pablo for proposed wording and disable current wording.
Previous resolution [SUPERSEDED]:
Edit Table 28 as indicated:
Table 28 — Allocator requirements (continued) Expression Return type Assertion/note pre-/post-condition Default … static_cast<X::const_pointer>(z) X::const_pointer static_cast<X::const_pointer>(z) == q pointer_traits<X::pointer>::pointer_to(r) X::pointer …
Proposed resolution:
Section: 23.2 [container.requirements] Status: New Submitter: Jonathan Wakely Opened: 2013-05-14 Last modified: 2016-02-12
Priority: 2
View other active issues in [container.requirements].
View all other issues in [container.requirements].
View all issues with New status.
Discussion:
Is a container C only supposed to refer to allocated memory (blocks of contiguous storage, nodes, etc.) through objects of type C::pointer rather than C::value_type*?
I don't see anything explicitly requiring this, so a container could immediately convert the result of get_allocator().allocate(1) to a built-in pointer of type value_type* and only deal with the built-in pointer until it needs to deallocate it again, but that removes most of the benefit of allowing allocators to use custom pointer types.
[2014-06-12, Jonathan comments]
This issue is basically the same issue as LWG 1521, which agrees it's an issue, to be dealt with in the future, so I request that 2261 not be closed as a dup unless we reopen 1521.
Proposed resolution:
Section: 20.11.1.2 [unique.ptr.single] Status: Open Submitter: Rob Desbois Opened: 2013-05-15 Last modified: 2016-02-12
Priority: 3
View all other issues in [unique.ptr.single].
View all issues with Open status.
Discussion:
N3337 20.11.1.2.5 [unique.ptr.single.modifiers] contains 2 non-normative notes stating:
[para 4]: "The order of these operations is significant because the call to get_deleter() may destroy *this."
[para 5]: "The postcondition does not hold if the call to get_deleter() destroys *this since this->get() is no longer a valid expression."
It seems this wording was created to resolve 998 due to the possibility that a unique_ptr may be destroyed through deletion of its stored pointer where that directly or indirectly refers to the same unique_ptr. If unique_ptr is required to support circular references then it seems this must be normative text: an implementation is currently allowed to operate on *this after the assignment and deletion specified in para 4, since this is only 'disallowed' by the non-normative note.
I propose the following draft rewording:
[para 4]: Effects: assigns p to the stored pointer, and then if the old value of the stored pointer, old_p, was not
equal to nullptr, calls get_deleter()(old_p). No operation shall be performed after the call to
get_deleter()(old_p) that requires *this to be valid, because the deletion may destroy *this if it is
referred to directly or indirectly by the stored pointer. [Note: The order of these operations is significant
because the call to get_deleter() may destroy *this. — end note]
I expect it will also be necessary to amend the requirements for a deleter, so in addition:
20.11.1.2 [unique.ptr.single] [para 1]: The default type for the template parameter D is default_delete. A client-supplied template argument D shall be a function object type (20.10), lvalue-reference to function, or lvalue-reference to function object type for which, given a value d of type D and a value ptr of type unique_ptr<T, D>::pointer, the expression d(ptr) is valid and has the effect of disposing of the pointer as appropriate for that deleter. Where D is not an lvalue reference type, d(ptr) shall be valid if ptr refers directly or indirectly to the invoking unique_ptr object.
[2013-10-05, Stephan T. Lavavej comments and provides alternative wording]
In Chicago, we determined that the original proposed change to 20.11.1.2 [unique.ptr.single]/1 was insufficient, because d might be a reference to a deleter functor that's destroyed during self-destruction.
We believed that 20.11.1.2.5 [unique.ptr.single.modifiers]/4 was already sufficiently clear. The Standard occasionally prevents implementations of X from doing various things, through the principle of "nothing allows X to fail in that situation". For example, v.push_back(v[0]) is required to work for non-empty vectors because nothing allows that to fail. In this case, the intent to allow self-destruction is already clear. Additionally, we did not believe that 20.11.1.2.5 [unique.ptr.single.modifiers]/5 had to be changed. The current note is slightly squirrely but it does not lead to confusion for implementers or users.Previous resolution from Rob Desbois:
Edit 20.11.1.2 [unique.ptr.single] p1 as indicated:
The default type for the template parameter D is default_delete. A client-supplied template argument D shall be a function object type (20.10), lvalue-reference to function, or lvalue-reference to function object type for which, given a value d of type D and a value ptr of type unique_ptr<T, D>::pointer, the expression d(ptr) is valid and has the effect of disposing of the pointer as appropriate for that deleter. Where D is not an lvalue reference type, d(ptr) shall be valid if ptr refers directly or indirectly to the invoking unique_ptr object.
Edit 20.11.1.2.5 [unique.ptr.single.modifiers] p4+5 as indicated:
void reset(pointer p = pointer()) noexcept;-3- Requires: The expression get_deleter()(get()) shall be well formed, shall have well-defined behavior, and shall not throw exceptions.
-4- Effects: assigns p to the stored pointer, and then if the old value of the stored pointer, old_p, was not equal to nullptr, calls get_deleter()(old_p). No operation shall be performed after the call to get_deleter()(old_p) that requires *this to be valid, because the deletion may destroy *this if it is referred to directly or indirectly by the stored pointer.[Note: The order of these operations is significant because the call to get_deleter() may destroy *this. — end note]-5- Postconditions: If the call get_deleter()(old_p) destroyed *this, none. Otherwise, get() == p.[Note: The postcondition does not hold if the call to get_deleter() destroys *this since this->get() is no longer a valid expression. — end note]
Previous resolution [SUPERSEDED]:
This wording is relative to N3691.
Edit 20.11.1.2 [unique.ptr.single] p1 as indicated:
The default type for the template parameter D is default_delete. A client-supplied template argument D shall be a function object type (20.10), lvalue-reference to function, or lvalue-reference to function object type for which, given a value d of type D and a value ptr of type unique_ptr<T, D>::pointer, the expression d(ptr) is valid and has the effect of disposing of the pointer as appropriate for that deleter. d(ptr) shall be valid even if it triggers the destruction of d or (if D is an lvalue reference to function object type) the function object that d refers to.
[2015-05, Lenexa]
After some discussion in Lenexa there was some wavering on if the added sentence is necessary. Here is example code that demonstrates why the extra sentence is necessary. In this example the call to d(ptr) is valid, however the deleter references *this after destructing its element:
#include <cassert> #include <memory> #include <iostream> class Deleter { int state_ = 0; enum { destructed = -4, self_move_assigned = -3, move_assigned_from = -2, move_constructed_from = -1 }; public: ~Deleter() {state_ = destructed;} Deleter() = default; Deleter(Deleter const&) = default; Deleter& operator=(Deleter const&) = default; Deleter(Deleter&& a) noexcept : state_(a.state_) {a.state_ = move_constructed_from;} Deleter& operator=(Deleter&& a) noexcept { if (this == &a) state_ = self_move_assigned; else { state_ = a.state_; a.state_ = move_assigned_from; } return *this; } Deleter(int state) : state_(state) { assert(state >= 0); } template <class T> void operator()(T* t) const { std::cout << "Deleter beginning operator()(T*)\n"; std::cout << "The deleter = " << *this << '\n'; std::cout << "Deleter about to destruct the X.\n"; delete t; std::cout << "Deleter has destructed the X.\n"; std::cout << "The deleter = " << *this << '\n'; std::cout << "Deleter ending operator()(T*)\n"; } friend std::ostream& operator<<(std::ostream& os, const Deleter& a) { switch (a.state_) { case destructed: os << "**destructed**"; break; case self_move_assigned: os << "self_move_assigned"; break; case move_assigned_from: os << "move_assigned_from"; break; case move_constructed_from: os << "move_constructed_from"; break; default: os << a.state_; break; } return os; } }; struct X { Deleter deleter_{1}; }; int main() { auto xp = new X; { std::unique_ptr<X, Deleter&> p(xp, xp->deleter_); std::cout << "unique_ptr is constructed.\n"; std::cout << "The deleter = " << p.get_deleter() << '\n'; std::cout << "Destructing unique_ptr...\n"; } std::cout << "unique_ptr is destructed.\n"; }
Which outputs:
unique_ptr is constructed. The deleter = 1 Destructing unique_ptr... Deleter beginning operator()(T*) The deleter = 1 Deleter about to destruct the X. Deleter has destructed the X. The deleter = **destructed** Deleter ending operator()(T*) unique_ptr is destructed.
The line "The deleter = **destructed**" represents the deleter referencing itself after it has been destructed by the d(ptr) expression, but prior to that call returning.
Suggested alternative to the current proposed wording:The expression d(ptr) shall not refer to the object d after it executes ptr->~T().
[2015-07, Telecom]
Geoffrey: Deleter may or may not execute ~T().
Alisdair: After the destructor after the element has run. Say it in words instead of code.
Howard will provide updated wording. Perhaps need both normative and non-normative wording.
[2015-08-03, Howard updates P/R per telecon discussion.]
Proposed resolution:
This wording is relative to N4431.
Edit 20.11.1.2 [unique.ptr.single] p1 as indicated:
The default type for the template parameter D is default_delete. A client-supplied template argument D shall be a function object type (20.9), lvalue-reference to function, or lvalue-reference to function object type for which, given a value d of type D and a value ptr of type unique_ptr<T, D>::pointer, the expression d(ptr) is valid and has the effect of disposing of the pointer as appropriate for that deleter. The expression d(ptr), if it destructs the object referred to by ptr, shall not refer to the object d after it destructs *ptr. [Note: The object being destructed may control the lifetime of d. — end note]
Section: 29.3 [atomics.order] Status: Open Submitter: Brian Demsky Opened: 2013-06-17 Last modified: 2016-02-12
Priority: 4
View other active issues in [atomics.order].
View all other issues in [atomics.order].
View all issues with Open status.
Discussion:
I believe that the following variation on IRIW should admit executions in which c1 = d1 = 5 and c2 = d2 = 0. If this is allowed, then what is sequence of program evaluations for 29.3 [atomics.order] p9 that justifies the store to z? It seems that 29.3 [atomics.order] p9 should not allow this execution because one of the stores to x or y has to appear earlier in the sequence, each of the fetch_adds reads the previous load in the thread (and thus must appear later in the sequence), and 29.3 [atomics.order] p9 states that each load must read from the last prior assignment in the sequence.
atomic_int x; atomic_int y; atomic_int z; int c1, c2, d1, d2; static void a(void* obj) { atomic_store_explicit(&x, 5, memory_order_relaxed); } static void b(void* obj) { atomic_store_explicit(&y, 5, memory_order_relaxed); } static void c(void* obj) { c1 = atomic_load_explicit(&x, memory_order_relaxed); // this could also be an atomic load if the address depends on c1: c2 = atomic_fetch_add_explicit(&y, c1, memory_order_relaxed); } static void d(void* obj) { d1 = atomic_load_explicit(&y, memory_order_relaxed); d2 = atomic_fetch_add_explicit(&x, d1, memory_order_relaxed); } int user_main(int argc, char** argv) { thrd_t t1, t2, t3, t4; atomic_init(&x, 0); atomic_init(&y, 0); printf("Main thread: creating 4 threads\n"); thrd_create(&t1, (thrd_start_t)&a, NULL); thrd_create(&t2, (thrd_start_t)&b, NULL); thrd_create(&t3, (thrd_start_t)&c, NULL); thrd_create(&t4, (thrd_start_t)&d, NULL); thrd_join(t1); thrd_join(t2); thrd_join(t3); thrd_join(t4); printf("c1=%d c2=%d\n",c1,c2); printf("d1=%d d2=%d\n",d1,d2); // Can this store write 1000 (i.e., c1=d1=5, c2=d2=0)? atomic_store(&z, (c1+d1)*100+c2+d2); printf("Main thread is finished\n"); return 0; }
It seems that the easiest fix is to allow a load in 29.3 [atomics.order] p9 to read from any prior store in the evaluation order.
That said, I would personally advocate the following: It seems to me that C/C++ atomics are in a bit of different situation than Java because:People are expected to use relaxed C++ atomics in potentially racy situations, so it isn't clear that semantics as complicated as the JMM's causality would be sane.
People who use C/C++ atomics are likely to be experts and use them in a very controlled fashion. I would be really surprised if compilers would find any real wins by optimizing the use of atomics.
Why not do something like:
There is satisfaction DAG of all program evaluations. Each evaluation observes the values of variables as computed by some prior assignment in the DAG. There is an edge x->y between two evaluations x and y if:the evaluation y observes a value computed by the evaluation x or
the evaluation y is an atomic store, the evaluation x is an atomic load, and there is a condition branch c that may depend (intrathread dependence) on x and x-sb->c and c-sb->y.
This seems to allow reordering of relaxed atomics that processors do without extra fence instructions, allows most reorderings by the compiler, and gets rid of satisfaction cycles.
[2015-02 Cologne]
Handed over to SG1.
[2015-05 Lenexa, SG1 response]
This was partially addressed (weasel-worded) in C++14 (See N3786). The remainder is an open research problem. N3710 outlines a "solution" that doesn't have a consensus behind it because it costs performance. We have no better solution at the moment.
Proposed resolution:
Section: 25.5.1.4 [partial.sort.copy] Status: New Submitter: Matt Austern Opened: 2013-06-26 Last modified: 2016-02-12
Priority: 3
View all issues with New status.
Discussion:
The signature of this function is:
template<class InputIterator, class RandomAccessIterator> RandomAccessIterator partial_sort_copy(InputIterator first, InputIterator last, RandomAccessIterator result_first, RandomAccessIterator result_last);
(and the usual overload for an explicitly provided comparison function). The standard says nothing about requirements in the case where the input type (iterator_traits<InputIterator>::value_type) and the output type (iterator_traits<RandomAccessIterator>::value_type) are different.
Presumably the input type must be convertible to the output type. What's less clear is what the requirements are on the comparison operator. Does the algorithm only perform comparisons on two values of the output type, or does it also perform comparisons on values of the input type, or might it even perform heterogeneous comparisons?Proposed resolution:
Section: 23.2.1 [container.requirements.general] Status: New Submitter: Matt Austern Opened: 2013-06-26 Last modified: 2016-02-12
Priority: 4
View other active issues in [container.requirements.general].
View all other issues in [container.requirements.general].
View all issues with New status.
Discussion:
Consider the following code snippet:
#include <vector> #include <algorithm> int main() { std::vector<int> v1(100, 3); std::vector<int> v2(100); copy(v1.begin(), v1.end(), v2.begin()); }
It compiles without error on my desktop. Is it required to? I can't find evidence from the standard that it is. In my test std::copy was found by argument-dependent lookup because the implementation I used made std::vector<int>::iterator a user-defined type defined in namespace std. But the standard only requires std::vector<int>::iterator to be an implementation specified random access iterator type. I can't find anything requiring it to be a user-defined type at all (and in fact there are reasonable implementation where it isn't), let alone a user defined type defined in a specific namespace.
Since the defining namespace of container iterators is visible to users, should the standard say anything about what that namespace is?
Proposed resolution:
Section: 27.8.2.4 [stringbuf.virtuals] Status: New Submitter: Sergey Zubkov Opened: 2013-08-29 Last modified: 2016-02-12
Priority: 4
View all other issues in [stringbuf.virtuals].
View all issues with New status.
Discussion:
In 27.8.2.4 [stringbuf.virtuals]/1, basic_stringbuf::underflow() is specified to unconditionally return traits::eof() when a read position is not available.
The semantics of basic_stringbuf require, and existing libraries implement it so that this function makes a read position available if possible to do so, e.g. if some characters were inserted into the stream since the last call to overflow(), resulting in pptr() > egptr(). Compare to the conceptually similar D.5.1.3 [depr.strstreambuf.virtuals]/15.Proposed resolution:
This wording is relative to N3691.
Change 27.8.2.4 [stringbuf.virtuals] as indicated:
int_type underflow();-1- Returns: If the input sequence has a read position available or the function makes a read position available (as described below), returns traits::to_int_type(*gptr()). Otherwise, returns traits::eof(). Any character in the underlying buffer which has been initialized is considered to be part of the input sequence.
-?- The function can make a read position available only if (mode & ios_base::in) != 0 and if the write next pointer pptr() is not null and is greater than the current read end pointer egptr(). To make a read position available, the function alters the read end pointer egptr() to equal pptr().
Section: 20.4.2 [pairs.pair], 20.5.2.1 [tuple.cnstr], 20.17.5 [time.duration] Status: Open Submitter: Daniel Krügler Opened: 2013-09-09 Last modified: 2016-02-12
Priority: 3
View other active issues in [pairs.pair].
View all other issues in [pairs.pair].
View all issues with Open status.
Discussion:
During the acceptance of N3471 and some similar constexpr papers, specific wording was added to pair, tuple, and other templates that were intended to impose implementation constraints that ensure that the observable constexpr "character" of a defaulted function template is solely determined by the required expressions of the user-provided types when instantiated, for example:
The defaulted move and copy constructor, respectively, of pair shall be a constexpr function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a constexpr function.
This wording doesn't require enough, especially since the core language via CWG 1358 does now support constexpr function template instantiations, even if such function cannot appear in a constant expression (as specified in 5.20 [expr.const]) or as a constant initializer of that object (as specified in [basic.start.init]). The wording should be improved and should require valid uses in constant expressions and as constant initializers instead.
[Lenexa 2015-05-05]
STL : notice order of move/copy and copy/move with "respectively".
General word-smithing; ask for updated wording
Are we happy with this with changes we are suggesting?
unanimous
Proposed resolution:
This wording is relative to N3691.
Change 20.4.2 [pairs.pair] p2 as indicated:
-2-
The defaulted move and copy constructor, respectively, of pair shall be a constexpr function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a constexpr functionAn invocation of the move or copy constructor of pair shall be a constant expression (5.20 [expr.const]) if all required element-wise initializations would be constant expressions. An invocation of the move or copy constructor of pair shall be a constant initializer for that pair object ( [basic.start.init]) if all required element-wise initializations would be constant initializers for the respective subobjects.
Change 20.5.2.1 [tuple.cnstr] p2 as indicated:
-2-
The defaulted move and copy constructor, respectively, of tuple shall be a constexpr function if and only if all required element-wise initializations for copy and move, respectively, would satisfy the requirements for a constexpr function. The defaulted move and copy constructor of tuple<> shall be constexpr functionsAn invocation of the move or copy constructor of tuple shall be a constant expression (5.20 [expr.const]) if all required element-wise initializations would be constant expressions. An invocation of the move or copy constructor of tuple shall be a constant initializer for that tuple object ( [basic.start.init]) if all required element-wise initializations would be constant initializers for the respective subobjects. An invocation of the move or copy constructor of tuple<> shall be a constant expression, or a constant initializer for that tuple<> object, respectively, if the function argument would be constant expression.
Change 20.17.5 [time.duration] p7 as indicated:
-7- Remarks:
The defaulted copy constructor of duration shall be a constexpr function if and only if the required initialization of the member rep_ for copy and move, respectively, would satisfy the requirements for a constexpr function.An invocation of the copy constructor of duration shall be a constant expression (5.20 [expr.const]) if the required initialization of the member rep_ would be a constant expression. An invocation of the copy constructor of duration shall be a constant initializer for that duration object ( [basic.start.init]) if the required initialization of the member rep_ would be constant initializers for this subobject.
Section: 20.15 [meta] Status: Open Submitter: Daniel Krügler Opened: 2013-09-02 Last modified: 2016-02-12
Priority: 3
View other active issues in [meta].
View all other issues in [meta].
View all issues with Open status.
Discussion:
The current library specification uses at several places wording that is intended to refer to core language template deduction failure at the top-level of expressions (aka "SFINAE"), for example:
The expression declval<T>() = declval<U>() is well-formed when treated as an unevaluated operand (Clause 5). Access checking is performed as if in a context unrelated to T and U. Only the validity of the immediate context of the assignment expression is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]
Similar wording can be found in the specification of result_of, is_constructible, and is_convertible, being added to resolve an NB comment by LWG 1390 and 1391 through N3142.
This wording is necessary to limit speculative compilations needed to implement these traits, but it is also lengthy and repetitive.[2014-05-19, Daniel suggests a descriptive term]
constrictedly well-formed expression:
An expression e depending on a set of types A1, ..., An which is well-formed when treated as an unevaluated operand (Clause 5). Access checking is performed as if in a context unrelated to A1, ..., An. Only the validity of the immediate context of e is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note][2014-05-20, Richard and Jonathan suggest better terms]
Richard suggested "locally well-formed"
Jonathan suggested "contextually well-formed" and then "The expression ... is valid in a contrived argument deduction context"[2014-06-07, Daniel comments and improves wording]
The 2014-05-19 suggestion did only apply to expressions, but there are two important examples that are not expressions, but instead are involving an object definition (std::is_constructible) and a function definition (std::is_convertible), respectively, instead. Therefore I suggest to rephrase the usage of "expression" into "program construct" in the definition of Jonathan's suggestion of "valid in a contrived argument deduction context".
I would like to point out that given the new definition of "valid in a contrived argument deduction context", there are several other places of the Library specification that could take advantage of this wording to improve the existing specification, such as 20.14.12.2 [func.wrap.func] p2, most functions in 20.10.8.2 [allocator.traits.members], and the **Insertable, EmplaceConstructible, and Erasable definitions in 23.2.1 [container.requirements.general], but given that these are not fully described in terms of the aforementioned wording yet, I would recommend to fix them by a separate issue once the committee has agreed on following the suggestion presented by this issue.[2015-05-05 Lenexa: Move to Open]
...
MC: I think we like the direction but it isn't quite right: it needs some work
JW: I'm prepared to volunteer to move that further, hopefully with the help of Daniel
Roger Orr: should this be Core wording because it doesn't really have anything to do with libraries - the term could then just be used here
AM: Core has nothing to deal with that, though
HT: it seems there is nothing to imply that allows dropping out with an error - maybe that's a separate issue
MC: I'm not getting what you are getting at: could you write an issue? - any objection to move to Open?
...
Proposed resolution:
This wording is relative to N3936.
Add the following new definition to 17.3 [definitions] as indicated:
valid in a contrived argument deduction context [defns.valid.contr.context]
A program construct c depending on a set of types A1, ..., An, and treated as an unevaluated operand (Clause 5) when c is an expression, which is well-formed. Access checking is performed as if in a context unrelated to A1, ..., An. Only the validity of the immediate context (14.8.2 [temp.deduct]) of c is considered. [Note: The compilation of c can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note].Change Table 49 ("Type property predicates") as indicated:
Table 49 — Type property predicates Template Condition Preconditions … template <class T, class U>
struct is_assignable;The expression declval<T>() =
declval<U>() is valid in a
contrived argument deduction context
([defns.valid.contr.context]) for types
T and U.well-formed when treated
as an unevaluated operand
(Clause 5). Access
checking is performed as if
in a context unrelated to T
and U. Only the validity of
the immediate context of
the assignment expression
is considered. [Note: The
compilation of the
expression can result in
side effects such as the
instantiation of class
template specializations
and function template
specializations, the
generation of
implicitly-defined
functions, and so on. Such
side effects are not in the
"immediate context" and
can result in the program
being ill-formed. — end
note][…] …
Change 20.15.4.3 [meta.unary.prop] p7 as indicated:
-7- Given the following function prototype:
template <class T> add_rvalue_reference_t<T> create() noexcept;the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition
would be well-formedfor some invented variable t would be valid in a contrived argument deduction context ([defns.valid.contr.context]) for types T and Args...:T t(create<Args>()...);[Note: These tokens are never interpreted as a function declaration. — end note]
Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered. [Note: The evaluation of the initialization can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]
Change Table 57 ("Other transformations") as indicated:
Table 57 — Other transformations Template Condition Comments … template <class Fn, class... ArgTypes>
struct result_of<Fn(ArgTypes...)>;[…] If the expression
INVOKE(declval<Fn>(),
declval<ArgTypes>()...) is
valid in a contrived argument deduction
context ([defns.valid.contr.context]) for types
Fn and ArgTypes...well, the
formed when treated as an
unevaluated operand (Clause 5)
member typedef type shall name the
type
decltype(INVOKE(declval<Fn>(),
declval<ArgTypes>()...));
otherwise, there shall be no member
type.Access checking is performed as
if in a context unrelated to Fn and
ArgTypes. Only the validity of the
immediate context of the expression is
considered. [Note: The compilation of
the expression can result in side
effects such as the instantiation of
class template specializations and
function template specializations, the
generation of implicitly-defined
functions, and so on. Such side effects
are not in the "immediate context"
and can result in the program being
ill-formed. — end note]…
Change 20.15.6 [meta.rel] p4 as indicated:
-4- Given the following function prototype:
template <class T> add_rvalue_reference_t<T> create() noexcept;the predicate condition for a template specialization is_convertible<From, To> shall be satisfied if and only if the return expression in the following code would be
well-formedvalid in a contrived argument deduction context ([defns.valid.contr.context]) for types To and From, including any implicit conversions to the return type of the function:To test() { return create<From>(); }[Note: This requirement gives well defined results for reference types, void types, array types, and function types. — end note]
Access checking is performed as if in a context unrelated to To and From. Only the validity of the immediate context of the expression of the return-statement (including conversions to the return type) is considered. [Note: The evaluation of the conversion can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]
Section: 17.5.1.4 [structure.specifications] Status: New Submitter: Jeffrey Yasskin Opened: 2013-09-03 Last modified: 2016-02-12
Priority: 3
View other active issues in [structure.specifications].
View all other issues in [structure.specifications].
View all issues with New status.
Discussion:
The C++14 CD has 25 sections including the phrase "X shall not participate in overload resolution ...". Most of these uses are double negatives, which are hard to interpret. "shall not ... unless" tends to be the easiest to read, since the condition is true when the function is available, but we also have a lot of "if X is not Y, then Z shall not participate", which actually means "You can call Z if X is Y." The current wording is also clumsy and long-winded. We should find a better and more concise phrasing.
As an initial proposal, I'd suggest using "X is enabled if and only if Y" in prose and adding an "Enabled If: ..." element to 17.5.1.4 [structure.specifications]. Daniel: I suggest to name this new specification element for 17.5.1.4 [structure.specifications] as "Template Constraints:" instead, because the mentioned wording form was intentionally provided starting with LWG 1237 to give implementations more freedom to realize the concrete constraints. Instead of the original std::enable_if-based specifications we can use better forms of "SFINAE" constraints today and it eases the path to possible language-based constraints in the future.Proposed resolution:
Section: 26.9 [c.math] Status: Open Submitter: Pete Becker Opened: 2013-09-04 Last modified: 2016-02-12
Priority: 2
View other active issues in [c.math].
View all other issues in [c.math].
View all issues with Open status.
Discussion:
… and abs(float) and abs(long double). And <cmath> should declare abs(int), abs(long), and abs(long long).
As things currently stand, this program is illegal:#include <cstdlib> int main() { double d = -1.23; double dd = std::abs(d); return 0; }
The call is ambiguous because of the various integer overloads, that's because <cstdlib> provides abs(int) but not abs(double).
This lead one commenter on Stackoverflow to state that abs is dangerous, and to recommend using fabs instead. In general, it makes sense to declare overloaded functions that take user-defined types in the same header as the definition of the user-defined types; it isn't necessary to declare all of the overloads in the same place. But here we're not dealing with any user-defined types; we're dealing with builtin types, which are always defined; all of the overloads should be defined in the same place, to avoid mysterious problems like the one in the code above. The standard library has six overloads for abs:int abs(int); // <cstdlib> long abs(long); // <cstdlib> long long abs(long long); // <cstdlib> float abs(float); // <cmath> double abs(double); // <cmath> long double abs(long double); // <cmath>
These should all be declared in both headers.
I have no opinion on <stdlib.h> and <math.h>.[2013-09 Chicago]
This issue is related to LWG 2192
Move to open[2014-02-13 Issaquah — Nicolai Josuttis suggest wording]
[2015-03-03, Geoffrey Romer provides improved wording]
See proposed resolution of LWG 2192.
[2015-09-11, Telecon]
Geoff provided combined wording for 2192 after Cologne, Howard to provide updated wording for Kona.
Howard: my notes say I wanted to use is_unsigned instead of 'unsigned integral type'.
Previous resolution from Nicolai [SUPERSEDED]:
Edit 26.9 [c.math] after p7 as indicated:
-6- In addition to the int versions of certain math functions in <cstdlib>, C++ adds long and long long overloaded versions of these functions, with the same semantics.
-7- The added signatures are:long abs(long); // labs() long long abs(long long); // llabs() ldiv_t div(long, long); // ldiv() lldiv_t div(long long, long long); // lldiv()-?- To avoid ambiguities, C++ also adds the following overloads of abs() to <cstdlib>, with the semantics defined in <cmath>:
float abs(float); double abs(double); long double abs(long double);-?- To avoid ambiguities, C++ also adds the following overloads of abs() to <cmath>, with the semantics defined in <cstdlib>:
int abs(int); long abs(long); long long abs(long long);
Proposed resolution:
See proposed resolution of LWG 2192.
Section: 22.3.1.2 [locale.cons] Status: New Submitter: Juan Soulie Opened: 2013-09-04 Last modified: 2016-02-12
Priority: 3
View all issues with New status.
Discussion:
22.3.1.2 [locale.cons] p14 ends with:
"[…] If f is null, the resulting object is a copy of other."
but the next line p15 says:
"Remarks: The resulting locale has no name."
But both can't be true when other has a name and f is null.
I've tried it on two implementations (MSVC,GCC) and they are inconsistent with each other on this.Daniel Krügler:
As currently written, the Remarks element applies unconditionally for all cases and thus should "win". The question arises whether the introduction of this element by LWG 424 had actually intended to change the previous Note to a Remarks element. In either case the wording should be improved to clarify this special case.Proposed resolution:
Section: 18.6.2.3 [new.delete.placement] Status: New Submitter: Daniel Krügler Opened: 2013-09-18 Last modified: 2016-02-12
Priority: 3
View all other issues in [new.delete.placement].
View all issues with New status.
Discussion:
The library gives explicit permission in 17.6.4.2.1 [namespace.std] p2 that user code may explicitly instantiate a library template provided that the instantiations depend on at least one user-defined type:
A program may explicitly instantiate a template defined in the standard library only if the declaration depends on the name of a user-defined type and the instantiation meets the standard library requirements for the original template.
But it seems that the C++11 library is not specified in a way that guarantees such an instantiation to be well-formed if the minimum requirements of the library is not satisfied.
For example, in general, the first template parameter of std::vector is not required to be DefaultConstructible in general, but due to the split of the single C++03 member function with default argumentvoid resize(size_type sz, T c = T());
into
void resize(size_type sz); void resize(size_type sz, const T& c);
the effect is now that for a type ND that is not DefaultConstructible, such as
struct NP { NP(int); };
the explicit instantiation of std::vector<ND> is no longer well-formed, because the attempt to instantiate the single-argument overload of resize cannot not succeed, because this function imposes the DefaultInsertable requirements and given the default allocator this effectively requires DefaultConstructible.
But DefaultConstructible is not the only point, what about CopyConstructible versus MoveConstructible alone? It turns out that currently the second resize overload would fail during an explicit instantiation for a type like
struct MO { MO() = default; MO(MO&&) = default; };
because it imposes CopyInsertable requirements that end up being equivalent to the CopyConstructible requirements for the default allocator.
Technically a library can solve these issues: For special member functions by defining them in some base class, for others by transforming them effectively into a function template due to the great feature of default template arguments for function templates (At the very moment the validity of the latter approach depends on a resolution of core language issue CWG 1635, though). E.g. the here mentioned resize functions of std::vector could be prevented from instantiation by defining them like this with an implementation:template<class = void> void resize(size_type sz) { […] } template<class = void> void resize(size_type sz, const T& c) { […] }
In this case, these functions could also be defined in a base class, but the latter approach won't work in all cases.
Basically such an implementation is required to constrain all member functions that are not covered by the general requirements imposed on the actual library template parameters. I tested three different C++11 library implementations and but none could instantiate for example std::list, std::vector, or std::deque with value types that are not DefaultConstructible or only MoveConstructible. This issue is raised to clarify the current situation in regard to the actual requirements imposed on user-provided types that are used to explicitly instantiate Library-provided templates. For example, the current Container requirements impose very little requirements on the actual value type and it is unclear to which extend library implementations have to respect that. The minimum solution of this issue should be to at least realize that there is no fundamental requirement on DefaultConstructible for value types of library containers, because we have since C++03 the general statement of 17.6.3.1 [utility.arg.requirements] ("In general, a default constructor is not required."). It is unclear whether CopyConstructible should be required for an explicit instantiation request, but given the careful introduction of move operations in the library it would seem astonishing that a MoveConstructible type wouldn't suffice for value types of the container types. In any case I can envision at least two approaches to solve this issue:As indicated in LWG 2292, those function could get an explicit "Template Constraints:" element, albeit this promises more than needed to solve this issue.
The library could introduce a completely new element form, such as "Instantiation Constraints:" that would handle this situation for explicit instantiation situations. This would allow for simpler techniques to solve the issue when explicit instantiation is required compared to the first bullet, because it would not (necessarily) guarantee SFINAE-friendly expression-wellformedness, such as inspecting the expression std::declval<std::vector<ND>&>.resize(0) in an unevaluated context.
It should be noted that the 2013-08-27 comment to LWG 2193 could be resolved by a similar solution as indicated in this issue here.
Proposed resolution:
Section: 23 [containers] Status: Open Submitter: Zhihao Yuan Opened: 2013-09-26 Last modified: 2016-02-12
Priority: 2
View other active issues in [containers].
View all other issues in [containers].
View all issues with Open status.
Discussion:
LWG 2193 yields explicit for default ctors to allow {}, but not for all cases of uniform initialization. For example:
explicit vector(size_type count, const Allocator& alloc = Allocator());
This prevents {n, alloc()}. Although this use is relatively rare, but the behavior is inconsistent with that of
vector(size_type count, const T& value, const Allocator& alloc = Allocator());
[Urbana 2014-11-07: Move to Open]
Proposed resolution:
Section: 21.3.1 [basic.string] Status: New Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2016-02-12
Priority: 4
View other active issues in [basic.string].
View all other issues in [basic.string].
View all issues with New status.
Discussion:
21.3.1.4 [string.capacity]/8 specifies basic_string::resize(n, c) with:
Effects: Alters the length of the string designated by *this as follows:
If n <= size(), the function replaces the string designated by *this with a string of length n whose elements are a copy of the initial elements of the original string designated by *this.
If n > size(), the function replaces the string designated by *this with a string of length n whose first size() elements are a copy of the original string designated by *this, and whose remaining elements are all initialized to c.
This wording is a relic of the copy-on-write era. In addition to being extremely confusing, it has undesirable implications. Saying "replaces the string designated by *this with a string of length n whose elements are a copy" suggests that the trimming case can reallocate. Reallocation during trimming should be forbidden, like vector.
At least 7 paragraphs are affected: 21.3.1.4 [string.capacity]/8, 21.3.1.6.2 [string::append]/9, 21.3.1.6.3 [string::assign]/3 and /10, 21.3.1.6.4 [string::insert]/11, 21.3.1.6.5 [string::erase]/4, and 21.3.1.6.6 [string::replace]/11 say "replaces the string [designated/controlled] by *this". (21.3.1.6.7 [string::copy]/3 is different — it "replaces the string designated by s".) Of the affected paragraphs, resize() and erase() are the most important to fix because they should forbid reallocation during trimming.Proposed resolution:
Section: 23.2.1 [container.requirements.general] Status: Open Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2016-02-12
Priority: 2
View other active issues in [container.requirements.general].
View all other issues in [container.requirements.general].
View all issues with Open status.
Discussion:
23.2.1 [container.requirements.general]/10 says that unless otherwise specified, "no swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. [Note: The end() iterator does not refer to any element, so it may be invalidated. — end note]". However, move constructors and move assignment operators aren't given similar invalidation guarantees. The guarantees need several exceptions, so I do not believe that blanket language like /11 "Unless otherwise specified (either explicitly or by defining a function in terms of other functions), invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to, or change the values of, objects within that container." is applicable.
[2014-02-13 Issaquah]
General agreeement on intent, several wording nits and additional paragraphs to hit.
STL to provide updated wording. Move to Open.
[2015-02, Cologne]
AM: in the proposed wording, I'd like to mention that the iterators now refer to elements of a different container. I think we're saying something like this somewhere. JY: There's some wording like that for swap I think. TK: It's also in list::splice(). DK to JY: 23.2.1p9.
VV: The issue says that STL was going to propose new wording. Has he done that? AM: I believe we're looking at that. GR: The request touches on multiple paragraphs, and this PR has only one new paragraph, so this looks like it's not up-to-date. MC: This was last updated a year ago in Issaquah. Conclusion: Skip, not up to date.[2015-06, Telecom]
Still waiting for updated wording
Proposed resolution:
This wording is relative to N3691.
In 23.2.1 [container.requirements.general]/10 change as indicated:
-10- Unless otherwise specified (see 23.2.4.1, 23.2.5.1, 23.3.3.4, and 23.3.7.5) all container types defined in this Clause meet the following additional requirements:
[…]
no copy constructor or assignment operator of a returned iterator throws an exception.
no move constructor (or move assignment operator when allocator_traits<allocator_type>::propagate_on_container_move_assignment::value is true) of a container (except for array) invalidates any references, pointers, or iterators referring to the elements of the source container. [Note: The end() iterator does not refer to any element, so it may be invalidated. — end note]
no swap() function throws an exception.
no swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped. [Note: The end() iterator does not refer to any element, so it may be invalidated. — end note]
Section: 28.5.1 [re.synopt] Status: Open Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2016-02-12
Priority: 3
View other active issues in [re.synopt].
View all other issues in [re.synopt].
View all issues with Open status.
Discussion:
The table in 28.5.1 [re.synopt]/1 says that regex_constants::collate "Specifies that character ranges of the form "[a-b]" shall be locale sensitive.", but 28.13 [re.grammar]/14 says that it affects individual character comparisons too.
[2012-02-12 Issaquah : recategorize as P3]
Marshall Clow: 28.13/14 only applies to ECMAScript
All: we're unsure
Jonathan Wakely: we should ask John Maddock
Move to P3
[2014-5-14, John Maddock response]
The original intention was the original wording: namely that collate only made character ranges locale sensitive. To be frank it's a feature that's probably hardly ever used (though I have no real hard data on that), and is a leftover from early POSIX standards which required locale sensitive collation for character ranges, and then later changed to implementation defined if I remember correctly (basically nobody implemented locale-dependent collation).
So I guess the question is do we gain anything by requiring all character-comparisons to go through the locale when this bit is set? Certainly it adds a great deal to the implementation effort (it's not what Boost.Regex has ever done). I guess the question is are differing code-points that collate identically an important use case? I guess there might be a few Unicode code points that do that, but I don't know how to go about verifying that. STL: If this was unintentional, then 28.5.1 [re.synopt]/1's table should be left alone, while 28.13 [re.grammar]/14 should be changed instead. Jeffrey Yasskin: This page mentions that [V] in Swedish should match "W" in a perfect world. However, the most recent version of TR18 retracts both language-specific loose matches and language-specific ranges because "for most full-featured regular expression engines, it is quite difficult to match under code point equivalences that are not 1:1" and "tailored ranges can be quite difficult to implement properly, and can have very unexpected results in practice. For example, languages may also vary whether they consider lowercase below uppercase or the reverse. This can have some surprising results: [a-Z] may not match anything if Z < a in that locale." ECMAScript doesn't include collation at all. IMO, +1 to changing 28.13 instead of 28.5.1. It seems like we'd be on fairly solid ground if we wanted to remove regex_constants::collate entirely, in favor of named character classes, but of course that's not for this issue.Proposed resolution:
This wording is relative to N3691.
In 28.5.1 [re.synopt]/1, Table 138 — "syntax_option_type effects", change as indicated:
Table 138 — syntax_option_type effects Element Effect(s) if set … collate Specifies that character ranges of the form "[a-b]"comparisons and character range comparisons shall be locale sensitive.…
Section: 23.3.7 [array] Status: New Submitter: Jeffrey Yasskin Opened: 2013-10-04 Last modified: 2016-02-12
Priority: 3
View other active issues in [array].
View all other issues in [array].
View all issues with New status.
Discussion:
In order to replace some uses of C arrays with std::array, we need it to be possible to cast from a std::array<> to an equivalent C array. Core wording doesn't appear to be in quite the right state to allow casting, but if we specify that appropriate types are layout-compatible, we can at least write:
union { array<array<array<int, 2>, 3>, 4> arr; int carr[4][3][2]; };
to view memory as the other type: C++14 CD [class.mem]p18.
I believe it's sufficient to add "array<T, N> shall be layout-compatible (3.9 [basic.types]) with T[N]." to 23.3.7.1 [array.overview], but we might also need some extension to 9.2 [class.mem] to address the possibility of layout-compatibility between struct and array types.I checked that libc++ on MacOS already implements this, although it would be good for someone else to double-check; I haven't checked any other standard libraries.
Proposed resolution:
Section: 28.7 [re.traits], 22.3.1.1.2 [locale.facet] Status: Open Submitter: Sergey Zubkov Opened: 2013-10-15 Last modified: 2016-02-12
Priority: 3
View all other issues in [re.traits].
View all issues with Open status.
Discussion:
28.7 [re.traits]/7, begins with "if typeid(use_facet<collate<charT> >) == typeid(collate_byname<charT>)", which appears to be pseudocode with the intention to convey that the collate facet has not been replaced by the user. Cf. the wording in N1429 "there is no portable way to implement transform_primary in terms of std::locale, since even if the sort key format returned by std::collate_byname<>::transform is known and can be converted into a primary sort key, the user can still install their own custom std::collate implementation into the locale object used, and that can use any sort key format they see fit.".
Taken literally, 28.7 [re.traits]/7 appears to imply that named locales are required to hold their collate facets with dynamic type std::collate_byname<charT>, which is in fact true in some implementations (e.g libc++), but not others (e.g. libstdc++). This does not follow from the description of _byname in 22.3.1.1.2 [locale.facet]/4, which is only required to provide equivalent semantics, to the named locale's facet, not to actually be one.[2015-05-06 Lenexa: Move to Open]
MC, RP: Consequence of failing to follow the rule is UB.
MC: Tightening of requirements.
RP: It should be this way, we just didn't impose it before.
MC: Second change is a bug fix, original code didn't work.
TK: Doesn't seem to make things worse.
Bring up in larger group tomorrow.
JW arrives.
JW: libstdc++ violates this due to two std::string ABIs.
JW: This prevents installing a type derived from Facet_byname, constrains the implementor from using a smarter derived class version.
JW: Can't look at facet id to detect replacement, because replacements have the same id.
RP: Can you give it multiple ids through multiple inheritance?
JW: No, the facet mechanism wouldn't like that.
JW: We should also ask Martin Sebor, he's implemented this stuff recently.
MC: Sounds like this resolution doesn't work, need a better solution.
JW: Write in words "if the facet has not been replaced by the user", the implementation knows how to detect that, but not like this.
RP: User RE traits need to detect this too.
JW: =(
Move to Open, JW will invite Martin Sebor to join LWG for discussion.
Later ...
JW: This is not needed for user specializations after all.
MC: Agree, [re.traits]/7 only applies to the stdlib traits.
NM: Effects: doesn't make sense.
JW, NM, Martin Sebor to come up with new wording.
Proposed resolution:
This wording is relative to N3691.
Modify 22.3.1.1.2 [locale.facet]/4 as indicated:
For some standard facets a standard "..._byname" class, derived from it, implements the virtual function semantics
equivalent toprovided by that facet of the locale constructed by locale(const char*) with the same name. Each such facet provides a constructor that takes a const char* argument, which names the locale, and a refs argument, which is passed to the base class constructor. Each such facet also provides a constructor that takes a string argument str and a refs argument, which has the same effect as calling the first constructor with the two arguments str.c_str() and refs. If there is no "..._byname" version of a facet, the base class implements named locale semantics itself by reference to other facets. For any locale loc constructed by locale(const char*) and facet Facet that has a corresponding standard Facet_byname class, typeid(use_facet<Facet>(loc)) == typeid(Facet_byname).
Modify 28.7 [re.traits]/7 as indicated:
template <class ForwardIterator> string_type transform_primary(ForwardIterator first, ForwardIterator last) const;-7- Effects: if typeid(use_facet<collate<charT> >(getloc())) == typeid(collate_byname<charT>) and the form of the sort key returned by collate_byname<charT>::transform(first, last) is known and can be converted into a primary sort key then returns that key, otherwise returns an empty string.
Section: 27.7.3.1 [ostream] Status: New Submitter: Alf P. Steinbach Opened: 2013-10-29 Last modified: 2016-02-12
Priority: 4
View all other issues in [ostream].
View all issues with New status.
Discussion:
For wide streams argument types wchar_t const* and wchar_t are supported only as template parameters. User defined conversions are not considered for template parameter matching. Hence inappropriate overloads of operator<< are selected when an implicit conversion is required for the argument, which is inconsistent with the behavior for char const* and char, is unexpected, and is a useless result.
Demonstration:#include <iostream> struct Byte_string { operator char const*() const { return "Hurray, it works!"; } }; struct Wide_string { operator wchar_t const*() const { return L"Hurray, it works!"; } }; struct Byte_ch { operator char() const { return 'X'; } }; struct Wide_ch { operator wchar_t() const { return L'X'; } }; auto main() -> int { using namespace std; wcout << "'X' as char value : " << Byte_ch() << endl; wcout << "'X' as wchar_t value: " << Wide_ch() << endl; wcout << "Byte string pointer : " << Byte_string() << endl; wcout << "Wide string pointer : " << Wide_string() << endl; }
Example output:
'X' as char value : X 'X' as wchar_t value: 88 Byte string pointer : Hurray, it works! Wide string pointer : 000803C8
Proposed resolution:
This wording is relative to N3797.
Modify 27.7.3.1 [ostream], class template basic_ostream synopsis, as indicated:
namespace std { […] // 27.7.3.6.4 character inserters template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, charT); template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, char); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, char); template<class traits> basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>&, wchar_t); […] template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*); template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const char*); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>&, const char*); template<class traits> basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>&, const wchar_t*); […] }
Modify 27.7.3.6.4 [ostream.inserters.character] as indicated: [Drafting note: The replacement of os by out in p1 and the insertion of "out." in p4 just fix two obvious typos — end drafting note]
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out, charT c); template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out, char c); // specialization template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, char c); template<class traits> basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>& out, wchar_t c); // signed and unsigned template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, signed char c); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, unsigned char c);-1- Effects: Behaves as a formatted output function (27.7.3.6.1 [ostream.formatted.reqmts]) of out. Constructs a character sequence seq. If c has type char and the character type of the stream is not char, then seq consists of out.widen(c); otherwise seq consists of c. Determines padding for seq as described in 27.7.3.6.1 [ostream.formatted.reqmts]. Inserts seq into out. Calls
-2- Returns: out.osout.width(0).template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out, const charT* s); template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>& out, const char* s); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, const char* s); template<class traits> basic_ostream<wchar_t,traits>& operator<<(basic_ostream<wchar_t,traits>& out, const wchar_t* s); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, const signed char* s); template<class traits> basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, const unsigned char* s);-3- Requires: s shall not be a null pointer.
-4- Effects: Behaves like a formatted inserter (as described in 27.7.3.6.1 [ostream.formatted.reqmts]) of out. Creates a character sequence seq of n characters starting at s, each widened using out.widen() (27.5.5.3), where n is the number that would be computed as if by:
traits::length(s) for the following overloads:
where the first argument is of type basic_ostream<charT, traits>& and the second is of type const charT*,
and also for the overloadwhere the first argument is of type basic_ostream<char, traits>& and the second is of type const char*,where the first argument is of type basic_ostream<wchar_t, traits>& and the second is of type const wchar_t*,
std::char_traits<char>::length(s) for the overload where the first argument is of type basic_ostream<charT, traits>& and the second is of type const char*,
traits::length(reinterpret_cast<const char*>(s)) for the other two overloads.
Determines padding for seq as described in 27.7.3.6.1 [ostream.formatted.reqmts]. Inserts seq into out. Calls out.width(0).
-5- Returns: out.
Section: 28.13 [re.grammar] Status: New Submitter: Nayuta Taga Opened: 2013-10-30 Last modified: 2016-02-12
Priority: 2
View other active issues in [re.grammar].
View all other issues in [re.grammar].
View all issues with New status.
Discussion:
In the following "Multiline" is the value of the ECMA-262 RegExp object's multiline property.
In ECMA-262, there are some definitions that relate to Multiline:ECMA-262 15.10.2.6:
If Multiline is true, ^ matches just after LineTerminator.
If Multiline is false, ^ does not match just after LineTerminator. If Multiline is true, $ matches just before LineTerminator. If Multiline is false, $ does not match just before LineTerminator.
ECMA-262 15.10.4.1, 15.10.7.4:
By default, Multiline is false.
So, the C++11 standard says that Multiline is false. As it is false, ^ matches only the beginning of the string, and $ matches only the end of the string.
However, two flags are defined in 28.5.2 [re.matchflag] Table 139:match_not_bol: the character ^ in the regular expression shall not match [first,first).
match_not_eol: the character "$" in the regular expression shall not match [last,last).
As Multiline is false, the match_not_bol and the match_not_eol are meaningless because they only make ^ and $ match none.
In my opinion, Multiline should be true. FYI, Multiline of the existing implementations are as follows: Multiline=false:libstdc++ r206594
libc++ r199174
Multiline=true:
Visual Studio Express 2013
boost 1.55
[2015-05-22, Daniel comments]
This issue interacts with LWG 2503.
Proposed resolution:
Section: 20.9 [template.bitset], 27.7.6 [quoted.manip] Status: Open Submitter: Zhihao Yuan Opened: 2013-12-02 Last modified: 2016-02-12
Priority: 3
View other active issues in [template.bitset].
View all other issues in [template.bitset].
View all issues with Open status.
Discussion:
Example: char16_t('1') != u'1' is possible.
The numeric value of char16_t is defined to be Unicode code point, which is same to the ASCII value and UTF-8 for 7-bit chars. However, char is not guaranteed to have an encoding which is compatible with ASCII. For example, '1' in EBCDIC is 241. I found three places in the standard casting narrow char literals: bitset::bitset, bitset::to_string and quoted. PJ confirmed this issue and says he has a solution used in their <filesystem> implementation, and he may want to propose it to the standard. The solution in my mind, for now, is to make those default arguments magical, where the "magic" can be implemented with a C11 _Generic selection (works in clang):#define _G(T, literal) _Generic(T{}, \ char: literal, \ wchar_t: L ## literal, \ char16_t: u ## literal, \ char32_t: U ## literal) _G(char16_t, '1') == u'1'
[Lenexa 2015-05-05: Move to Open]
Ask for complete PR (need quoted, to string, et al.)
Will then take it up again
Expectation is that this is correct way to fix this
Proposed resolution:
This wording is relative to N3797.
[Drafting note: This is a sample wording fixing only one case; I'm just too lazy to copy-paste it before we discussed whether the solution is worth and sufficient (for example, should the other `charT`s like `unsigned char` just don't compile without supplying those arguments? I hope so). — end drafting note]Modify 20.9 [template.bitset] p1, class template bitset synopsis, as indicated:
namespace std { template <size_t N> class bitset { public: […] template<class charT, class traits, class Allocator> explicit bitset( const basic_string<charT,traits,Allocator>& str, typename basic_string<charT,traits,Allocator>::size_type pos = 0, typename basic_string<charT,traits,Allocator>::size_type n = basic_string<charT,traits,Allocator>::npos, charT zero =charT('0')see below, charT one =charT('1')see below); […] }; […] }
Modify 20.9.1 [bitset.cons] as indicated:
template<class charT, class traits, class Allocator> explicit bitset(const basic_string<charT, traits, Allocator>& str, typename basic_string<charT, traits, Allocator>::size_type pos = 0, typename basic_string<charT, traits, Allocator>::size_type n = basic_string<charT, traits, Allocator>::npos, charT zero =charT('0')see below, charT one =charT('1')see below);-?- The default values of zero and one compare equal to the character literals 0 and 1 of type charT, respectively.
-3- Requires:: pos <= str.size(). […]
Section: 27.7.2.2.1 [istream.formatted.reqmts] Status: Open Submitter: Zhihao Yuan Opened: 2013-12-06 Last modified: 2016-02-12
Priority: 3
View all other issues in [istream.formatted.reqmts].
View all issues with Open status.
Discussion:
The formatted input function requirement says in 27.7.2.2.1 [istream.formatted.reqmts]:
"If an exception is thrown during input then ios::badbit is turned on in *this's error state. If (exceptions()&badbit) != 0 then the exception is rethrown."
while some formatted function may throw an exception from basic_ios::clear, for example in 20.9.4 [bitset.operators] p6:
"If no characters are stored in str, calls is.setstate(ios_base::failbit) (which may throw ios_base::failure)"
So should this exception be considered as "an exception [...] thrown during input"? And here is an implementation divergence (or you can read the following as "a bug libc++ only has" :)
cin.exceptions(ios_base::failbit); bitset<N> b; try { cin >> b; // type 'a' and return } catch (...) {}
Now cin.rdstate() is just failbit in libstdc++ (and Dinkumware, by PJ), but failbit & badbit libc++. Similar difference found in other places, like eofbit & badbid after std::getline.
PJ and Matt both agree that the intention (of badbit + rethrow) is "to signify an exception arising in user code, not the iostreams package". In addition, I found the following words in unformatted input function's requirements (27.7.2.3 [istream.unformatted]):If an exception is thrown during input then ios::badbit is turned on in *this's error state. (Exceptions thrown from basic_ios<>::clear() are not caught or rethrown.) If (exceptions()&badbit) != 0 then the exception is rethrown.
The content within the parenthesis is added by LWG defect 61, and does fix the ambiguity. However, it only fixed the 1 of 4 requirements, and it lost some context (the word "rethrown" is not seen before this sentence within this section).
[Lenexa 2015-05-07: Marshall to research and report]
Proposed resolution:
This wording is relative to N3797.
[Drafting note: The editor is kindly asked to introduce additional spaces at the following marked occurrences of operator& — end drafting note]Modify 27.7.2.2.1 [istream.formatted.reqmts] p1 as indicated:
-1- Each formatted input function begins execution by constructing an object of class sentry with the noskipws (second) argument false. If the sentry object returns true, when converted to a value of type bool, the function endeavors to obtain the requested input. If an exception, other than the ones thrown from clear(), if any, is thrown during input then ios::badbit is turned on[Footnote 314] in *this's error state. If (exceptions() & badbit) != 0 then the exception is rethrown. In any case, the formatted input function destroys the sentry object. If no exception has been thrown, it returns *this.
Modify 27.7.3.6.1 [ostream.formatted.reqmts] p1 as indicated:
-1- Each formatted output function begins execution by constructing an object of class sentry. If this object returns true when converted to a value of type bool, the function endeavors to generate the requested output. If the generation fails, then the formatted output function does setstate(ios_base::failbit), which might throw an exception. If an exception, other than the ones thrown from clear(), if any, is thrown during output, then ios::badbit is turned on[Footnote 327] in *this's error state. If (exceptions() & badbit) != 0 then the exception is rethrown. Whether or not an exception is thrown, the sentry object is destroyed before leaving the formatted output function. If no exception is thrown, the result of the formatted output function is *this.
Modify 27.7.3.7 [ostream.unformatted] p1 as indicated:
-1- Each unformatted output function begins execution by constructing an object of class sentry. If this object returns true, while converting to a value of type bool, the function endeavors to generate the requested output. If an exception, other than the ones thrown from clear(), if any, is thrown during output, then ios::badbit is turned on[Footnote 330] in *this's error state. If (exceptions() & badbit) != 0 then the exception is rethrown. In any case, the unformatted output function ends by destroying the sentry object, then, if no exception was thrown, returning the value specified for the unformatted output function.
Modify 27.7.2.3 [istream.unformatted] p1 as indicated:
-1- Each unformatted input function begins execution by constructing an object of class sentry with the default argument noskipws (second) argument true. If the sentry object returns true, when converted to a value of type bool, the function endeavors to obtain the requested input. Otherwise, if the sentry constructor exits by throwing an exception or if the sentry object returns false, when converted to a value of type bool, the function returns without attempting to obtain any input. In either case the number of extracted characters is set to 0; unformatted input functions taking a character array of non-zero size as an argument shall also store a null character (using charT()) in the first location of the array. If an exception, other than the ones thrown from clear(), if any, is thrown during input then ios::badbit is turned on[Footnote 317] in *this's error state.
(Exceptions thrown from basic_ios<>::clear() are not caught or rethrown.)If (exceptions() & badbit) != 0 then the exception is rethrown. It also counts the number of characters extracted. If no exception has been thrown it ends by storing the count in a member object and returning the value specified. In any event the sentry object is destroyed before leaving the unformatted input function.
Section: 26.6.7.1 [rand.util.seedseq] Status: New Submitter: Thomas Plum Opened: 2013-12-02 Last modified: 2016-02-12
Priority: 2
View all other issues in [rand.util.seedseq].
View all issues with New status.
Discussion:
With respect to class seed_seq 26.6.7.1 [rand.util.seedseq], is a default-constructed std::seed_seq intended to produce a predictable .generate() sequence?
Implementations differ.Proposed resolution:
Section: 20.15.4.3 [meta.unary.prop] Status: New Submitter: Richard Smith Opened: 2014-02-01 Last modified: 2016-02-12
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with New status.
Discussion:
The 'Condition' for std::is_empty is listed as:
"T is a class type, but not a union type, with no non-static data members other than bit-fields of length 0, no virtual member functions, no virtual base classes, and no base class B for which is_empty<B>::value is false."
This is incorrect: there is no such thing as a non-static data member that is a bit-field of length 0, since bit-fields of length 0 must be unnamed, and unnamed bit-fields are not members (see 9.2.4 [class.bit] p2).
It also means that classes such as:struct S { int : 3; };
are empty (because they have no non-static data members). There's implementation divergence on the value of is_empty<S>::value.
I'm not sure what the purpose of is_empty is (or how it could be useful), but if it's desirable for the above type to not be treated as empty, something like this could work:"T is a class type, but not a union type, with no non-static data members
other than, no unnamed bit-fields of non-zero length0, no virtual member functions, no virtual base classes, and no base class B for which is_empty<B>::value is false."
and if the above type should be treated as empty, then this might be appropriate:
"T is a class type, but not a union type, with no (named) non-static data members
other than bit-fields of length 0, no virtual member functions, no virtual base classes, and no base class B for which is_empty<B>::value is false."
Proposed resolution:
Section: 23.2.4 [associative.reqmts], 23.2.5 [unord.req] Status: New Submitter: Jeffrey Yasskin Opened: 2014-02-15 Last modified: 2015-09-25
Priority: 3
View other active issues in [associative.reqmts].
View all other issues in [associative.reqmts].
View all issues with New status.
Discussion:
a_uniq.emplace(args) is specified as:
Effects: Inserts a value_type object t constructed with
std::forward<Args>(args)... if and only if there is no element in the
container with key equivalent to the key of t. The bool component of
the returned pair is true if and only if the insertion takes place,
and the iterator component of the pair points to the element with key
equivalent to the key of t.
However, we occasionally find code of the form:
std::unique_ptr<Foo> p(new Foo); auto res = m.emplace("foo", std::move(p));
where we'd like to avoid destroying the Foo if the insertion doesn't take place (if the container already had an element with the specified key).
N3873 includes a partial solution to this in the form of a new emplace_stable member function, but LEWG's discussion strongly agreed that we'd rather have emplace() Just Work: Should map::emplace() be guaranteed not to move/copy its arguments if the insertion doesn't happen? SF: 8 F: 3 N: 0 A: 0 SA: 0 This poll was marred by the fact that we didn't notice or call out that emplace() must construct the key before doing the lookup, and it must not then move the key after it determines whether an insert is going to happen, and the mapped_type instance must live next to the key. The very similar issue 2006 was previously marked NAD, with N3178 as discussion. However, given LEWG's interest in the alternate behavior, we should reopen the question in this issue. We will need a paper that describes how to implement this before we can make more progress.Proposed resolution:
Section: 30.4.1.5.1 [thread.sharedtimedmutex.class] Status: Open Submitter: Richard Smith Opened: 2014-02-16 Last modified: 2016-03-06
Priority: 2
View all issues with Open status.
Discussion:
30.4.1.5.1 [thread.sharedtimedmutex.class] paragraph 2:
The class shared_timed_mutex shall satisfy all of the SharedTimedMutex requirements (30.4.1.4). It shall be a standard layout class (Clause 9).
There's no SharedTimedMutex requirements; this name doesn't appear anywhere else in the standard. (Prior to N3891, this was SharedMutex, which was equally undefined.)
I assume this concept should be defined somewhere? Also, n3891 changes 30.4.1.5 [thread.sharedtimedmutex.requirements] from defining "shared mutex type" to defining "shared timed mutex type", but its paragraph 2 still talks about "shared mutex type". Is that OK? I think you could argue that it's clear enough what it means, but presumably it should use the term that paragraph 1 defined. 30.4.2.3 [thread.lock.shared] paragraph 1 talks about the "shared mutex requirements", which again is a term that isn't defined, and presumably means "the requirements on a shared timed mutex type" or similar (maybe if SharedMutex or SharedTimedMutex were defined it could be reused here).[2014-05-22, Daniel comments]
As for SharedTimedMutex, there exists a similar problem in regard to TimedMutex referred to in 30.4.1.3.1 [thread.timedmutex.class] p2 and in 30.4.1.3.2 [thread.timedmutex.recursive] p2, but nowhere defined.
Another problem is, that according to 30.4.1.2.1 [thread.mutex.class] p3, "The class mutex shall satisfy all the Mutex requirements (30.4.1 [thread.mutex.requirements]).", but there are no concrete Mutex requirements, 30.4.1 [thread.mutex.requirements] — titled as "Mutex requirements" — describes mutex types, timed mutex types, and shared timed mutex types.[2014-06-08, Daniel comments and provides wording]
The presented wording adds to the existing mutex types, timed mutex types, and shared timed mutex types terms a new set of corresponding MutexType, TimedMutexType, and SharedTimedMutexType requirements.
The reason for the change of requirement names is two-fold: First, the new name better matches the intention to have a concrete name for the requirements imposed on the corresponding mutex types (This kind of requirement deviate from the more general Lockable requirements, which are not restricted to a explicitly enumerated set of library types). Second, using **MutexType over **Mutex provides the additional advantage that it reduces the chances of confusing named requirements from template parameters named Mutex (such as for unique_lock or shared_lock). Nonetheless the here presented wording has one unfortunate side-effect: Once applied it would have the effect that types used to instantiate std::shared_lock cannot be user-defined shared mutex types due to 30.4.2.3 [thread.lock.shared]. The reason is based on the currently lack of an existing SharedLockable requirement set, which would complete the existing BasicLockable and Lockable requirements (which are "real" requirements). This restriction is not actually a problem introduced by the provided resolution but instead one that existed before but becomes more obvious now.[2015-02 Cologne]
Handed over to SG1.
[2015-05 Lenexa, SG1 response]
Thanks to Daniel, and please put it in SG1-OK status. Perhaps open another issue for the remaining problem Daniel points out?
[2015-10 pre-Kona]
SG1 hands this over to LWG for wording review
[2015-10-21 Kona, Daniel comments and adjusts wording to to untimed shared mutex types]
The new wording reflects the addition of the new shared mutex types. The approach used for shared_lock is similar to the one used for unique_lock: The template argument Mutex has a reduced requirement set that is not sufficient for all operations. Only those members that require stronger requirements of SharedTimedMutexType specify that additionally in the Requires element of the corresponding prototype specifications.
The proposed wording could be more general if we would introduce more fundamental requirements set for SharedLockable and SharedTimedLockable types which could be satisfied by user-provided types as well, because the SharedMutexType and SharedTimedMutexType requirements are essentially restricted to an enumerated set of types provided by the Standard Library. But this extension seemed too large for this issue and can be easily fixed later without any harm.Previous resolution [SUPERSEDED]:
This wording is relative to N3936.
Change 30.4.1.2 [thread.mutex.requirements.mutex] as indicated:
-1- The mutex types are the standard library types std::mutex, std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex, and std::shared_timed_mutex. They shall meet the MutexType requirements set out in this section. In this description, m denotes an object of a mutex type.
Change 30.4.1.2.1 [thread.mutex.class] as indicated:
-3- The class mutex shall satisfy all the MutexType requirements (30.4.1.2 [thread.mutex.requirements.mutex]
30.4.1 [thread.mutex.requirements]). It shall be a standard-layout class (Clause 9).Change 30.4.1.2.2 [thread.mutex.recursive] as indicated:
-2- The class recursive_mutex shall satisfy all the
MutexMutexType requirements (30.4.1.2 [thread.mutex.requirements.mutex]30.4.1 [thread.mutex.requirements]). It shall be a standard-layout class (Clause 9).Change 30.4.1.3 [thread.timedmutex.requirements] as indicated:
-1- The timed mutex types are the standard library types std::timed_mutex, std::recursive_timed_mutex, and std::shared_timed_mutex. They shall meet the TimedMutexType requirements set out below. In this description, m denotes an object of a mutex type, rel_time denotes an object of an instantiation of duration (20.12.5), and abs_time denotes an object of an instantiation of time_point (20.12.6).
Change 30.4.1.3.1 [thread.timedmutex.class] as indicated:
-2- The class timed_mutex shall satisfy all of the TimedMutexType requirements (30.4.1.3 [thread.timedmutex.requirements]). It shall be a standard-layout class (Clause 9).
Change 30.4.1.3.2 [thread.timedmutex.recursive] as indicated:
-2- The class recursive_timed_mutex shall satisfy all of the TimedMutexType requirements (30.4.1.3 [thread.timedmutex.requirements]). It shall be a standard-layout class (Clause 9).
Change 30.4.1.5 [thread.sharedtimedmutex.requirements] as indicated: [Drafting note: The reference to the timed mutex types requirements has been moved after introducing the new requirement set to ensure that SharedTimedMutexType refine TimedMutexType.]
-1- The standard library type std::shared_timed_mutex is a shared timed mutex type. Shared timed mutex types shall meet the SharedTimedMutexType requirements
-?- The shared timed mutex types shall meet the TimedMutexType requirements (30.4.1.3 [thread.timedmutex.requirements]).of timed mutex types (30.4.1.3 [thread.timedmutex.requirements]), and additionally shall meet the requirementsset out below. In this description, m denotes an object of a mutex type, rel_type denotes an object of an instantiation of duration (20.12.5), and abs_time denotes an object of an instantiation of time_point (20.12.6).Change 30.4.1.5.1 [thread.sharedtimedmutex.class] as indicated:
-2- The class shared_timed_mutex shall satisfy all of the SharedTimedMutexType requirements (30.4.1.5 [thread.sharedtimedmutex.requirements]). It shall be a standard-layout class (Clause 9).
Change 30.4.2.3 [thread.lock.shared] as indicated: [Drafting note: Once N3995 has been applied, the following reference should be changed to the new SharedMutexType requirements ([thread.sharedmutex.requirements]) or even better to some new SharedLockable requirements (to be defined) — end drafting note]
-1- […] The supplied Mutex type shall meet the
-2- [Note: shared_lock<Mutex> meets the TimedLockable requirements (30.2.5.4). — end note]shared mutexSharedTimedMutexType requirements (30.4.1.5 [thread.sharedtimedmutex.requirements]).
[2016-02 Jacksonville]
Marshall to review wording.
Proposed resolution:
This wording is relative to N4527.
Change 30.4.1.2 [thread.mutex.requirements.mutex] as indicated:
-1- The mutex types are the standard library types std::mutex, std::recursive_mutex, std::timed_mutex, std::recursive_timed_mutex, std::shared_mutex, and std::shared_timed_mutex. They shall meet the MutexType requirements set out in this section. In this description, m denotes an object of a mutex type.
-2- The mutex types shall meet the Lockable requirements (30.2.5.3 [thread.req.lockable.req]).
Change 30.4.1.2.1 [thread.mutex.class] as indicated:
-3- The class mutex shall satisfy all the MutexType requirements (30.4.1.2 [thread.mutex.requirements.mutex]
30.4.1 [thread.mutex.requirements]). It shall be a standard-layout class (Clause 9).
Change 30.4.1.2.2 [thread.mutex.recursive] as indicated:
-2- The class recursive_mutex shall satisfy all the
MutexMutexType requirements (30.4.1.2 [thread.mutex.requirements.mutex]30.4.1 [thread.mutex.requirements]). It shall be a standard-layout class (Clause 9).
Change 30.4.1.3 [thread.timedmutex.requirements] as indicated:
-1- The timed mutex types are the standard library types std::timed_mutex, std::recursive_timed_mutex, and std::shared_timed_mutex. They shall meet the TimedMutexType requirements set out below. In this description, m denotes an object of a mutex type, rel_time denotes an object of an instantiation of duration (20.12.5), and abs_time denotes an object of an instantiation of time_point (20.12.6).
-2- The timed mutex types shall meet the TimedLockable requirements (30.2.5.4 [thread.req.lockable.timed]).
Change 30.4.1.3.1 [thread.timedmutex.class] as indicated:
-2- The class timed_mutex shall satisfy all of the TimedMutexType requirements (30.4.1.3 [thread.timedmutex.requirements]). It shall be a standard-layout class (Clause 9).
Change 30.4.1.3.2 [thread.timedmutex.recursive] as indicated:
-2- The class recursive_timed_mutex shall satisfy all of the TimedMutexType requirements (30.4.1.3 [thread.timedmutex.requirements]). It shall be a standard-layout class (Clause 9).
Change 30.4.1.4 [thread.sharedmutex.requirements] as indicated: [Drafting note: The reference to the mutex types requirements has been moved after introducing the new requirement set to ensure that SharedMutexType refines MutexType.]
-1- The standard library types std::shared_mutex and std::shared_timed_mutex are shared mutex types. Shared mutex types shall meet the SharedMutexType requirements
-?- The shared mutex types shall meet the MutexType requirements (30.4.1.2 [thread.mutex.requirements.mutex]).of mutex types (30.4.1.2 [thread.mutex.requirements.mutex]), and additionally shall meet the requirementsset out below. In this description, m denotes an object of a shared mutex type.
Change 30.4.1.4.1 [thread.sharedmutex.class] as indicated:
-2- The class shared_mutex shall satisfy all of the SharedMutexType requirements
for shared mutexes(30.4.1.4 [thread.sharedmutex.requirements]). It shall be a standard-layout class (Clause 9).
Change 30.4.1.5 [thread.sharedtimedmutex.requirements] as indicated: [Drafting note: The reference to the timed mutex types requirements has been moved after introducing the new requirement set to ensure that SharedTimedMutexType refines TimedMutexType and SharedMutexType.]
-1- The standard library type std::shared_timed_mutex is a shared timed mutex type. Shared timed mutex types shall meet the SharedTimedMutexType requirements
-?- The shared timed mutex types shall meet the TimedMutexType requirements (30.4.1.3 [thread.timedmutex.requirements]) and the SharedMutexType requirements (30.4.1.4 [thread.sharedmutex.requirements]).of timed mutex types (30.4.1.3 [thread.timedmutex.requirements]), shared mutex types (30.4.1.4 [thread.sharedmutex.requirements]), and additionally shall meet the requirementsset out below. In this description, m denotes an object of a shared timed mutex type, rel_type denotes an object of an instantiation of duration (20.12.5), and abs_time denotes an object of an instantiation of time_point (20.12.6).
Change 30.4.1.5.1 [thread.sharedtimedmutex.class] as indicated:
-2- The class shared_timed_mutex shall satisfy all of the SharedTimedMutexType requirements
for shared timed mutexes(30.4.1.5 [thread.sharedtimedmutex.requirements]). It shall be a standard-layout class (Clause 9).
Change 30.4.2.3 [thread.lock.shared] as indicated:
-1- […] The supplied Mutex type shall meet the
-2- [Note: shared_lock<Mutex> meets the TimedLockable requirements (30.2.5.4). — end note]shared mutexSharedMutexType requirements (30.4.1.5 [thread.sharedtimedmutex.requirements]30.4.1.4 [thread.sharedmutex.requirements]).
Change 30.4.2.3.1 [thread.lock.shared.cons] as indicated:
template <class Clock, class Duration> shared_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time);-14- Requires: The supplied Mutex type shall meet the SharedTimedMutexType requirements (30.4.1.5 [thread.sharedtimedmutex.requirements]). The calling thread does not own the mutex for any ownership mode.
-15- Effects: Constructs an object of type shared_lock and calls m.try_lock_shared_until(abs_time). […]template <class Rep, class Period> shared_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time);-17- Requires: The supplied Mutex type shall meet the SharedTimedMutexType requirements (30.4.1.5 [thread.sharedtimedmutex.requirements]). The calling thread does not own the mutex for any ownership mode.
-18- Effects: Constructs an object of type shared_lock and calls m.try_lock_shared_for(rel_time). […]
Change 30.4.2.3.2 [thread.lock.shared.locking] as indicated:
template <class Clock, class Duration> bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time);-?- Requires: The supplied Mutex type shall meet the SharedTimedMutexType requirements (30.4.1.5 [thread.sharedtimedmutex.requirements]).
-8- Effects: pm->try_lock_shared_until(abs_time). […]template <class Rep, class Period> bool try_lock_for(const chrono::duration<Rep, Period>& rel_time);-?- Requires: The supplied Mutex type shall meet the SharedTimedMutexType requirements (30.4.1.5 [thread.sharedtimedmutex.requirements]).
-12- Effects: pm->try_lock_shared_for(rel_time). […]
Section: 24.6.3 [istreambuf.iterator] Status: New Submitter: Hyman Rosen Opened: 2014-02-19 Last modified: 2015-04-08
Priority: 3
View all other issues in [istreambuf.iterator].
View all issues with New status.
Discussion:
Given the following code,
#include <sstream> std::stringbuf buf; std::istreambuf_iterator<char> begin(&buf); std::istreambuf_iterator<char> end;
it is not clear from the wording of the Standard whether begin.equal(end) must be true. In at least one implementation it is not (CC: Sun C++ 5.10 SunOS_sparc Patch 128228-25 2013/02/20) and in at least one implementation it is (gcc version 4.3.2 x86_64-unknown-linux-gnu).
24.6.3 [istreambuf.iterator] says that end is an end-of-stream iterator since it was default constructed. It also says that an iterator becomes equal to an end-of-stream iterator when end of stream is reached by sgetc() having returned eof(). 24.6.3.5 [istreambuf.iterator::equal] says that equal() returns true iff both iterators are end of stream or not end of stream. But there seems to be no requirement that equal check for end-of-stream by calling sgetc(). Jiahan Zi at BloombergLP discovered this issue through his code failing to work correctly. Dietmar Kühl has opined in a private communication that the iterators should compare equal.Proposed resolution:
Section: 18.6.2 [new.delete] Status: Open Submitter: Stephen Clamage Opened: 2014-02-20 Last modified: 2016-03-08
Priority: 2
View all other issues in [new.delete].
View all issues with Open status.
Discussion:
Section 18.6.2 [new.delete] and subsections shows:
void* operator new(std::size_t size); void* operator new[](std::size_t size);
That is, without exception-specifications. (Recall that C++03 specified these functions with throw(std::bad_alloc).)
Section 17.6.5.12 [res.on.exception.handling] the end of paragraph 4 says:Any other functions defined in the C++ standard library that do not have an exception-specification may throw implementation-defined exceptions unless otherwise specified. An implementation may strengthen this implicit exception-specification by adding an explicit one.
For example, an implementation could provide C++03-compatible declarations of operator new.
Programmers are allowed to replace these operator new functions. But how can you write the definition of these functions when the exception specification can vary among implementations? For example, the declarationsvoid* operator new(std::size_t size) throw(std::bad_alloc); void* operator new(std::size_t size);
are not compatible.
From what I have been able to determine, gcc has a hack for the special case of operator new to ignore the differences in (at least) the two cases I show above. But can users expect all compilers to quietly ignore the incompatibility? The blanket permission to add any explicit exception specification could cause a problem for any user-overridable function. Different implementations could provide incompatible specifications, making portable code impossible to write.[2016-03, Jacksonville]
STL: Core changes to remove dynamic exception specs would make this moot
Room: This is on track to be resolved by P0003, or may be moot.
Proposed resolution:
Section: 20.14.12.2 [func.wrap.func] Status: Open Submitter: Pablo Halpern Opened: 2014-02-27 Last modified: 2016-01-04
Priority: 3
View other active issues in [func.wrap.func].
View all other issues in [func.wrap.func].
View all issues with Open status.
Discussion:
The following constructors in 20.14.12.2 [func.wrap.func] are declared noexcept, even though it is not possible for an implementation to guarantee that they will not throw:
template <class A> function(allocator_arg_t, const A&) noexcept; template <class A> function(allocator_arg_t, const A&, nullptr_t) noexcept;
In addition, the following functions are guaranteed not to throw if the target is a function pointer or a reference_wrapper:
template <class A> function(allocator_arg_t, const A& a, const function& f); template <class F, class A> function(allocator_arg_t, const A& a, F f);
In all of the above cases, the function object might need to allocate memory (an operation that can throw) in order to hold a copy of the type-erased allocator itself. The first two constructors produce an empty function object, but the allocator is still needed in case the object is later assigned to. In this case, we note that the propagation of allocators on assignment is underspecified for std::function. There are three possibilities:
The allocator is never copied on copy-assignment, moved on move-assignment, or swapped on swap.
The allocator is always copied on copy-assignment, moved on move-assignment, and swapped on swap.
Whether or not the allocator is copied, moved, or swapped is determined at run-time based on the propagate_on_container_copy_assignment and propagate_on_container_move_assignment traits of the allocators at construction of the source function, the target function, or both.
Although the third option seems to be the most consistent with existing wording in the containers section of the standard, it is problematic in a number of respects. To begin with, the propagation behavior is determined at run time based on a pair of type-erased allocators, instead of at compile time. Such run-time logic is not consistent with the rest of the standard and is hard to reason about. Additionally, there are two allocator types involved, rather than one. Any set of rules that attempts to rationally interpret the propagation traits of both allocators is likely to be arcane at best, and subtly wrong for some set of codes at worst.
The second option is a non-starter. Historically, and in the vast majority of existing code, an allocator does not change after an object is constructed. The second option, if adopted, would undermine the programmer's ability to construct, e.g., an array of function objects, all using the same allocator.
The first option is (in Pablo's opinion) the simplest and best. It is consistent with historical use of allocators, is easy to understand, and requires minimal wording. It is also consistent with the wording in N3916, which formalizes type-erased allocators.
For cross-referencing purposes: The resolution of this issue should be harmonized with any resolution to LWG 2062, which questions the noexcept specification on the following member functions of std::function:
template <class F> function& operator=(reference_wrapper<F>) noexcept; void swap(function&) noexcept;
[2015-05, Lenexa]
MC: change to P3 and status to open.
STL: note that noexcept is an issue and large chunks of allocator should be destroyed.[2015-12-16, Daniel comments]
See 2564 for a corresponding issue addressing library fundamentals v2.
Proposed resolution:
This wording is relative to N3936.
Change 20.14.12.2 [func.wrap.func], class template function synopsis, as indicated:
template <class A> function(allocator_arg_t, const A&)noexcept; template <class A> function(allocator_arg_t, const A&, nullptr_t)noexcept;
Change 20.14.12.2.1 [func.wrap.func.con] as indicated:
-1- When any function constructor that takes a first argument of type allocator_arg_t is invoked, the second argument shall have a type that conforms to the requirements for Allocator (Table 17.6.3.5). A copy of the allocator argument is used to allocate memory, if necessary, for the internal data structures of the constructed function object. For the remaining constructors, an instance of allocator<T>, for some suitable type T, is used to allocate memory, if necessary, for the internal data structures of the constructed function object.
function() noexcept; template <class A> function(allocator_arg_t, const A&)noexcept;-2- Postconditions: !*this.
function(nullptr_t) noexcept; template <class A> function(allocator_arg_t, const A&, nullptr_t)noexcept;-3- Postconditions: !*this.
function(const function& f);template <class A> function(allocator_arg_t, const A& a, const function& f);-4- Postconditions: !*this if !f; otherwise, *this targets a copy of f.target().
-5- Throws: shall not throw exceptions if f's target is a callable object passed via reference_wrapper or a function pointer. Otherwise, may throw bad_alloc or any exception thrown by the copy constructor of the stored callable object. [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable 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]
template <class A> function(allocator_arg_t, const A& a, const function& f);-?- Postconditions: !*this if !f; otherwise, *this targets a copy of f.target().
function(function&& f); template <class A> function(allocator_arg_t, const A& a, function&& f);-6- Effects: If !f, *this has no target; otherwise, move-constructs the target of f into the target of *this, leaving f in a valid state with an unspecified value. If an allocator is not specified, the constructed function will use the same allocator as f.
template<class F> function(F f); template <class F, class A> function(allocator_arg_t, const A& a, F f);-7- Requires: F shall be CopyConstructible.
-8- Remarks: These constructors shall not participate in overload resolution unless f is Callable (20.9.11.2) for argument types ArgTypes... and return type R.-9- Postconditions: !*this if any of the following hold:
f is a null function pointer value.
f is a null member pointer value.
F is an instance of the function class template, and !f
-10- Otherwise, *this targets a copy of f initialized with std::move(f). [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable 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]
-11- Throws: shall not throw exceptions when an allocator is not specified and f is a function pointer or a reference_wrapper<T> for some T. Otherwise, may throw bad_alloc or any exception thrown by F's copy or move constructor or by A's allocate function.
Section: 24.2.1 [iterator.requirements.general] Status: New Submitter: Marshall Clow Opened: 2014-03-25 Last modified: 2015-04-08
Priority: 3
View all issues with New status.
Discussion:
24.2.1 [iterator.requirements.general] p9 says:
Destruction of an iterator may invalidate pointers and references previously obtained from that iterator.
But the resolution of LWG issue 2360 specifically advocates returning *--temp; where temp is a local variable.
And 24.2.5 [forward.iterators] p6 says:If a and b are both dereferenceable, then a == b if and only if *a and *b are bound to the same object.
which disallows "stashing" iterators (i.e, iterators that refer to data inside themselves).
So, I suspect that the restriction in p9 should only apply to input iterators, and can probably be moved into 24.2.3 [input.iterators] instead of 24.2.1 [iterator.requirements.general].[2014-05-22, Daniel comments]
Given that forward iterators (and beyond) are refinements of input iterator, moving this constraint to input iterators won't help much because it would still hold for all refined forms.
Proposed resolution:
Section: 22.4.2.1.2 [facet.num.get.virtuals] Status: New Submitter: Marshall Clow Opened: 2014-04-30 Last modified: 2016-04-15
Priority: 2
View all other issues in [facet.num.get.virtuals].
View all issues with New status.
Discussion:
In 22.4.2.1.2 [facet.num.get.virtuals] we have:
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>:
For a signed integer value, the function strtoll.
For an unsigned integer value, the function strtoull.
For a floating-point value, the function strtold.
This implies that for many cases, this routine should return true:
bool is_same(const char* p) { std::string str{p}; double val1 = std::strtod(str.c_str(), nullptr); std::stringstream ss(str); double val2; ss >> val2; return std::isinf(val1) == std::isinf(val2) && // either they're both infinity std::isnan(val1) == std::isnan(val2) && // or they're both NaN (std::isinf(val1) || std::isnan(val1) || val1 == val2); // or they're equal }
and this is indeed true, for many strings:
assert(is_same("0")); assert(is_same("1.0")); assert(is_same("-1.0")); assert(is_same("100.123")); assert(is_same("1234.456e89"));
but not for others
assert(is_same("0xABp-4")); // hex float assert(is_same("inf")); assert(is_same("+inf")); assert(is_same("-inf")); assert(is_same("nan")); assert(is_same("+nan")); assert(is_same("-nan")); assert(is_same("infinity")); assert(is_same("+infinity")); assert(is_same("-infinity"));
These are all strings that are correctly parsed by std::strtod, but not by the stream extraction operators. They contain characters that are deemed invalid in stage 2 of parsing.
If we're going to say that we're converting by the rules of strtold, then we should accept all the things that strtold accepts.[2016-04, Issues Telecon]
People are much more interested in round-tripping hex floats than handling inf and nan. Priority changed to P2.
Marshall says he'll try to write some wording, noting that this is a very closely specified part of the standard, and has remained unchanged for a long time. Also, there will need to be a sample implementation.
Proposed resolution:
Section: 20.17.5.8 [time.duration.literals] Status: Open Submitter: Jonathan Wakely Opened: 2014-05-16 Last modified: 2015-04-08
Priority: 3
View all issues with Open status.
Discussion:
20.17.5.8 [time.duration.literals] p3 says:
If any of these suffixes are applied to an integer literal and the resulting chrono::duration value cannot be represented in the result type because of overflow, the program is ill-formed.
Ill-formed requires a diagnostic at compile-time, but there is no way to detect the overflow from unsigned long long to the signed duration<>::rep type.
Overflow could be detected if the duration integer literals were literal operator templates, otherwise overflow can either be undefined or a run-time error, not ill-formed.[Urbana 2014-11-07: Move to Open]
Proposed resolution:
Section: 17.3.16 [defns.ntcts], 22.3.1.1.1 [locale.category], 27.2.2 [iostreams.limits.pos], 27.7.3.6.1 [ostream.formatted.reqmts], 27.7.3.6.4 [ostream.inserters.character] Status: New Submitter: Jeffrey Yasskin Opened: 2014-06-01 Last modified: 2015-09-25
Priority: 3
View all issues with New status.
Discussion:
The term "character type" is used in 17.3.16 [defns.ntcts], 22.3.1.1.1 [locale.category], 27.2.2 [iostreams.limits.pos], 27.7.3.6.1 [ostream.formatted.reqmts], and 27.7.3.6.4 [ostream.inserters.character], but the core language only defines "narrow character types" (3.9.1 [basic.fundamental]).
"wide-character type" is used in 22.5 [locale.stdcvt], but the core language only defines a "wide-character set" and "wide-character literal".Proposed resolution:
Section: 22.3.1.3 [locale.members] Status: New Submitter: Richard Smith Opened: 2014-06-09 Last modified: 2015-04-08
Priority: 3
View all other issues in [locale.members].
View all issues with New status.
Discussion:
22.3.1.3 [locale.members] p5 says:
Returns: The name of *this, if it has one; otherwise, the string "*". If *this has a name, then locale(name().c_str()) is equivalent to *this. Details of the contents of the resulting string are otherwise implementation-defined.
So… what is implementation-defined here, exactly? The first sentence completely defines the behavior of this function in all cases.
Also, the second sentence says (effectively) that all locales with the same name are equivalent: given L1 and L2 that have the same name N, they are both equivalent to locale(N), and since there is no definition of "equivalent" specific to locale, I assume it's the normal transitive equivalence property, which would imply that L1 is equivalent to L2. I'm not sure why this central fact is in the description of locale::name, nor why it's written in this roundabout way.Proposed resolution:
Section: 18.7.2 [type.info] Status: Open Submitter: Stephan T. Lavavej Opened: 2014-06-14 Last modified: 2016-06-08
Priority: 2
View all other issues in [type.info].
View all issues with Open status.
Discussion:
type_info's destructor is depicted as being virtual, which is nearly unobservable to users (since they can't construct or copy this class, they can't usefully derive from it). However, it's technically observable (via is_polymorphic and has_virtual_destructor). It also imposes real costs on implementations, requiring them to store one vptr per type_info object, when RTTI space consumption is a significant concern.
Making this implementation-defined wouldn't affect users (who can observe this only if they're specifically looking for it) and wouldn't affect implementations who need virtual here, but it would allow other implementations to drop virtual and improve their RTTI space consumption. Richard Smith: It's observable in a few other ways.std::map<void*, something> m; m[dynamic_cast<void*>(&typeid(blah))] = stuff;
... is broken by this change, because you can't dynamic_cast a non-polymorphic class type to void*.
type_info& f(); typeid(f());
... evaluates f() at runtime without this change, and might not do so with this change.
These are probably rare things, but I can imagine at least some forms of the latter being used in SFINAE tricks.[Lenexa 2015-05-05: Move to Open]
Marshall to poll LEWG for their opinion
[June 2016]
On the reflector, STL wrote:
> We'll prototype this change and report back with data in the future.
Proposed resolution:
This wording is relative to N3936.
Change 18.7.2 [type.info] as indicated:
namespace std { class type_info { public:virtualsee below ~type_info(); […] }; }-1- The class type_info describes type information generated by the implementation. Objects of this class effectively store a pointer to a name for the type, and an encoded value suitable for comparing two types for equality or collating order. The names, encoding rule, and collating sequence for types are all unspecified and may differ between programs. Whether ~type_info() is virtual is implementation-defined.
Section: 19.3 [assertions] Status: New Submitter: David Krauss Opened: 2014-06-25 Last modified: 2015-04-08
Priority: 4
View all other issues in [assertions].
View all issues with New status.
Discussion:
When NDEBUG is defined, assert must expand exactly to the token sequence ((void)0), with no whitespace (C99 §7.2/1 and also C11 §7.2/1). This is a lost opportunity to pass the condition along to the optimizer.
The user may observe the token sequence using the stringize operator or discriminate it by making a matching #define directive. There is little chance of practical code doing such things. It's reasonable to allow any expansion that is a void expression with no side effects or semantic requirements, for example, an extension keyword or an attribute-specifier finagled into the context. Conforming optimizations would still be limited to treating the condition as hint, not a requirement. Nonconformance on this point is quite reasonable though, given user preferences. Anyway, it shouldn't depend on preprocessor quirks. As for current practice, Darwin OS <assert.h> provides a GCC-style compiler hint __builtin_expect but only in debug mode. Shouldn't release mode preserve hints? Daniel: The corresponding resolution should take care not to conflict with the intention behind LWG 2234.Proposed resolution:
Section: 17.6.5.8 [reentrancy] Status: Open Submitter: Stephan T. Lavavej Opened: 2014-07-01 Last modified: 2015-09-27
Priority: 3
View all other issues in [reentrancy].
View all issues with Open status.
Discussion:
N3936 17.6.5.8 [reentrancy]/1 talks about "functions", but that doesn't address the scenario of calling different member functions of a single object. Member functions often have to violate and then re-establish invariants. For example, vectors often have "holes" during insertion, and element constructors/destructors/etc. shouldn't be allowed to observe the vector while it's in this invariant-violating state. The [reentrancy] Standardese should be extended to cover member functions, so that implementers can either say that member function reentrancy is universally prohibited, or selectively allowed for very specific scenarios.
(For clarity, this issue has been split off from LWG 2382.)[2014-11-03 Urbana]
AJM confirmed with SG1 that they had no special concerns with this issue, and LWG should retain ownership.
AM: this is too overly broad as it also covers calling the exact same member function on a different objectMove to Open
[2015-07 Telecom Urbana]
Marshall to ping STL for updated wording.
Proposed resolution:
This wording is relative to N3936.
Change 17.6.5.8 [reentrancy] p1 as indicated:
-1- Except where explicitly specified in this standard, it is implementation-defined which functions (including different member functions called on a single object) in the Standard C++ library may be recursively reentered.
Section: 20.10.5 [ptr.align] Status: New Submitter: Melissa Mears Opened: 2014-08-06 Last modified: 2015-04-08
Priority: 3
View all other issues in [ptr.align].
View all issues with New status.
Discussion:
The specification of std::align does not appear to specify what happens when the value of the size parameter is 0. (The question of what happens when alignment is 0 is mentioned in another Defect Report, 2377; it would change the behavior to be undefined rather than potentially implementation-defined.)
The case of size being 0 is interesting because the result is ambiguous. Consider the following code's output:#include <cstdio> #include <memory> int main() { alignas(8) char buffer[8]; void *ptr = &buffer[1]; std::size_t space = sizeof(buffer) - sizeof(char[1]); void *result = std::align(8, 0, ptr, space); std::printf("%d %td\n", !!result, result ? (static_cast<char*>(result) - buffer) : std::ptrdiff_t(-1)); }
There are four straightforward answers as to what the behavior of std::align with size 0 should be:
The behavior is undefined because the size is invalid.
The behavior is implementation-defined. This seems to be the status quo, with current implementations using #3.
Act the same as size == 1, except that if size == 1 would fail but would be defined and succeed if space were exactly 1 larger, the result is a pointer to the byte past the end of the ptr buffer. That is, the "aligned" version of a 0-byte object can be one past the end of an allocation. Such pointers are, of course, valid when not dereferenced (and a "0-byte object" shouldn't be), but whether that is desired is not specified in the Standard's definition of std::align, it appears. The output of the code sample is "1 8" in this case.
Act the same as size == 1; this means that returning "one past the end" is not a possible result. In this case, the code sample's output is "0 -1".
The two compilers I could get working with std::align, Visual Studio 2013 and Clang 3.4, implement #3. (Change %td to %Id on Visual Studio 2013 and earlier. 2014 and later will have %td.)
Proposed resolution:
Section: 26.7.5 [template.slice.array], 26.7.7 [template.gslice.array], 26.7.8 [template.mask.array], 26.7.9 [template.indirect.array] Status: New Submitter: Akira Takahashi Opened: 2014-08-12 Last modified: 2015-04-08
Priority: 4
View all other issues in [template.slice.array].
View all issues with New status.
Discussion:
I found a missing specification of the copy constructor of the following class templates:
slice_array (26.7.5 [template.slice.array])
gslice_array (26.7.7 [template.gslice.array])
mask_array (26.7.8 [template.mask.array])
indirect_array (26.7.9 [template.indirect.array])
Proposed resolution:
Before 26.7.5.2 [slice.arr.assign] insert a new sub-clause as indicated:
-?- slice_array constructors [slice.arr.cons]
slice_array(const slice_array&);-?- Effects: The constructed slice refers to the same valarray<T> object to which the argument slice refers.
Before 26.7.7.2 [gslice.array.assign] insert a new sub-clause as indicated:
-?- gslice_array constructors [gslice.array.cons]
gslice_array(const gslice_array&);-?- Effects: The constructed slice refers to the same valarray<T> object to which the argument slice refers.
Before 26.7.8.2 [mask.array.assign] insert a new sub-clause as indicated:
-?- mask_array constructors [mask.array.cons]
mask_array(const mask_array&);-?- Effects: The constructed slice refers to the same valarray<T> object to which the argument slice refers.
Before 26.7.9.2 [indirect.array.assign] insert a new sub-clause as indicated:
-?- indirect_array constructors [indirect.array.cons]
indirect_array(const indirect_array&);-?- Effects: The constructed slice refers to the same valarray<T> object to which the argument slice refers.
Section: 29.5 [atomics.types.generic] Status: Open Submitter: Jens Maurer Opened: 2014-08-14 Last modified: 2016-03-06
Priority: 2
View other active issues in [atomics.types.generic].
View all other issues in [atomics.types.generic].
View all issues with Open status.
Discussion:
Otherwise, one could use memcpy to save and restore the value according to 3.9p2.
It seems the core language rules in 9 [class]p6 with 12.8 [class.copy]p12 (trivial copy constructor) etc. and 8.4.2 [dcl.fct.def.default]p5 (user-provided) say that the atomic types are trivially copyable, which is bad. We shouldn't rely on future core changes in that area and simply say in the library section 29.5 [atomics.types.generic] that these very special types are not trivially copyable.[2014-11 Urbana]
Lawrence:Definition of "trivially copyable" has been changing.
Doesn't hurt to add proposed change, even if the sentence is redundant
Move to Review.
[2015-02 Cologne]
GR has a minor problem with the style of the wording. VV has major issues with implementability.
[2015-03-22, Jens Maurer responses to Cologne discussion]
A library implementation could provide a partial specialization for is_trivially_copyable<atomic<T>>, to ensure that any such type query would return false.
Assuming such a specialization would be provided, how could a conforming program observe that per language rules an atomic specialization would actually be trivially copyable if there is no way to call the (deleted) copy constructor or copy assignment operator? The sole effect of the suggested addition of the constraining sentence is that it would make a user program non-conforming that attempts to invoke memcpy (and the like) on atomic types, since that would invoke undefined behaviour.[2015-05 Lenexa, SG1 response]
SG1 is fine with P/R (and agrees it's needed), but LWG may want to check the details; it's not entirely an SG1 issue.
[2015-05-05 Lenexa]
Marshall: This was discussed on the telecon. Alisdair was going to write something to Mike and send it to Core.
Hwrd: Core says that deleted copies are trivially copyable, which makes no sense to Library people.
STL: There doesn't appear to be a Core issue about it.
[2015-09-11 Telecom]
Howard: currently std::is_trivially_copyable<std::atomic> is true, so this resolution would contradict reality
Jonathan: changing that is good, we don't want it to be trivially copyable, otherwise users can memcpy them, which we really don't want
Howard: is it reasonable to memcpy something that isn't trivially copy constructible or trivially assignable?
Jonathan: no, it's not, but Core says you can, so this resolution is needed to stop people memcpying atomic
Howard: we should fix the core rule
Marshall: there is a separate issue of whether trivially-copyable makes sense, but this resolution is a net good purely because it stops memcpy of atomics
Howard: so should implementations specialize is_trivially_copyable the trait to meet this?
Jonathan: or add an empty, user-defined destructor.
Howard: should the spec specify that then?
Over-specification.
Howard: without that I fear implementation divergence.
Ville and Jonathan to investigate potential implementation options.
Ville: request a note on the issue saying we need review other types such as condition_variable to see if they are also unintentionally trivially-copyable. N4460 mentions some such types.
[2016-03 Jacksonville]
We think there is something coming from Core to resolve that, and that this will be NAD.
Until then, defer.
Proposed resolution:
Change 29.5 [atomics.types.generic]p3 as indicated:
Specializations and instantiations of the atomic template shall have a deleted copy constructor, a deleted copy assignment operator, and a constexpr value constructor. They are not trivially copyable types (3.9 [basic.types]).
Section: 28.3 [re.req] Status: New Submitter: Jonathan Wakely Opened: 2014-09-30 Last modified: 2015-04-08
Priority: 3
View all issues with New status.
Discussion:
The requirements on the traits class in 28.3 [re.req] do not say whether a regular expression traits class is required to be DefaultConstructible, CopyConstructible, CopyAssignable etc.
The std::regex_traits class appears to be all of the above, but can basic_regex assume that for user-defined traits classes? Should the following statements all leave u in equivalent states?X u{v}; X u; u = v; X u; u.imbue(v.getloc();
Whether they are equivalent has implications for basic_regex copy construction and assignment.
Proposed resolution:
Section: 18.9 [support.initlist] Status: EWG Submitter: David Krauss Opened: 2014-09-30 Last modified: 2015-05-22
Priority: 2
View other active issues in [support.initlist].
View all other issues in [support.initlist].
View all issues with EWG status.
Discussion:
std::initializer_list::operator= 18.9 [support.initlist] is horribly broken and it needs deprecation:
std::initializer_list<foo> a = {{1}, {2}, {3}}; a = {{4}, {5}, {6}}; // New sequence is already destroyed.
Assignability of initializer_list isn't explicitly specified, but most implementations supply a default assignment operator. I'm not sure what 17.5 [description] says, but it probably doesn't matter.
[Lenexa 2015-05-05: Send to EWG as discussed in Telecon]
Proposed resolution:
Edit 18.9 [support.initlist] p1, class template initializer_list synopsis, as indicated:
namespace std { template<class E> class initializer_list { public: […] constexpr initializer_list() noexcept; initializer_list(const initializer_list&) = default; initializer_list(initializer_list&&) = default; initializer_list& operator=(const initializer_list&) = delete; initializer_list& operator=(initializer_list&&) = delete; constexpr size_t size() const noexcept; […] }; […] }
Section: 25.5.6.4 [sort.heap] Status: Open Submitter: François Dumont Opened: 2014-10-07 Last modified: 2015-05-22
Priority: 3
View all issues with Open status.
Discussion:
While creating complexity tests for the GNU libstdc++ implementation I stumbled across a surprising requirement for the std::sort_heap algorithm.
In 25.5.6.4 [sort.heap] p3 the Standard states:Complexity: At most N log(N) comparisons (where N == last - first).
As stated on the libstdc++ mailing list by Marc Glisse sort_heap can be implemented by N calls to pop_heap. As max number of comparisons of pop_heap is 2 * log(N) then sort_heap max limit should be 2 * log(1) + 2 * log(2) + .... + 2 * log(N) that is to say 2 * log(N!). In terms of log(N) we can also consider that this limit is also cap by 2 * N * log(N) which is surely what the Standard wanted to set as a limit.
This is why I would like to propose to replace paragraph 3 by:Complexity: At most 2N log(N) comparisons (where N == last - first).
[2015-02 Cologne]
Marshall will research the maths and report back in Lenexa.
[2015-05-06 Lenexa]
STL: I dislike exact complexity requirements, they prevent one or two extra checks in debug mode. Would it be better to say O(N log(N)) not at most?
Proposed resolution:
This wording is relative to N3936.
In 25.5.6.4 [sort.heap] p3 the Standard states:
template<class RandomAccessIterator> void sort_heap(RandomAccessIterator first, RandomAccessIterator last); template<class RandomAccessIterator, class Compare> void sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);[…]
-3- Complexity: At most 2N log(N) comparisons (where N == last - first).
Section: 23.3.11.5 [vector.modifiers] Status: New Submitter: Marc Glisse Opened: 2014-10-21 Last modified: 2015-04-08
Priority: 3
View other active issues in [vector.modifiers].
View all other issues in [vector.modifiers].
View all issues with New status.
Discussion:
this issue is based on the discussion here.
23.3.11.5 [vector.modifiers] says about vector::insert: "If no reallocation happens, all the iterators and references before the insertion point remain valid." This doesn't seem to guarantee anything about the iterator at the point of insertion. The question comes from people asking if the following is valid, assuming a sufficient call to reserve() was done first:v.insert(v.end(), v.begin(), v.end());
It could fail for an implementation using a sentinel for the end of the vector, but I don't know of any (it would be quite inconvenient). And for any implementation using a simple position as iterator (pointer (possibly in a wrapper), or base+offset), this is needlessly restrictive. The fact that this alternative:
v.insert(v.end(), &v[0], &v[0]+v.size())
is arguably valid (again assuming a large enough reserve()) makes it a bit confusing that the first version isn't (23.2.3 [sequence.reqmts] has a precondition that iterator arguments to insert() do not point into the sequence, but vector::insert is more refined and seems to give enough guarantees that it cannot fail).
Then we might as well say that vector iterators act as positions, and that after a reallocation-free operation an iterator points to the same position, whatever may be there now…Proposed resolution:
Section: 20.15 [meta] Status: Core Submitter: Hubert Tong Opened: 2014-11-04 Last modified: 2015-10-20
Priority: 3
View other active issues in [meta].
View all other issues in [meta].
Discussion:
The BaseCharacteristic for is_constructible is defined in terms of the well-formedness of a declaration for an invented variable. The well-formedness of the described declaration itself may change for the same set of arguments because of the introduction of default arguments.
In the following program, there appears to be conflicting definitions of a specialization of std::is_constructible; however, it seems that this situation is caused without a user violation of the library requirements or the ODR. There is a similar issue with is_convertible, result_of and others. a.cc:#include <type_traits> struct A { A(int, int); }; const std::false_type& x1 = std::is_constructible<A, int>(); int main() { }
b.cc:
#include <type_traits> struct A { A(int, int); }; inline A::A(int, int = 0) { } const std::true_type& x2 = std::is_constructible<A, int>();
Presumably this program should invoke undefined behaviour, but the Library specification doesn't say that.
[2015-02 Cologne]
Core wording should say "this kind of thing is ill-formed, no diagnostic required"
Proposed resolution:
Section: 18.9 [support.initlist], 24.7 [iterator.range], 24.8 [iterator.container] Status: New Submitter: Richard Smith Opened: 2014-11-11 Last modified: 2016-02-12
Priority: 3
View other active issues in [support.initlist].
View all other issues in [support.initlist].
View all issues with New status.
Discussion:
These sections define helper functions, some of which apply to initializer_list<T>. And they're available if you include one of a long list of header files, many of which include <initializer_list>. But they are not available if you include <initializer_list>. This seems very odd.
#include <initializer_list> auto x = {1, 2, 3}; const int *p = data(x); // error, undeclared #include <vector> const int *q = data(x); // ok
Proposed resolution:
Section: 24.7 [iterator.range] Status: New Submitter: Janez Žemva Opened: 2014-11-16 Last modified: 2015-04-08
Priority: 3
View all other issues in [iterator.range].
View all issues with New status.
Discussion:
The following code:
#include <algorithm> #include <iterator> #include <iostream> #include <cassert> int main() { int a[2][3][4] = { { { 1, 2, 3, 4}, { 5, 6, 7, 8}, { 9, 10, 11, 12} }, { {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } }; int b[2][3][4]; assert(std::distance(std::begin(a), std::end(a)) == 2 * 3 * 4); std::copy(std::begin(a), std::end(a), std::begin(b)); std::copy(std::begin(b), std::end(b), std::ostream_iterator<int>(std::cout, ",")); }
does not compile.
A possible way to remedy this would be to add the following overloads of begin, end, rbegin, and rend to 24.7 [iterator.range], relying on recursive evaluation:namespace std { template <typename T, size_t M, size_t N> constexpr remove_all_extents_t<T>* begin(T (&array)[M][N]) { return begin(*array); } template <typename T, size_t M, size_t N> constexpr remove_all_extents_t<T>* end(T (&array)[M][N]) { return end(array[M - 1]); } template <typename T, size_t M, size_t N> reverse_iterator<remove_all_extents_t<T>*> rbegin(T (&array)[M][N]) { return decltype(rbegin(array))(end(array[M - 1])); } template <typename T, size_t M, size_t N> reverse_iterator<remove_all_extents_t<T>*> rend(T (&array)[M][N]) { return decltype(rend(array))(begin(*array)); } }
Proposed resolution:
Section: 20.15.7.6 [meta.trans.other], 24.4.1 [iterator.traits] Status: New Submitter: Richard Smith Opened: 2014-11-19 Last modified: 2015-04-08
Priority: 2
View other active issues in [meta.trans.other].
View all other issues in [meta.trans.other].
View all issues with New status.
Discussion:
LWG issue 2408 changes the meat of the specification of common_type to compute:
[..] the type, if any, of an unevaluated conditional expression (5.16) whose first operand is an arbitrary value of type bool, whose second operand is an xvalue of type T1, and whose third operand is an xvalue of type T2.
This has an effect on the specification that I think was unintended. It used to be the case that common_type<T&, U&&> would consider the type of a conditional between an lvalue of type T and an xvalue of type U. It's now either invalid (because there is no such thing as an xvalue of reference type) or considers the type of a conditional between an xvalue of type T and an xvalue of type U, depending on how you choose to read it.
Put another way, this has the effect of changing the usual definition from:typedef decay_t<decltype(true ? declval<T>() : declval<U>())> type;
to:
typedef decay_t<decltype(true ? declval<remove_reference_t<T>>() : declval<remove_reference_t<U>>())> type;
It also makes common_type underspecified in the case where one of the operands is of type void; in that case, the resulting type depends on whether the expression is a throw-expression, which is not specified (but used to be).
Also on the subject of this wording: the changes to 24.4.1 [iterator.traits] say that iterator_traits<T> "shall have no members" in some cases. That's wrong. It's a class type; it always has at least a copy constructor, a copy assignment operator, and a destructor. Plus this removes the usual library liberty to add additional members with names that don't collide with normal usage (for instance, if a later version of the standard adds members, they can't be present here as a conforming extension). Perhaps this should instead require that the class doesn't have members with any of those five names? That's what 2408 does for common_type's type member.Proposed resolution:
Section: 17.6.3.5 [allocator.requirements], 23.3.11.3 [vector.capacity], 23.3.11.5 [vector.modifiers] Status: New Submitter: dyp Opened: 2014-12-06 Last modified: 2015-06-12
Priority: 3
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with New status.
Discussion:
When resizing a vector, the accessibility and exception specification of the value type's constructors determines whether the elements are copied or moved to the new buffer. However, the copy/move is performed via the allocator's construct member function, which is assumed, but not required, to call the copy/move constructor and propagate only exceptions from the value type's copy/move constructor. The issue might also affect other classes.
The current wording in N4296 relevant here is from Table 28 — "Allocator requirements" in 17.6.3.5 [allocator.requirements]:
Table 28 — Allocator requirements Expression Return type Assertion/note
pre-/post-conditionDefault … a.construct(c, args) (not used) Effect: Constructs an object of type C at c ::new ((void*)c) C(forward<Args>(args)...) …
and from 17.6.3.5 [allocator.requirements] p9:
An allocator may constrain the types on which it can be instantiated and the arguments for which its construct member may be called. If a type cannot be used with a particular allocator, the allocator class or the call to construct may fail to instantiate.
I conclude the following from the wording:
The allocator is not required to call the copy constructor if the arguments (args) is a single (potentially const) lvalue of the value type. Similarly for a non-const rvalue + move constructor. See also 23.2.1 [container.requirements.general] p15 which seems to try to require this, but is not sufficient: That paragraph specifies the semantics of the allocator's operations, but not which constructors of the value type are used, if any.
The allocator may throw exceptions in addition to the exceptions propagated by the constructors of the value type; it can also propagate exceptions from constructors other than a copy/move constructor.
This leads to an issue with the wording of the exception safety guarantees for vector modifiers in 23.3.11.5 [vector.modifiers] p1:
[…]
void push_back(const T& x); void push_back(T&& x);Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T or by any InputIterator operation there are no effects. If an exception is thrown while inserting a single element at the end and T is CopyInsertable or is_nothrow_move_constructible<T>::value is true, there are no effects. Otherwise, if an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.
The wording leads to the following problem: Copy and move assignment are invoked directly from vector. For intermediary objects (see 2164), vector also directly invokes the copy and move constructor of the value type. However, construction of the actual element within the buffer is invoked via the allocator abstraction. As discussed above, the allocator currently is not required to call a copy/move constructor. If is_nothrow_move_constructible<T>::value is true for some value type T, but the allocator uses modifying operations for MoveInsertion that do throw, the implementation is required to ensure that "there are no effects", even if the source buffer has been modified.
Similarly, the vector capacity functions specify exception safety guarantees referring to the move constructor of the value type. For example, vector::resize in 23.3.11.3 [vector.capacity] p14:Remarks: If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.
The wording leads to the same issue as described above.
Code example:template<class T> class allocator; class pot_reg_type // a type which creates // potentially registered instances { private: friend class allocator<pot_reg_type>; struct register_t {}; static std::set<pot_reg_type*>& get_registry() { static std::set<pot_reg_type*> registry; return registry; } void enregister() noexcept(false) { get_registry().insert(this); } void deregister() { get_registry().erase(this); } public: pot_reg_type(void ) noexcept(true) {} pot_reg_type(pot_reg_type const&) noexcept(true) {} pot_reg_type(pot_reg_type&& ) noexcept(true) {} private: pot_reg_type(register_t ) noexcept(false) { enregister(); } pot_reg_type(register_t, pot_reg_type const&) noexcept(false) { enregister(); } pot_reg_type(register_t, pot_reg_type&& ) noexcept(false) { enregister(); } }; template<class T> class allocator { public: using value_type = T; value_type* allocate(std::size_t p) { return (value_type*) ::operator new(p); } void deallocate(value_type* p, std::size_t) { ::operator delete(p); } void construct(pot_reg_type* pos) { new((void*)pos) pot_reg_type((pot_reg_type::register_t())); } void construct(pot_reg_type* pos, pot_reg_type const& source) { new((void*)pos) pot_reg_type(pot_reg_type::register_t(), source); } template<class... Args> void construct(T* p, Args&&... args) { new((void*)p) T(std::forward<Args>(args)...); } };
The construct member function template is only required for rebinding, which can be required e.g. to store additional debug information in the allocated memory (e.g. VS2013).
Even though the value type has an accessible and noexcept(true) move constructor, this allocator won't call that constructor for rvalue arguments. In any case, it does not call a constructor for which vector has formulated its requirements. An exception thrown by a constructor called by this allocator is not covered by the specification in 23.3.11.5 [vector.modifiers] and therefore is guaranteed not to have any effect on the vector object when resizing. For an example how this might invalidate the exception safety guarantees, see this post on the std-discussion mailing list. Another problem arises for value types whose constructors are private, but may be called by the allocator e.g. via friendship. Those value types are not MoveConstructible (is_move_constructible is false), yet they can be MoveInsertable. It is not possible for vector to create intermediary objects (see 2164) of such a type by directly using the move constructor. Current implementations of the single-element forms of vector::insert and vector::emplace do create intermediary objects by directly calling one of the value type's constructors, probably to allow inserting objects from references that alias other elements of the container. As far as I can see, Table 100 — "Sequence container requirements" in 23.2.3 [sequence.reqmts] does not require that the creation of such intermediare objects can be performed by containers using the value type's constructor directly. It is unclear to me if the allocator's construct function could be used to create those intermediary objects, given that they have not been allocated by the allocator. Two possible solutions:Add the following requirement to the allocator_traits::construct function: If the parameter pack args consists of a single parameter of the type value_type&&, the function may only propagate exceptions if is_nothrow_move_constructible<value_type>::value is false.
Requiring alloctor_traits::construct to call a true copy/move constructor of the value type breaks std::scoped_allocator_adapter, as pointed out by Casey Carter in a post on the std-discussion mailing list.Change vector's criterion whether to move or copy when resizing:
Instead of testing the value type's constructors via is_move_constructible, check the value of noexcept( allocator_traits<Allocator>::construct(alloc, ptr, rval) ) where alloc is an lvalue of type Allocator, ptr is an expression of type allocator_traits<Allocator>::pointer and rval is a non-const rvalue of type value_type.A short discussion of the two solutions:
Solution 1 allows keeping is_nothrow_move_constructible<value_type> as the criterion for vector to decide between copying and moving when resizing. It restricts what can be done inside the construct member function of allocators, and requires implementers of allocators to pay attention to the value types used. One could conceive allocators checking the following with a static_assert: If the value type is_nothrow_move_constructible, then the constructor actually called for MoveInsertion within the construct member function is also declared as noexcept. Solution 2 requires changing both the implementation of the default allocator (add a conditional noexcept) and vector (replace is_move_constructible with an allocator-targeted check). It does not impose additional restrictions on the allocator (other than 23.2.1 [container.requirements.general] p15), and works nicely even if the move constructor of a MoveInsertable type is private or deleted (the allocator might be a friend of the value type). In both cases, an addition might be required to provide the basic exception safety guarantee. A short discussion on this topic can be found in the std-discussion mailing list. Essentially, if allocator_traits<Allocator>::construct throws an exception, the object may or may not have been constructed. Two solutions are mentioned in that discussion:allocator_traits<Allocator>::construct needs to tell its caller whether or not the construction was successful, in case of an exception.
If allocator_traits<Allocator>::construct propagates an exception, it shall either not have constructed an object at the specified location, or that object shall have been destroyed (or it shall ensure otherwise that no resources are leaked).
[2015-05-23, Tomasz Kamiński comments]
Solution 1 discussed in this issue also breaks support for the polymorphic_allocator proposed in the part of the Library Fundamentals TS v1, in addition to already mentioned std::scoped_allocator_adapter. Furthermore there is unknown impact on the other user-defined state-full allocators code written in the C++11.
In addition the library resolution proposed in the LWG issues 2089 and N4462, will break the relation between the std::allocator_trait::construct method and copy/move constructor even for the standard std::allocator. As example please consider following class:struct NonCopyable { NonCopyable() = default; NonCopyable(NonCopyable const&) = delete; NonCopyable(NonCopyable&&) = delete; }; struct InitListConstructor : NonCopyable { InitListConstructor() = default; InitListConstructor(std::initializer_list<int>); operator int() const; };
For the above declarations following expression are ill-formed:
InitListConstructor copy(std::declval<InitListConstructor const&>()); InitListConstructor move(std::declval<InitListConstructor&&>());
So the class is not CopyConstructible nor MoveConstructible. However the following are well formed:
InitListConstructor copy{std::declval<InitListConstructor const&>()}; InitListConstructor move{std::declval<InitListConstructor&&>()};
And will be used by std::allocator<InitListConstructor>::construct in case of move-insertion and copy-insertion, after appliance of the resolution proposed in mentioned papers:
The gist of the proposed library fix is simple:
if is_constructible_v<TargetType, Args...>, use direct-nonlist-initialization
otherwise, use brace-initialization.
As consequence the requirement proposed in the Solution 1:
If the parameter pack args consists of a single parameter of the type value_type&&, the function may only propagate exceptions if is_nothrow_move_constructible<value_type>::value is false.
Will no longer hold for the std::allocator.
Proposed resolution:
Section: 20.15.7.6 [meta.trans.other] Status: New Submitter: Eric Niebler Opened: 2015-01-12 Last modified: 2015-04-08
Priority: 2
View other active issues in [meta.trans.other].
View all other issues in [meta.trans.other].
View all issues with New status.
Discussion:
I think there's a defect regarding common_type and its specializations. Unless I've missed it, there is nothing preventing folks from instantiating common_type with cv-qualified types or reference types. In fact, the wording in N3797 explicitly mentions cv void, so presumably at least cv qualifications are allowed.
Users are given license to specialize common_type when at least of of the types is user-defined. (A separate issue is the meaning of user-defined. In core, I believe this is any class/struct/union/enum, but in lib, I think it means any type not defined in std, right?) There is at least one place in the standard that specializes common_type (time.traits.specializations) on time_point and duration. But the specializations are only for non-cv-qualified and non-reference specializations of time_point and duration. If the user uses, say, common_type<duration<X,Y> const, duration<A,B> const>, they're not going to get the behavior they expect. Suggest we clarify the requirements of common_type's template parameters. Also, perhaps we can add blanket wording that common_type<A [cv][&], B [cv][&]> is required to be equivalent to common_type<A,B> (if that is in fact the way we intent this to work). Also, the change to make common_type SFINAE-friendly regressed key functionality, as noted by Agustín K-ballo Bergé in c++std-lib-37178. Since decay_t is not applied until the very end of the type computation, user specializations are very likely to to be found. Agustín says:Consider the following snippet:
struct X {}; struct Y { explicit Y(X){} }; namespace std { template<> struct common_type<X, Y> { typedef Y type; }; template<> struct common_type<Y, X> { typedef Y type; }; } static_assert(is_same<common_type_t<X, Y>, Y>()); // (A) static_assert(is_same<common_type_t<X, Y, Y>, Y>()); // (B) static_assert(is_same<common_type_t<X, X, Y>, Y>()); // (C)Under the original wording, all three assertion holds. Under the current wording,
(A) picks the user-defined specialization, so the assertion holds.
(B) goes to the third bullet and, ignoring the user-defined specialization, looks for decltype(true ? declval<X>() : declval<Y>()); since it is ill-formed there is no common type.
(C) goes to the third bullet and yields common_type_t<X&&, Y>, which again misses the user-defined specialization.
The discussion following c++std-lib-35636 seemed to cohere around the idea that the primary common_type specialization should have the effect of stripping top-level ref and cv qualifiers by applying std::decay_t to its arguments and, if any of them change as a result of that transformation, re-dispatching to common_type on those transformed arguments, thereby picking up any user-defined specializations. This change to common_type would make the specializations in time.traits.specializations sufficient.
Suggested wording: I'm afraid I don't know enough to suggest wording. But for exposition, the following is my best shot at implementing the suggested resolution. I believe it also fixes the regression noted by Agustín K-ballo Bergé in c++std-lib-37178.namespace detail { template<typename T, typename U> using default_common_t = decltype(true? std::declval<T>() : std::declval<U>()); template<typename T, typename U, typename Enable = void> struct common_type_if {}; template<typename T, typename U> struct common_type_if<T, U, void_t<default_common_t<T, U>>> { using type = decay_t<default_common_t<T, U>>; }; template<typename T, typename U, typename TT = decay_t<T>, typename UU = decay_t<U>> struct common_type2 : common_type<TT, UU> // Recurse to catch user specializations {}; template<typename T, typename U> struct common_type2<T, U, T, U> : common_type_if<T, U> {}; template<typename Meta, typename Enable = void> struct has_type : std::false_type {}; template<typename Meta> struct has_type<Meta, void_t<typename Meta::type>> : std::true_type {}; template<typename Meta, typename...Ts> struct common_type_recurse : common_type<typename Meta::type, Ts...> {}; template<typename Meta, typename...Ts> struct common_type_recurse_if : std::conditional< has_type<Meta>::value, common_type_recurse<Meta, Ts...>, empty >::type {}; } template<typename ...Ts> struct common_type {}; template<typename T> struct common_type<T> { using type = std::decay_t<T>; }; template<typename T, typename U> struct common_type<T, U> : detail::common_type2<T, U> {}; template<typename T, typename U, typename... Vs> struct common_type<T, U, Vs...> : detail::common_type_recurse_if<common_type<T, U>, Vs...> {};
Proposed resolution:
Section: 17.6.4.9 [res.on.arguments], 17.6.3.1 [utility.arg.requirements], 17.6.5.15 [lib.types.movedfrom], 23.2.1 [container.requirements.general] Status: Open Submitter: Matt Austern Opened: 2015-01-22 Last modified: 2015-11-04
Priority: 2
View all other issues in [res.on.arguments].
View all issues with Open status.
Discussion:
Suppose we write
vector<string> v{"a", "b", "c", "d"}; v = move(v);
What should be the state of v be? The standard doesn't say anything specific about self-move-assignment. There's relevant text in several parts of the standard, and it's not clear how to reconcile them.
17.6.4.9 [res.on.arguments] writes that, for all functions in the standard library, unless explicitly stated otherwise, "If a function argument binds to an rvalue reference parameter, the implementation may assume that this parameter is a unique reference to this argument." The MoveAssignable requirements table in 17.6.3.1 [utility.arg.requirements] writes that, given t = rv, t's state is equivalent to rv's from before the assignment and rv's state is unspecified (but valid). For containers specifically, the requirements table in 23.2.1 [container.requirements.general] says that, given a = rv, a becomes equal to what rv was before the assignment (and doesn't say anything about rv's state post-assignment). Taking each of these pieces in isolation, without reference to the other two:17.6.4.9 [res.on.arguments] would clearly imply that the effect of v = move(v) is undefined.
17.6.3.1 [utility.arg.requirements] would clearly imply that v = move(v) has defined behavior. It might be read to imply that this is a no-op, or might be read to imply that it leaves v in a valid but unspecified state; I'm not sure which reading is more natural.
23.2.1 [container.requirements.general] would clearly imply that v = move(v) is a no-op.
It's not clear from the text how to put these pieces together, because it's not clear which one takes precedence. Maybe 17.6.4.9 [res.on.arguments] wins (it imposes an implicit precondition that isn't mentioned in the MoveAssignable requirements, so v = move(v) is undefined), or maybe 23.2.1 [container.requirements.general] wins (it explicitly gives additional guarantees for Container::operator= beyond what's guaranteed for library functions in general, so v = move(v) is a no-op), or maybe something else.
On the existing implementations that I checked, for what it's worth, v = move(v) appeared to clear the vector; it didn't leave the vector unchanged and it didn't cause a crash. Proposed wording: Informally: change the MoveAssignable and Container requirements tables (and any other requirements tables that mention move assignment, if any) to make it explicit that x = move(x) is defined behavior and it leaves x in a valid but unspecified state. That's probably not what the standard says today, but it's probably what we intended and it's consistent with what we've told users and with what implementations actually do.[2015-10, Kona Saturday afternoon]
JW: So far, the library forbids self-assignment since it assumes that anything bound to an rvalue reference has no aliases. But self-assignment can happen in real code, and it can be implemented. So I want to add an exception to the Standard that this should be allowed and leave the object in a valid-but-unspecified state.
STL: When this is resolved, I want to see a) VBU for library types after self-move, but also b) requirements on user types for self-moves. E.g. should algorithms be required to avoid self-assignments (since a user-defined type might blow up)? HH: In other words, should we require that you can assign from moved-from values.
WEB: What can one generally do with moved-from values?
VV: Call any member function that has no preconditions.
JW: That's certainly the library requirement, and it's also good guidance for user types.
JW: I'm writing wording. I care about this.
Move to Open; Jonathan to provide wording
Proposed resolution:
Section: 25.4.1 [alg.copy] Status: New Submitter: Jonathan Wakely Opened: 2015-01-28 Last modified: 2015-05-04
Priority: 3
View other active issues in [alg.copy].
View all other issues in [alg.copy].
View all issues with New status.
Discussion:
It's unspecified how many times copy_n increments the InputIterator. uninitialized_copy_n is specified to increment it exactly n times, which means if an istream_iterator is used then the next character after those copied is read from the stream and then discarded, losing data.
I believe all three of Dinkumware, libc++ and libstdc++ implement copy_n with n - 1 increments of the InputIterator, which avoids reading and discarding a character when used with istream_iterator, but is inconsistent with uninitialized_copy_n and causes surprising behaviour with istreambuf_iterator instead, because copy_n(in, 2, copy_n(in, 2, out)) is not equivalent to copy_n(in, 4, out)Proposed resolution:
Section: 20.5.2.8 [tuple.rel], 20.10.9.2 [allocator.globals], 20.11.1.5 [unique.ptr.special], 20.11.2.2.7 [util.smartptr.shared.cmp], 20.17.5.6 [time.duration.comparisons], 20.17.6.6 [time.point.comparisons], 20.13.5 [scoped.adaptor.operators], 24.5.1.3.13 [reverse.iter.op==], 24.5.3.3.13 [move.iter.op.comp] Status: New Submitter: Richard Smith Opened: 2015-02-07 Last modified: 2015-05-04
Priority: 3
View other active issues in [tuple.rel].
View all other issues in [tuple.rel].
View all issues with New status.
Discussion:
The standard library specifies a lot of heterogeneous comparison operators. For instance:
template<class... TTypes, class... UTypes> constexpr bool operator!=(const tuple<TTypes...>&, const tuple<UTypes...>&);
This has an unfortunate consequence:
#include <tuple> #include <utility> using namespace std::rel_ops; std::tuple<int> a(0); bool b = a != a;
The last line here is ill-formed due to ambiguity: it might be rel_ops::operator!=, and it might be the heterogeneous tuple operator!=. These are not partially ordered, because they have different constraints: rel_ops requires the types to match, whereas the tuple comparison requires both types to be tuples (but not to match). The same thing happens for user code that defines its own unconstrained 'template<typename T> operator!=(const T&, const T&)' rather than using rel_ops.
One straightforward fix would be to add a homogeneous overload for each heterogeneous comparison:template<class... TTypes> constexpr bool operator!=(const tuple<TTypes...>&, const tuple<TTypes...>&);This is then unambiguously chosen over the other options in the preceding case. FWIW, libstdc++ already does this in some cases.
Proposed resolution:
Section: 21.3.1.5 [string.access] Status: New Submitter: Matt Weber Opened: 2015-02-21 Last modified: 2015-05-04
Priority: 3
View all other issues in [string.access].
View all issues with New status.
Discussion:
It is often desirable to use a std::basic_string object as a buffer when interoperating with libraries that mutate null-terminated arrays of characters. In many cases, these legacy APIs write a null terminator at the specified end of the provided buffer. Providing such a function with an appropriately-sized std::basic_string results in undefined behavior when the charT object at the size() position is overwritten, even if the value remains unchanged.
Absent the ability to allow for this, applications are forced into pessimizations such as: providing appropriately-sized std::vectors of charT for interoperating with the legacy API, and then copying the std::vector to a std::basic_string; providing an oversized std::basic_string object and then calling resize() later. A trivial example:#include <string> #include <vector> void legacy_function(char *out, size_t count) { for (size_t i = 0; i < count; ++i) { *out++ = '0' + (i % 10); } *out = '\0'; // if size() == count, this results in undefined behavior } int main() { std::string s(10, '\0'); legacy_function(&s[0], s.size()); // undefined behavior std::vector<char> buffer(11); legacy_function(&buffer[0], buffer.size() - 1); std::string t(&buffer[0], buffer.size() - 1); // potentially expensive copy std::string u(11, '\0'); legacy_function(&u[0], u.size() - 1); u.resize(u.size() - 1); // needlessly complicates the program's logic }
A slight relaxation of the requirement on the returned object from the element access operator would allow for this interaction with no semantic change to existing programs.
Proposed resolution:
This wording is relative to N4296.
Edit 21.3.1.5 [string.access] as indicated:
const_reference operator[](size_type pos) const; reference operator[](size_type pos);-1- Requires: […]
-2- Returns: *(begin() + pos) if pos < size(). Otherwise, returns a reference to an object of type charT with value charT(), where modifying the object to any value other than charT() leads to undefined behavior. […]
Section: 22.3.3.2.2 [conversions.string] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2015-05-04
Priority: 4
View other active issues in [conversions.string].
View all other issues in [conversions.string].
View all issues with New status.
Discussion:
How do wstring_convert::from_bytes and wstring_convert::to_bytes use the cvtstate member?
Is it passed to the codecvt member functions? Is a copy of it passed to the member functions? "Otherwise it shall be left unchanged" implies a copy is used, but if that's really what's intended there are simpler ways to say so.Proposed resolution:
Section: 22.3.3.2.3 [conversions.buffer] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2015-05-04
Priority: 4
View other active issues in [conversions.buffer].
View all other issues in [conversions.buffer].
View all issues with New status.
Discussion:
How does wbuffer_convert use the cvtstate member?
Is the same conversion state object used for converting both the get and put areas? That means a read which runs out of bytes halfway through a multibyte character will leave some shift state in cvtstate, which would then be used by a following write, even though the shift state of the get area is unrelated to the put area.Proposed resolution:
Section: 22.3.3.2.3 [conversions.buffer] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2015-05-04
Priority: 4
View other active issues in [conversions.buffer].
View all other issues in [conversions.buffer].
View all issues with New status.
Discussion:
If a codecvt conversion returns codecvt_base::error should that be treated as EOF? An exception? Should all the successfully converted characters before a conversion error be available to the users of the wbuffer_convert and/or the internal streambuf, or does a conversion error lose information?
Proposed resolution:
Section: 22.3.3.2.2 [conversions.string] Status: New Submitter: Jonathan Wakely Opened: 2015-03-04 Last modified: 2015-05-04
Priority: 4
View other active issues in [conversions.string].
View all other issues in [conversions.string].
View all issues with New status.
Discussion:
Paragraph 4 of 22.3.3.2.2 [conversions.string] introduces byte_err_string as "a byte string to display on errors". What does display mean? The string is returned on error, it's not displayed anywhere.
Paragraph 14 says "Otherwise, if the object was constructed with a byte-error string, the member function shall return the byte-error string." The term byte-error string is not used anywhere else. Paragraph 17 talks about storing "default values in byte_err_string". What default value? Is "Hello, world!" allowed? If it means default-construction it should say so. If paragraph 14 says it won't be used what does it matter how it's initialized? The end of the paragraph refers to storing "byte_err in byte_err_string". This should be more clearly related to the wording in paragraph 14. It might help if the constructor (and destructor) was specified before the other member functions, so it can more formally define the difference between being "constructed with a byte-error string" and not. All the same issues apply to the wide_err_string member.Proposed resolution:
Section: 28 [re] Status: New Submitter: Stephan T. Lavavej Opened: 2015-03-27 Last modified: 2015-05-04
Priority: 3
View other active issues in [re].
View all other issues in [re].
View all issues with New status.
Discussion:
Only 4 functions are marked noexcept in all of Clause 28. Many more need to be marked — for example, regex_error::code(), basic_regex::swap(), and sub_match::length().
Proposed resolution:
Section: 20.14.6 [comparisons] Status: New Submitter: Agustín K-ballo Bergé Opened: 2015-04-01 Last modified: 2015-05-04
Priority: 3
View other active issues in [comparisons].
View all other issues in [comparisons].
View all issues with New status.
Discussion:
It is not entirely clear if and when the specializations of std::less (and friends) for pointer types can be used in a constant expression. Consider the following code:
#include <functional> struct foo {}; foo x, y; constexpr bool b = std::less<foo*>{}(&x, &y); // [1] foo z[] = {{}, {}}; constexpr bool ba = std::less<foo*>{}(&z[0], &z[1]); // [2]
Comparing the address of unrelated objects is not a constant expression since the result is unspecified, so it could be expected for [1] to fail and [2] to succeed. However, std::less specialization for pointer types is well-defined and yields a total order, so it could just as well be expected for [1] to succeed. Finally, since the implementation of such specializations is not mandated, [2] could fail as well (This could happen, if an implementation would provide such a specialization and if that would use built-in functions that would not be allowed in constant expressions, for example). In any case, the standard should be clear so as to avoid implementation-defined constexpr-ness.
Proposed resolution:
Section: 18.9 [support.initlist] Status: New Submitter: David Krauss Opened: 2015-04-27 Last modified: 2015-05-04
Priority: 4
View other active issues in [support.initlist].
View all other issues in [support.initlist].
View all issues with New status.
Discussion:
The typical use-case of std::initializer_list<T> is for a pass-by-value parameter of T's constructor. However, this contravenes 17.6.4.8 [res.on.functions]/2.5 because initializer_list doesn't specifically allow incomplete types (as do for example std::unique_ptr (20.11.1 [unique.ptr]/5) and std::enable_shared_from_this (20.11.2.5 [util.smartptr.enab]/2)).
A resolution would be to copy-paste the relevant text from such a paragraph.Proposed resolution:
Section: 20.15.4.3 [meta.unary.prop] Status: New Submitter: Hubert Tong Opened: 2015-05-07 Last modified: 2015-08-03
Priority: 3
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with New status.
Discussion:
I do not believe that the wording in 20.15.4.3 [meta.unary.prop] paragraph 3 allows for the following program to be ill-formed:
#include <type_traits> template <typename T> struct B : T { }; template <typename T> struct A { A& operator=(const B<T>&); }; std::is_assignable<A<int>, int> q;
In particular, I do not see where the wording allows for the "compilation of the expression" declval<T>() = declval<U>() to occur as a consequence of instantiating std::is_assignable<T, U> (where T and U are, respectively, A<int> and int in the example code).
Instantiating A<int> as a result of requiring it to be a complete type does not trigger the instantiation of B<int>; however, the "compilation of the expression" in question does.Proposed resolution:
Section: 27.7.3.4 [ostream::sentry] Status: New Submitter: Roger Orr Opened: 2015-05-08 Last modified: 2015-09-27
Priority: 3
View all other issues in [ostream::sentry].
View all issues with New status.
Discussion:
In the current 27.7.3.4 [ostream::sentry], p4 refers to the now deprecated std::uncaught_exception(): D.9 [depr.uncaught].
If ((os.flags() & ios_base::unitbuf) && !uncaught_exception() && os.good()) is true, calls os.rdbuf()->pubsync().
This needs to be changed, for example to use std::uncaught_exceptions() and to capture the value on entry and compare with the saved value on exit.
[2015-06, Telecom]
JW: I already added an 's' here to make it use the new function, but that doesn't resolve Roger's suggestion to capture the value on entry and check it.
Proposed resolution:
Section: 27.7.2.6 [istream.rvalue] Status: New Submitter: Richard Smith Opened: 2015-05-08 Last modified: 2015-09-27
Priority: 3
View all other issues in [istream.rvalue].
View all issues with New status.
Discussion:
Consider:
auto& is = make_istream() >> x; // oops, istream object is already gone
With a basic_istream&& return type, the above would be ill-formed, and generally we'd preserve the value category properly.
[2015-06, Telecom]
JW: think this needs proper consideration, it would make
stream() >> x >> y >> zgo from 3 operator>> calls to 6 operator>> calls, and wouldn't prevent dangling references (change the example to auto&&)
Proposed resolution:
Section: 27.7.2.2.3 [istream::extractors] Status: Open Submitter: Richard Smith Opened: 2015-05-08 Last modified: 2015-11-04
Priority: 2
View all other issues in [istream::extractors].
View all issues with Open status.
Discussion:
We removed gets() (due to an NB comment and C11 — bastion of backwards compatibility — doing the same). Should we remove this too?
Unlike gets(), there are legitimate uses:char buffer[32]; char text[32] = // ... ostream_for_buffer(text) >> buffer; // ok, can't overrun buffer
… but the risk from constructs like "std::cin >> buffer" seems to outweigh the benefit.
The issue had been discussed on the library reflector starting around c++std-lib-35541.[2015-06, Telecom]
VV: Request a paper to deprecate / remove anything
[2015-10, Kona Saturday afternoon]
STL: This overload is evil and should probably die.
VV: I agree with that, even though I don't care.
STL: Say that we either remove it outright following the gets() rationale, or at least deprecate it.
Move to Open; needs a paper.
Proposed resolution:
Section: 20.14.12.2 [func.wrap.func] Status: New Submitter: David Krauss Opened: 2015-05-20 Last modified: 2015-08-03
Priority: 3
View other active issues in [func.wrap.func].
View all other issues in [func.wrap.func].
View all issues with New status.
Discussion:
The idea behind propagate_on_container_move_assignment is that you can keep an allocator attached to a container. But it's not really designed to work with polymorphism, which introduces the condition where the current allocator is non-POCMA and the RHS of assignment, being POCMA, wants to replace it. If function were to respect the literal meaning, any would-be attached allocator is at the mercy of every assignment operation. So, std::function is inherently POCMA, and passing a non-POCMA allocator should be ill-formed.
The other alternative, and the status quo, is to ignore POCMA and assume it is true. This seems just dangerous enough to outlaw. It is, in theory, possible to properly support POCMA as far as I can see, albeit with difficulty and brittle results. It would require function to keep a throwing move constructor, which otherwise can be noexcept. The same applies to propagate_on_container_copy_assignment. This presents more difficulty because std::allocator does not set this to true. Perhaps it should. For function to respect this would require inspecting the POCCA of the source allocator, slicing the target from the erasure of the source, slicing the allocation from the erasure of the destination, and performing a copy with the destination's allocator with the source's target. This comes out of the blue for the destination allocator, which might not support the new type anyway. Theoretically possible, but brittle and not very practical. Again, current implementations quietly ignore the issue but this isn't very clean. The following code example is intended to demonstrate the issue here:#include <functional> #include <iostream> #include <vector> template <typename T> struct diag_alloc { std::string name; T* allocate(std::size_t n) const { std::cout << '+' << name << '\n'; return static_cast<T*>(::operator new(n * sizeof(T))); } void deallocate(T* p, std::size_t) const { std::cout << '-' << name << '\n'; return ::operator delete(p); } template <typename U> operator diag_alloc<U>() const { return {name}; } friend bool operator==(const diag_alloc& a, const diag_alloc& b) { return a.name == b.name; } friend bool operator!=(const diag_alloc& a, const diag_alloc& b) { return a.name != b.name; } typedef T value_type; template <typename U> struct rebind { typedef diag_alloc<U> other; }; }; int main() { std::cout << "VECTOR\n"; std::vector<int, diag_alloc<int>> foo({1, 2}, {"foo"}); // +foo std::vector<int, diag_alloc<int>> bar({3, 4}, {"bar"}); // +bar std::cout << "move\n"; foo = std::move(bar); // no message std::cout << "more foo\n"; foo.reserve(40); // +foo -foo std::cout << "more bar\n"; bar.reserve(40); // +bar -bar std::cout << "\nFUNCTION\n"; int bigdata[100]; auto bigfun = [bigdata]{}; typedef decltype(bigfun) ft; std::cout << "make fizz\n"; std::function<void()> fizz(std::allocator_arg, diag_alloc<ft>{"fizz"}, bigfun); // +fizz std::cout << "another fizz\n"; std::function<void()> fizz2; fizz2 = fizz; // +fizz as if POCCA std::cout << "make buzz\n"; std::function<void()> buzz(std::allocator_arg, diag_alloc<ft>{"buzz"}, bigfun); // +buzz std::cout << "move\n"; buzz = std::move(fizz); // -buzz as if POCMA std::cout << "\nCLEANUP\n"; }
Proposed resolution:
Section: 20.14.12.2 [func.wrap.func] Status: New Submitter: David Krauss Opened: 2015-05-20 Last modified: 2015-08-03
Priority: 3
View other active issues in [func.wrap.func].
View all other issues in [func.wrap.func].
View all issues with New status.
Discussion:
It is impossible for std::function to construct its target object using the construct method of a type-erased allocator. More confusingly, it is possible when the allocator and the target are created at the same time. The means of target construction should be specified.
Proposed resolution:
Section: 28.5.1 [re.synopt] Status: New Submitter: Nozomu Katō Opened: 2015-05-22 Last modified: 2015-09-27
Priority: 2
View other active issues in [re.synopt].
View all other issues in [re.synopt].
View all issues with New status.
Discussion:
The specification of ECMAScript defines the Multiline property for its RegExp and the regular expressions ^ and $ behave differently according to the value of this property. Thus, this property should be available also in the ECMAScript compatible engine in std::regex.
[2015-05-22, Daniel comments]
This issue interacts somewhat with LWG 2343.
[Telecom 2015-07]
Set the priority to match LWG 2343.
Proposed resolution:
This wording is relative to N4431.
Change 28.5.1 [re.synopt] as indicated:
namespace std::regex_constants { typedef T1 syntax_option_type; constexpr syntax_option_type icase = unspecified ; constexpr syntax_option_type nosubs = unspecified ; constexpr syntax_option_type optimize = unspecified ; constexpr syntax_option_type collate = unspecified ; constexpr syntax_option_type ECMAScript = unspecified ; constexpr syntax_option_type basic = unspecified ; constexpr syntax_option_type extended = unspecified ; constexpr syntax_option_type awk = unspecified ; constexpr syntax_option_type grep = unspecified ; constexpr syntax_option_type egrep = unspecified ; constexpr syntax_option_type multiline = unspecified ; }
Change 28.5.2 [re.matchflag], Table 138 — "syntax_option_type effects" as indicated:
Table 138 — syntax_option_type effects Element Effect(s) if set … multiline Specifies that ^ shall match the beginning of a line and $ shall match the end of a line, if the ECMAScript engine is selected. …
Section: 27.6.3 [streambuf] Status: New Submitter: Jonathan Wakely Opened: 2015-05-28 Last modified: 2015-08-03
Priority: 3
View other active issues in [streambuf].
View all other issues in [streambuf].
View all issues with New status.
Discussion:
27.6.3 [streambuf] p1 says:
The class template basic_streambuf<charT, traits> serves as an abstract base class for deriving various stream buffers whose objects each control two character sequences: […]
The term "abstract base class" is not defined in the standard, but "abstract class" is (10.4 [class.abstract]).
According to the synopsis basic_streambuf has no pure virtual functions so is not an abstract class and none of libstdc++, libc++, or dinkumware implement it as an abstract class. I don't believe the wording was ever intended to require it to be an abstract class, but it could be read that way. I suggest the wording be changed to "polymorphic base class" or something else that can't be seen to imply a normative requirement to make it an abstract class.Proposed resolution:
Section: 99 [auto.ptr.conv] Status: Open Submitter: Hubert Tong Opened: 2015-05-28 Last modified: 2016-05-17
Priority: 4
View all other issues in [auto.ptr.conv].
View all issues with Open status.
Discussion:
In C++14 sub-clause 99 [auto.ptr.conv], there appears to be no requirement that the formation of an auto_ptr_ref<Y> from an auto_ptr<X> is done only when X* can be implicitly converted to Y*.
For example, I expect formation of the auto_ptr_ref<A> from the prvalue of type auto_ptr<B> to be invalid in the case below (but the wording does not seem to be there):#include <memory> struct A { }; struct B { } b; std::auto_ptr<B> apB() { return std::auto_ptr<B>(&b); } int main() { std::auto_ptr<A> apA(apB()); apA.release(); }
The behaviour of the implementation in question on the case presented above is to compile and execute it successfully (which is what the C++14 wording implies). The returned value from apA.release() is essentially reinterpret_cast<A*>(&b).
There is nothing in the specification oftemplate <class X> template <class Y> operator auto_ptr<X>::auto_ptr_ref<Y>() throw();
which implies that X* should be implicitly convertible to Y*.
The implementation in question uses the reinterpret_cast interpretation even when Y is an accessible, unambiguous base class of X; the result thereof is that no offset adjustment is performed.[2015-07, Telecon]
Marshall to resolve.
[2016-03-16, Alisdair Meredith comments]
This issue is a defect in a component we have actively removed from the standard. I can't think of a clearer example of something that is no longer a defect!
Proposed resolution:
This wording is relative to ISO/IEC 14882:2014(E).
Change 99 [auto.ptr.conv] as indicated:
template<class Y> operator auto_ptr_ref<Y>() throw();-?- Requires: X* can be implicitly converted to Y*.
-3- Returns: An auto_ptr_ref<Y> that holds *this. -?- Notes: Because auto_ptr_ref is present for exposition only, the only way to invoke this function is by calling one of the auto_ptr conversions which take an auto_ptr_ref as an argument. Since all such conversions will call release() on *this (in the form of the auto_ptr that the auto_ptr_ref holds a reference to), an implementation of this function may cause instantiation of said release() function without changing the semantics of the program.template<class Y> operator auto_ptr<Y>() throw();-?- Requires: X* can be implicitly converted to Y*.
-4- Effects: Calls release(). -5- Returns: An auto_ptr<Y> that holds the pointer returned from release().
Section: 1.10 [intro.multithread], 29.5 [atomics.types.generic], 18.10 [support.runtime] Status: New Submitter: Geoffrey Romer Opened: 2015-05-29 Last modified: 2015-09-25
Priority: 3
View all other issues in [intro.multithread].
View all issues with New status.
Discussion:
The concurrency libraries specified in clauses 29 and 30 do not adequately specify how they relate to the concurrency model specified in 1.10 [intro.multithread]. In particular:
1.10 [intro.multithread] specifies "atomic objects" as having certain properties. I can only assume that instances of the classes defined in Clause 29 are intended to be "atomic objects" in this sense, but I can't find any wording to specify that, and it's genuinely unclear whether Clause 30 objects are atomic objects. In fact, on a literal reading the C++ Standard doesn't appear to provide any portable way to create an atomic object, or even determine whether an object is an atomic object. (It's not clear if the term "atomic object" is actually needed, given that atomic objects can have non-atomic operations, and non-atomic objects can have atomic operations. But even if the term itself goes away, there still needs to be some indication that Clause 29 objects have the properties currently attributed to atomic objects). Similarly, 1.10 [intro.multithread] uses "atomic operation" as a term of art, but the standard never unambiguously identifies any operation as an "atomic operation" (although in one case it unambiguously identifies an operation that is not atomic). It does come close in a few cases, but not close enough:1.10 [intro.multithread]/p7 could be read to imply that "synchronization operations" in Clauses 29 and 30 are also atomic operations. However, that's vague and indirect, and somewhat belied by 30.4.1.2 [thread.mutex.requirements.mutex]/p5, which specifies that mutex lock and unlock operations "behave as atomic operations", but only "for purposes of determining the existence of a data race". Furthermore, not a single operation in Clause 29 explicitly identifies itself as a "synchronization operation".
29.5 [atomics.types.generic]/p4 states in part that "There shall be a specialization atomic<bool> which provides the general atomic operations as specified in 29.6.1", but read in context, "general atomic operations" appears to be a loose synonym for "general operations on atomic types" as defined in 29.6.1 [atomics.types.operations.general], rather than a use of "atomic object" as Words of Power. Incidentally, "atomic type" is never satisfactorily defined either (although the <atomic> synopsis comes close).
18.10 [support.runtime]/p10 specifies exactly which operations are "plain lock-free atomic operations", but in a standard where an "integral constant expression" isn't necessarily a "constant expression", I do not feel safe assuming that a "plain lock-free atomic operation" is an "atomic operation".
Hans Boehm tells me the operations with "atomically" in the Effects element are intended to be atomic operations, but since "atomic operation" is a term of art (e.g. in 1.10 [intro.multithread]/p27.4), I think this needs to be spelled out rather than assumed. Furthermore, this does not help with 29.8 [atomics.fences], or anything in Clause 30.
Proposed resolution:
Section: 22.5 [locale.stdcvt] Status: New Submitter: Jonathan Wakely Opened: 2015-06-08 Last modified: 2015-08-03
Priority: 3
View all other issues in [locale.stdcvt].
View all issues with New status.
Discussion:
The enumeration type codecvt_mode is effectively a bitmask type (17.5.2.1.3 [bitmask.types]) with three elements, but isn't defined as such.
This harms usability because bitmask types are required to work well with bitwise operators, but codecvt_mode doesn't have overloaded operators, making it very inconvenient to combine values:std::codecvt_utf16<char32_t, 0x10FFFF, static_cast<std::codecvt_mode>(std::little_endian|std::generate_header)> cvt;
The static_cast harms readability and should not be necessary.
I suggest that either codecvt_mode is specified to be a bitmask type, or as a minimal fix we provide an overloaded operator| that returns the right type.Proposed resolution:
Section: 18.6.2.4 [new.delete.dataraces] Status: New Submitter: Hans Boehm Opened: 2015-06-09 Last modified: 2016-02-12
Priority: 3
View all other issues in [new.delete.dataraces].
View all issues with New status.
Discussion:
18.6.2.4 [new.delete.dataraces] uses obsolete wording.
It should introduce a "synchronizes with" relationship. "Happens before" is too weak, since that may not composes with sequenced before. The "shall not introduce a data race" wording is probably not technically correct either. These may race with other (non-allocation/deallocation) concurrent accesses to the object being allocated or deallocated.Proposed resolution:
Section: 18.6 [support.dynamic], 20.2 [utility], 20.4.5 [pair.piecewise], 20.10.2 [memory.syn], 20.10.6 [allocator.tag], 30.4 [thread.mutex] Status: Open Submitter: Ville Voutilainen Opened: 2015-06-13 Last modified: 2016-06-28
Priority: 2
View all other issues in [support.dynamic].
View all issues with Open status.
Discussion:
std::experimental::optional, for certain reasons, specifies its nullopt type to not be DefaultConstructible. It doesn't do so for its tag type in_place_t and neither does the standard proper for any of its tag types. That turns out to be very unfortunate, consider the following:
#include <memory> #include <array> void f(std::array<int, 1>, int) {} // #1 void f(std::allocator_arg_t, int) {} // #2 int main() { f({}, 666); // #3 }
The call at #3 is ambiguous. What's even worse is that if the overload #1 is removed, the call works just fine. The whole point of a tag type is that it either needs to mentioned in a call or it needs to be a forwarded argument, so being able to construct a tag type like that makes no sense.
Making the types have an explicit default constructor might have helped, but CWG 1518 is going against that idea. [optional.nullopt]/3 solves this problem for nullopt:Type nullopt_t shall not have a default constructor. It shall be a literal type. Constant nullopt shall be initialized with an argument of literal type.
[2015-06, Telecom]
Move to Tentatively Ready.
[2015-10, Kona Saturday afternoon]
Move back to Open
JW: The linked Core issue (CWG 1518) gives us a better tool to solve this (explicit default constructors). [The CWG Issue means that an explicit default constructor will no longer match "{}".] JW explains that it's important that tag types cannot be constructed from "{}" (e.g. the allocator tag in the tuple constructors).
WEB: Should we now go back and update our constructors? JW: For tag types, yes.
VV: The guideline is that anything that does not mention the type name explicitly should not invoke an explicit constructor.
Ville will provide wording.
Discussion about pair/tuple's default constructor - should they now be explicit?
[2016-01-31]
Ville provides revised wording.
Previous resolution [SUPERSEDED]:
This wording is relative to N4527.
In 18.6 [support.dynamic]/1, change the header <new> synopsis:
[…] struct nothrow_t{}; see below extern const nothrow_t nothrow; […]Add a new paragraph after 18.6 [support.dynamic]/1 (following the header <new> synopsis):
-?- Type nothrow_t shall not have a default constructor.
In 20.2 [utility]/2, change the header <utility> synopsis:
[…] // 20.3.5, pair piecewise construction struct piecewise_construct_t{ }; see below constexpr piecewise_construct_t piecewise_construct{ unspecified }; […]Add a new paragraph after 20.2 [utility]/2 (following the header <utility> synopsis):
-?- Type piecewise_construct_t shall not have a default constructor. It shall be a literal type. Constant piecewise_construct shall be initialized with an argument of literal type.
In 20.4.5 [pair.piecewise], apply the following edits:
struct piecewise_construct_t{ }; constexpr piecewise_construct_t piecewise_construct{ unspecified };In 20.10.2 [memory.syn]/1, change the header <memory> synopsis:
[…] // 20.7.6, allocator argument tag struct allocator_arg_t{ }; see below constexpr allocator_arg_t allocator_arg{ unspecified }; […]Add a new paragraph after 20.10.2 [memory.syn]/1 (following the header <memory> synopsis):
-?- Type allocator_arg_t shall not have a default constructor. It shall be a literal type. Constant allocator_arg shall be initialized with an argument of literal type.
In 20.10.6 [allocator.tag], apply the following edits:
namespace std { struct allocator_arg_t{ }; constexpr allocator_arg_t allocator_arg{ unspecified }; }Editorial drive-by: piecewise_construct_t is written, in 20.4.5 [pair.piecewise] like
struct piecewise_construct_t { }; constexpr piecewise_construct_t piecewise_construct{};whereas other tag types such as allocator_construct_t are, in e.g. 20.10.6 [allocator.tag], written like
namespace std { struct allocator_arg_t { }; constexpr allocator_arg_t allocator_arg{}; }We should decide whether or not to write out the std namespace in such paragraphs. I would suggest not to write it out.
In 30.4 [thread.mutex]/1, change the header <mutex> synopsis:
[…] struct defer_lock_t{ }; see below struct try_to_lock_t{ }; see below struct adopt_lock_t{ }; see below constexpr defer_lock_t defer_lock { unspecified }; constexpr try_to_lock_t try_to_lock { unspecified }; constexpr adopt_lock_t adopt_lock { unspecified }; […]Add three new paragraphs after [thread.mutex]/1 (following the header <mutex> synopsis):
-?- Type defer_lock_t shall not have a default constructor. It shall be a literal type. Constant defer_lock shall be initialized with an argument of literal type.
-?- Type try_to_lock_t shall not have a default constructor. It shall be a literal type. Constant try_to_lock shall be initialized with an argument of literal type. -?- Type adopt_lock_t shall not have a default constructor. It shall be a literal type. Constant adopt_lock shall be initialized with an argument of literal type.
[2016-03 Jacksonville]
AM: should have note about compatibility in Annex C
HH: like this idiom well enough that I've started using it in my own code
AM: why are pair and tuple involved here?
GR: they are the only types which forward explicitness with EXPLICIT
AM: British spelling of behaviour
AM: happy to drop my issue about Annex C
[Oulu, 2016-06]
This is waiting on Core issue 1518
Saturday: Core 1518 was resolved in Oulu
Proposed resolution:
This wording is relative to N4567.
In 18.6 [support.dynamic]/1, change the header <new> synopsis:
[…] struct nothrow_t { explicit nothrow_t() = default; }; extern const nothrow_t nothrow; […]
In 20.2 [utility]/2, change the header <utility> synopsis:
[…] // 20.3.5, pair piecewise construction struct piecewise_construct_t { explicit piecewise_construct_t() = default; }; constexpr piecewise_construct_t piecewise_construct{}; […]
In 20.4.2 [pairs.pair], change the class template pair synopsis:
[…] pair(pair&&) = default; EXPLICIT constexpr pair(); EXPLICIT constexpr pair(const T1& x, const T2& y); […]
Around 20.4.2 [pairs.pair] p3, apply the following edits:
EXPLICIT constexpr pair();-3- Effects: Value-initializes first and second.
-4- Remarks: This constructor shall not participate in overload resolution unless is_default_constructible<first_type>::value is true and is_default_constructible<second_type>::value is true. [Note: This behaviour can be implemented by a constructor template with default template arguments. — end note] The constructor is explicit if and only if either first_type or second_type is not implicitly default-constructible. [Note: This behaviour can be implemented with a trait that checks whether a const first_type& or a const second_type& can be initialized with {}. — end note]
In 20.4.5 [pair.piecewise], apply the following edits:
struct piecewise_construct_t { explicit piecewise_construct_t() = default; }; constexpr piecewise_construct_t piecewise_construct{};
In 20.5.2 [tuple.tuple], change the class template tuple synopsis:
[…] // 20.4.2.1, tuple construction EXPLICIT constexpr tuple(); EXPLICIT constexpr tuple(const Types&...); // only if sizeof...(Types) >= 1 […]
Around 20.5.2.1 [tuple.cnstr] p4, apply the following edits:
EXPLICIT constexpr tuple();-4- Effects: Value initializes each element.
-5- Remarks: This constructor shall not participate in overload resolution unless is_default_constructible<Ti>::value is true for all i. [Note: This behaviour can be implemented by a constructor template with default template arguments. — end note] The constructor is explicit if and only if Ti is not implicitly default-constructible for at least one i. [Note: This behaviour can be implemented with a trait that checks whether a const Ti& can be initialized with {}. — end note]
In 20.10.2 [memory.syn]/1, change the header <memory> synopsis:
[…] // 20.7.6, allocator argument tag struct allocator_arg_t { explicit allocator_arg_t() = default; }; constexpr allocator_arg_t allocator_arg{}; […]
In 20.10.6 [allocator.tag], apply the following edits:
namespace std { struct allocator_arg_t { explicit allocator_arg_t() = default; }; constexpr allocator_arg_t allocator_arg{}; }
Editorial drive-by: piecewise_construct_t is written, in 20.4.5 [pair.piecewise] like
struct piecewise_construct_t { }; constexpr piecewise_construct_t piecewise_construct{};whereas other tag types such as allocator_construct_t are, in e.g. 20.10.6 [allocator.tag], written like
namespace std { struct allocator_arg_t { }; constexpr allocator_arg_t allocator_arg{}; }We should decide whether or not to write out the std namespace in such paragraphs. I would suggest not to write it out.
In 30.4 [thread.mutex]/1, change the header <mutex> synopsis:
[…] struct defer_lock_t { explicit defer_lock_t() = default; }; struct try_to_lock_t { explicit try_to_lock_t() = default; }; struct adopt_lock_t { explicit adopt_lock_t() = default; }; constexpr defer_lock_t defer_lock { }; constexpr try_to_lock_t try_to_lock { }; constexpr adopt_lock_t adopt_lock { }; […]
Section: 20.13.4 [allocator.adaptor.members] Status: New Submitter: David Krauss Opened: 2015-06-16 Last modified: 2015-08-03
Priority: 3
View other active issues in [allocator.adaptor.members].
View all other issues in [allocator.adaptor.members].
View all issues with New status.
Discussion:
20.13.4 [allocator.adaptor.members]/10 requires that the argument types in the piecewise-construction tuples all be CopyConstructible. These tuples are typically created by std::forward_as_tuple, such as in ¶13. So they will be a mix of lvalue and rvalue references, the latter of which are not CopyConstructible.
My guess is that CopyConstructible was specified to feed the tuple_cat, before that function could handle rvalues. Since the argument tuple is already moved in ¶11, the requirement is obsolete. It should either be changed to MoveConstructible, or perhaps better, convert the whole tuple to references (i.e. form tuple<Args1&&...>) so nothing needs to be moved. After all, this is a facility for handling non-movable types. It appears that the resolution of DR 2203, which added std::move to ¶11, simply omitted the change to ¶10.Proposed resolution:
Section: 22.4.5.1.2 [locale.time.get.virtuals] Status: New Submitter: Hubert Tong Opened: 2015-06-19 Last modified: 2015-08-03
Priority: 2
View all other issues in [locale.time.get.virtuals].
View all issues with New status.
Discussion:
I recently encountered a failure related to questionable use of do_get_year. The platform where the code happened to work had an implementation which handled certain three-digit "year identifiers" as the number of years since 1900 (this article describes such an implementation).
22.4.5.1.2 [locale.time.get.virtuals] makes it implementation defined whether two-digit years are accepted, etc., but does not say anything specifically about three-digit years. The implementation freedom to not report errors in 22.4.5.1 [locale.time.get] paragraph 1 also seems to be too broad. See also the discussion following c++std-lib-38042.Proposed resolution:
Section: 21.1 [strings.general] Status: New Submitter: Jonathan Wakely Opened: 2015-06-26 Last modified: 2016-07-06
Priority: 4
View all other issues in [strings.general].
View all issues with New status.
Discussion:
The allocator-aware container requirements in Table 98 impose no MoveAssignable requirements on the value_type when propagate_on_container_move_assignment is true, because typically the container's storage would be moved by just exchanging some pointers.
However for a basic_string using the small string optimization move assignment may need to assign individual characters into the small string buffer, even when the allocator propagates. The only requirement on the char-like objects stored in a basic_string are that they are non-array POD types and Destructible, which means that a POD type with a deleted move assignment operator should be usable in a basic_string, despite it being impossible to move assign:#include <string> struct odd_pod { odd_pod() = default; odd_pod& operator=(odd_pod&&) = delete; }; static_assert(std::is_pod<odd_pod>::value, "POD"); int main() { using S = std::basic_string<odd_pod>; S s; s = S{}; // fails }
Using libstdc++ basic_string<odd_pod> cannot even be default-constructed because the constructor attempts to assign the null terminator to the first element of the small string buffer.
Similar problems exist with POD types with a deleted default constructor. I believe that basic_string should require its value_type to be at least DefaultConstructible and MoveAssignable.[2016-06, Oulu]
This should be resolved by P0178
Note: P0178 was sent back to LEWG in Oulu.
Proposed resolution:
Section: 20.15.1 [meta.rqmts] Status: New Submitter: Jonathan Wakely Opened: 2015-07-03 Last modified: 2015-08-03
Priority: 3
View all issues with New status.
Discussion:
We should make it clear that all standard UnaryTypeTraits, BinaryTypeTraits and TransformationTraits are not final.
Otherwise it is not safe to use them as arguments to a template like this:template<typename C1, typename C2> struct conjunction : conditional_t<C1::value, C2, C1> { };
Proposed resolution:
Section: 99 [fund.ts.v2::propagate_const.algorithms] Status: New Submitter: Tim Song Opened: 2015-07-08 Last modified: 2016-02-29
Priority: 3
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
For consistency with the rest of the standard library, the non-member swap for propagate_const should call member swap.
[2015-07, Telecom]
Both P3 and NAD were suggested.
[2016-02-20, Ville comments]
Feedback from an implementation:
The implementation of propagate_const in libstdc++ calls propagate_const's member swap from the non-member swap.Proposed resolution:
This wording is relative to N4529.
Edit [propagate_const.algorithms] as indicated:
-1- template <class T> constexpr void swap(propagate_const<T>& pt1, propagate_const<T>& pt2) noexcept(see below)-2- The constant-expression in the exception-specification is noexcept(
-3- Effects:swap(pt1.t_, pt2.t_)pt1.swap(pt2)).swap(pt1.t_, pt2.t_)pt1.swap(pt2).
Section: 24.2.7 [random.access.iterators] Status: New Submitter: Hubert Tong Opened: 2015-07-15 Last modified: 2015-09-14
Priority: 2
View all other issues in [random.access.iterators].
View all issues with New status.
Discussion:
In subclause 24.2.7 [random.access.iterators], Table 110, the operational semantics for the expression "r -= n" are defined as
return r += -n;
Given a difference_type of a type int with range [-32768, 32767], if the value of n is -32768, then the evaluation of -n causes undefined behaviour (Clause 5 [expr] paragraph 4).
The operational semantics may be changed such that the undefined behaviour is avoided. Suggested wording: Replace the operational semantics for "r -= n" with:{ difference_type m = n; if (m >= 0) while (m--) --r; else while (m++) ++r; return r; }
Jonathan Wakely:
I'm now convinced we don't want to change the definition of -= and instead we should explicitly state the (currently implicit) precondition that n != numeric_limits<difference_type>::min().Proposed resolution:
This wording is relative to N4527.
Change Table 110 "Random access iterator requirements (in addition to bidirectional iterator)" as indicated:
Table 110 — Random access iterator requirements (in addition to bidirectional iterator) Expression Return type Operational
semanticsAssertion/note
pre-/post-condition… r -= n X& return r += -n; pre: the absolute value of n is in the range of representable values of difference_type. …
Section: 99 [fund.ts.v2::memory.smartptr.weak.const] Status: New Submitter: Tim Song Opened: 2015-07-25 Last modified: 2016-01-04
Priority: 2
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
[memory.smartptr.weak.const] altered the constraints on weak_ptr's constructor from const weak_ptr<Y>& and const shared_ptr<Y>&. The constraints on the converting move constructor from weak_ptr<Y>&& was not, but should be, similarly modified.
[2015-10-26]
Daniel adjusts wording to lib. fund. v2. As link to the originating proposal: The discussion in this issue refers to wording changes that were requested by N3920.
Proposed resolution:
This wording is relative to N4529.
At the end of [memory.smartptr.weak.const], add:
[Drafting note: The current paragraph [memory.smartptr.weak.const] p2 is incorrectly declared as Requires element,
but it does not describe a requirement, instead it describes a "template constraint" which are elsewhere always
specified within a Remarks element because it describes constraints that an implementation (and not the user) has to meet.
See LWG 2292 for a suggestion to introduce a separate new specification element for this situation.
This has also been fixed in the current working draft. —
end drafting note]
weak_ptr(weak_ptr&& r) noexcept; template<class Y> weak_ptr(weak_ptr<Y>&& r) noexcept;-?- Remark: The second constructor shall not participate in overload resolution unless Y* is compatible with T*.
-?- Effects: Move-constructs a weak_ptr instance from r. -?- Postconditions: *this shall contain the old value of r. r shall be empty. r.use_count() == 0.
Section: 26.6.8.4.2 [rand.dist.pois.exp] Status: Open Submitter: Michael Prähofer Opened: 2015-08-20 Last modified: 2015-11-04
Priority: 2
View all issues with Open status.
Discussion:
Original title was: exponential_distribution<float> sometimes returns inf.
The random number distribution class template exponential_distribution<float> may return "inf" as can be seen from the following example program:
// compiled with // g++ -std=c++11 Error_exp_distr.cpp #include <iostream> #include <random> #include <bitset> int main(){ unsigned long long h; std::mt19937_64 mt1(1); std::mt19937_64 mt2(1); mt1.discard(517517); mt2.discard(517517); std::exponential_distribution<float> dis(1.0); h = mt2(); std::cout << std::bitset<64>(h) << " " << (float) -log(1 - h/pow(2, 64)) << " " << -log(1 - (float) h/pow(2, 64)) << " " << dis(mt1) << std::endl; h = mt2(); std::cout << std::bitset<64>(h) << " " << (float) -log(1 - h/pow(2, 64)) << " " << -log(1 - (float) h/pow(2, 64)) << " " << dis(mt1) << std::endl; }
output:
0110010110001001010011000111000101001100111110100001110011100001 0.505218 0.505218 0.505218 1111111111111111111111111101010011000110011110011000110101100110 18.4143 inf inf
The reason seems to be that converting a double x in the range [0, 1) to float may result in 1.0f if x is close enough to 1. I see two possibilities to fix that:
use internally double (or long double?) and then convert the result at the very end to float.
take only 24 random bits and convert them to a float x in the range [0, 1) and then return -log(1 - x).
I have not checked if std::exponential_distribution<double> has the same problem: For float on the average 1 out of 224 (~107) draws returns "inf", which is easily confirmed. For double on the average 1 out of 253 (~1016) draws might return "inf", which I have not tested.
Marshall:
I don't think the problem is in std::exponential_distribution; but rather in generate_canonical.
Consider:
which outputs:std::mt19937_64 mt2(1); mt2.discard(517517); std::cout << std::hexfloat << std::generate_canonical<float, std::numeric_limits<float>::digits>(mt2) << std::endl; std::cout << std::hexfloat << std::generate_canonical<float, std::numeric_limits<float>::digits>(mt2) << std::endl; std::cout << std::hexfloat << std::generate_canonical<float, std::numeric_limits<float>::digits>(mt2) << std::endl;
but generate_canonical is defined to return a result in the range [0, 1).0x1.962532p-2 0x1p+0 0x1.20d0cap-3
[2015-10, Kona Saturday afternoon]
Options:
WEB: The one thing we cannot tolerate is any output range other than [0, 1).
WEB: I believe there may be a documented algorithm for the generator, and perhaps it's possible to discover en-route that the algorithm produces the wrong result and fix it.
MC: No. I analyzed this once, and here it is: the algorithm is in [rand.util.canonical], and it's all fine until p5. The expression S/R^k is mathematically less than one, but it may round to one.
GR: Could we change the rounding mode for the computation?
HH: No, because the rounding mode is global, not thread-local.
AM: SG1 wants to get rid of the floating point environment.
STL: The problem is that the standard specifies the implementation, and the implementation doesn't work.
MC: I'm not sure if nudging it down will introduce a subtle bias.
EF: I worry about how the user's choice of floating point environment affects the behaviour.
MS offers to run the topic past colleagues.
MC: Will set the status to open. STL wants to rename the issue. WEB wants to be able to find the issue by its original name still.
Mike Spertus to run the options past his mathematical colleagues, and report back.
Proposed resolution:
Section: 99 [fund.ts.v2::func.wrap.func], 99 [fund.ts.v2::futures.promise], 99 [fund.ts.v2::futures.task] Status: New Submitter: Tim Song Opened: 2015-08-04 Last modified: 2015-10-27
Priority: 3
View other active issues in [fund.ts.v2::func.wrap.func].
View all other issues in [fund.ts.v2::func.wrap.func].
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
There doesn't seem to be any reason why this member function cannot be called on a const object, or why it would ever throw. I discussed this with Pablo Halpern, the author of N3916, and he agrees that this appears to have been an oversight.
[2015-10-26]
Daniel adjusts wording to lib. fund. v2.
Proposed resolution:
This wording is relative to N4529.
Edit each of the synposes in 99 [func.wrap.func], 99 [futures.promise], and 99 [futures.task] as indicated:
pmr::memory_resource* get_memory_resource() const noexcept;
Section: 99 [fund.ts.v2::func.wrap.func.con] Status: New Submitter: Tim Song Opened: 2015-08-04 Last modified: 2015-10-27
Priority: 3
View all other issues in [fund.ts.v2::func.wrap.func.con].
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
According to the table in [memory.type.erased.allocator], if no allocator argument is specified at the time of construction, the memory resource pointer used is the value of experimental::pmr::get_default_resource() at the time of construction.
Yet in 99 [func.wrap.func.con], ALLOCATOR_OF is specified to return allocator<char>() if no allocator was specified at the time of construction, which seems incorrect, especially as the user can change the default memory resource pointer to something other than new_delete_resource().
[2015-10-26]
Daniel adjusts wording to lib. fund. v2.
Proposed resolution:
This wording is relative to N4529.
Edit 99 [func.wrap.func.con]/p2 as indicated:
-2- In the following descriptions, let ALLOCATOR_OF(f) be the allocator specified in the construction of function f, or
allocator<char>()the value of experimental::pmr::get_default_resource() at the time of the construction of f if no allocator was specified.
Section: 20.5.2.1 [tuple.cnstr] Status: New Submitter: Brian Rodriguez Opened: 2015-08-25 Last modified: 2015-09-14
Priority: 3
View other active issues in [tuple.cnstr].
View all other issues in [tuple.cnstr].
View all issues with New status.
Discussion:
The std::tuple order of element construction is unspecified. It is either in the same order of the type list or in reverse.
Consider the following program:#include <iostream> #include <tuple> struct X { X(int) { std::cout << "X constructor\n"; } }; struct Y { Y(int) { std::cout << "Y constructor\n"; } }; int main() { std::tuple<X, Y> t(1, 2); }
Here is a link to two sample compilations. The first uses libstdc++ and constructs in reverse order, and the second uses libc++ and constructs in in-order.
A std::tuple mimics both a struct and type-generic container and should thus follow their standards. Construction is fundamentally different from a function call, and it has been historically important for a specific order to be guaranteed; namely: whichever the developer may decide. Mandating construction order will allow developers to reference younger elements later on in the chain as well, much like a struct allows you to do with its members. There are implementation issues as well. Reversed lists will require unnecessary overhead for braced-initializer-list initialization. Since lists are evaluated from left to right, the initializers must be placed onto the stack to respect the construction order. This issue could be significant for large tuples, deeply nested tuples, or tuples with elements that require many constructor arguments. I propose that the std::tuple<A, B, ..., Y, Z>'s constructor implementation be standardized, and made to construct in the same order as its type list e.g. A{}, B{}, ..., Y{}, Z{}.Daniel:
When N3140 became accepted, wording had been added that gives at least an indication of requiring element initialization in the order of the declaration of the template parameters. This argumentation can be based on 20.5.2.1 [tuple.cnstr] p3 (emphasize mine):-3- In the constructor descriptions that follow, let i be in the range [0,sizeof...(Types)) in order, Ti be the ith type in Types, and Ui be the ith type in a template parameter pack named UTypes, where indexing is zero-based.
But the current wording needs to be improved to make that intention clearer and an issue like this one is necessary to be sure that the committee is agreeing (or disagreeing) with that intention, especially because N3140 didn't really point out the relevance of the element construction order in the discussion, and because not all constructors explicitly refer to the ordered sequence of numbers generated by the variable i (The move constructor does it right, but most other don't do that).
Proposed resolution:
Section: 30.6.4 [futures.state] Status: New Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2015-10-20
Priority: 3
View all other issues in [futures.state].
View all issues with New status.
Discussion:
When a shared-state is released, it may be necessary to execute user defined code for the destructor of a stored value or exception. It is unclear whether the execution of said destructor constitutes an observable side effect.
While discussing N4445 in Lenexa, Nat Goodspeed pointed out that 30.6.4 [futures.state]/5.1 does not explicitly mention the destruction of the result, so implementations should be allowed to release (or reuse) a shared state ahead of time under the "as-if" rule.
The standard should clarify whether the execution of destructors is a visible side effect of releasing a shared state.Proposed resolution:
Section: 30.6.6 [futures.unique_future] Status: New Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2015-10-20
Priority: 3
View other active issues in [futures.unique_future].
View all other issues in [futures.unique_future].
View all issues with New status.
Discussion:
The standard is usually very explicit on when a shared state is released, except for future::get for which it only states valid() == false as a postcondition.
Proposed resolution:
This wording is relative to N4527.
Modify 30.6.6 [futures.unique_future] as indicated:
R future::get(); R& future<R&>::get(); void future<void>::get();-14- Note: as described above, the template and its two required specializations differ only in the return type and return value of the member function get.
-15- Effects:
wait()s until the shared state is ready, then retrieves the value stored in the shared state
.;releases any shared state (30.6.4 [futures.state]).
[…]
Section: 30.6.5 [futures.promise] Status: New Submitter: Agustín K-ballo Bergé Opened: 2015-09-03 Last modified: 2015-10-20
Priority: 3
View other active issues in [futures.promise].
View all other issues in [futures.promise].
View all issues with New status.
Discussion:
promise::set_value_at_thread_exit and promise::set_exception_at_thread_exit operate on a shared state at thread exit, without making the thread participate in the ownership of such shared state.
Consider the following snippet:std::promise<int>{}.set_value_at_thread_exit(42);
Arguably, since the promise abandons its shared state without actually making it ready, a broken_promise error condition should be stored in the shared state. Implementations diverge, they either crash at thread exit by dereferencing an invalid pointer, or keep the shared state around until thread exit.
Proposed resolution:
Section: 27.7.3.9 [ostream.rvalue], 27.7.2.6 [istream.rvalue] Status: New Submitter: Robert Haberlach Opened: 2015-09-08 Last modified: 2015-09-25
Priority: 3
View other active issues in [ostream.rvalue].
View all other issues in [ostream.rvalue].
View all issues with New status.
Discussion:
The rvalue stream insertion and extraction operators should be constrained to not participate in overload resolution unless the expression they evaluate is well-formed. Programming code that tests the validity of stream insertions (or extractions) using SFINAE can result in false positives, as the present declarations accept virtually any right-hand side argument. Moreover, there is no need for pollution of the candidate set with ill-formed specializations.
Proposed resolution:
This wording is relative to N4527.
Modify 27.7.3.9 [ostream.rvalue] as indicated:
template <class charT, class traits, class T> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);-1- Effects: os << x
-2- Returns: os -?- Remarks: This overload shall not participate in overload resolution unless the expression os << x, considered as an unevaluated operand, is well-formed.
Modify 27.7.2.6 [istream.rvalue] as indicated:
template <class charT, class traits, class T> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>&& is, T& x);-1- Effects: is >> x
-2- Returns: is -?- Remarks: This overload shall not participate in overload resolution unless the expression is >> x, considered as an unevaluated operand, is well-formed.
Section: 21.3.2.9 [string.io], 27.7.3.7 [ostream.unformatted] Status: New Submitter: Marshall Clow Opened: 2015-09-10 Last modified: 2015-09-14
Priority: 2
View all other issues in [string.io].
View all issues with New status.
Discussion:
Consider the following program:
#include <iostream> #include <ostream> #include <string> template <class CharT> class testbuf : public std::basic_streambuf<CharT> { public: testbuf() {} protected: virtual std::streamsize xsputn(const CharT *s, std::streamsize n) { std::cout << "xsputn('" << s << "', " << n << ")\n"; return n; } }; int main () { testbuf<char> sb; std::ostream os (&sb); std::string s1{"abc"}; os.write(s1.data(), s1.size()); os.write(s1.data(), 0); std::string s2{"def"}; os << s2; std::string s3{""}; os << s3; os << ""; }
What should it print?
libc++:xsputn('abc', 3) xsputn('def', 3)
libstdc++:
xsputn('abc', 3) xsputn('abc', 0) xsputn('def', 3) xsputn('', 0) xsputn('', 0)
VS:
xsputn('abc', 3) xsputn('def', 3) xsputn('', 0) xsputn('', 0)
21.3.2.9 [string.io]/5 seems to say that an implementation is required to call sputn (which calls xsputn) even if there's nothing to output (in the case of ostream::operator<<(basic_string)).
27.7.3.7 [ostream.unformatted]/5.1 implies that it's OK to not call sputn if there's nothing to output (in the case of ostream::write) Backstory: A user has a ostream with a subclass of basic_streambuf. it creates an output file on first write. Sometimes, he calls ostream::write(p, 0), and expects this to create the file. This doesn't work in libc++, and then he pointed out the inconsistency between operator<< and write. For reference to a bug report see here. There are two obvious possible resolutions:a) require all output functions to call sputn, even if there are no characters to output. In practice, this reduces to "string-like" things which are empty (string, string_view, char*, etc), and write(ptr, len).
b) remove the requirement that ostream::operator<< call sputn when there are no characters to output.
Proposed resolution:
Section: D.4 [depr.c.headers] Status: New Submitter: Richard Smith Opened: 2015-09-10 Last modified: 2015-09-14
Priority: 2
View all other issues in [depr.c.headers].
View all issues with New status.
Discussion:
LWG issue 1134 removed the resolution of LWG 551, leaving an incorrect specification for the behavior of <complex.h>. This header is currently required to make std::complex (and associated functions) visible in the global namespace, but should not be so required.
Proposed resolution:
This wording is relative to N4527.
Add a new paragraph before D.4 [depr.c.headers]/2:
-?- The header <complex.h> behaves as if it simply includes the header <ccomplex>.
Change in D.4 [depr.c.headers]/2:
-2- Every other C header, each of which has a name of the form name.h, behaves as if each name placed in the standard library namespace by the corresponding cname header is placed within the global namespace scope. It is unspecified whether these names are first declared or defined within namespace scope (3.3.6) of the namespace std and are then injected into the global namespace scope by explicit using-declarations (7.3.3).
Section: 23.2.5 [unord.req] Status: New Submitter: Isaac Hier Opened: 2015-09-16 Last modified: 2015-10-20
Priority: 3
View other active issues in [unord.req].
View all other issues in [unord.req].
View all issues with New status.
Discussion:
I have been wondering about the C++ standard requirements regarding the hint iterator for insertion into an unordered_multimap (and I imagine a similar question could be asked of unordered_map, but I have not researched that topic). As far as I can tell, it seems perfectly valid for an implementation to allow only valid dereferencable iterators to be used as the hint argument for this member function. If that is correct, it means that one could not expect the end iterator to be used as a valid hint nor could one use the begin iterator of an empty unordered_multimap as the hint. However, this essentially precludes all uses of inserter on an empty unordered_multimap seeing as the inserter functor requires that a hint iterator be passed to its constructor.
Howard Hinnant:The intent of the standard is that the iterator produced from container c by c.end() is a valid (but non-dereferenceable) iterator into container c. It is reachable by every other iterator into c.
It appears to me that you and the Bloomberg implementation have fallen victim to a type-o in the Unordered associative container requirements, Table 102. The row containing:a.insert(q, t);should read instead:
a.insert(p, t);The distinction is that p is valid, and q is both valid and dereferenceable. The correction of this type-o would make unordered container insert consistent with unordered emplace_hint, associative insert, and associative emplace_hint.
Proposed resolution:
Change the insert-with-hint row in Table 102 Unordered associative container requirements like so:
a.insert( qp, t);iterator Requires: If t is a non-const
...Average Case
...
Section: 99 [parallel.ts::parallel.alg.overloads] Status: New Submitter: Tim Song Opened: 2015-09-26 Last modified: 2015-10-20
Priority: 1
View all issues with New status.
Discussion:
Addresses: parallel.ts
99 [parallel.alg.overloads] provides parallel algorithm overloads for many algorithms in the standard library, but I can't find any normative wording specifying which headers these new overloads live in. Presumably, if the original algorithm is in <meow>, the new overloads should be in <experimental/meow>.
Proposed resolution:
Section: 20.14.14 [unord.hash] Status: Open Submitter: Ville Voutilainen Opened: 2015-09-27 Last modified: 2016-06-20
Priority: 2
View other active issues in [unord.hash].
View all other issues in [unord.hash].
View all issues with Open status.
Discussion:
The rationale in issue 2148 says:
This proposed resolution doesn't specify anything else about the primary template, allowing implementations to do whatever they want for non-enums: static_assert nicely, explode horribly at compiletime or runtime, etc.
libc++ seems to implement it by defining the primary template and static_asserting is_enum inside it. However, that brings forth a problem; there are reasonable SFINAE uses broken by it:
#include <type_traits> #include <functional> class S{}; // No hash specialization template<class T> auto f(int) -> decltype(std::hash<T>(), std::true_type()); template<class T> auto f(...) -> decltype(std::false_type()); static_assert(!decltype(f<S>(0))::value, "");
MSVC doesn't seem to accept that code either.
There is a way to implement LWG 2148 so that hash for enums is supported without breaking that sort of SFINAE uses:Derive the main hash template from a library-internal uglified-named base template that takes a type and a bool, pass as argument for the base the result of is_enum.
Partially specialize that base template so that the false-case has a suitable set of private special member function declarations so that it's not an aggregate nor usable in almost any expression.
[2015-10, Kona Saturday afternoon]
EricWF to come back with wording; move to Open
[2016-05-08, Eric Fiselier & Ville provide wording]
[2016-05-25, Tim Song comments]
I see two issues with this P/R:
"for which neither the library nor the user provides an explicit specialization" should probably be "for which neither the library nor the user provides an explicit or partial specialization".
Saying that the specialization "is not DefaultConstructible nor MoveAssignable" is not enough to guarantee that common SFINAE uses will work. Both of those requirements have several parts, and it's not too hard to fail only some of them. For instance, not meeting the assignment postcondition breaks MoveAssignable, but is usually not SFINAE-detectible. And for DefaultConstructible, it's easy to write something in a way that breaks T() but not T{} (due to aggregate initialization in the latter case).
[2016-06-14, Daniel comments]
The problematic part of the P/R is that it describes constraints that would be suitable if they were constraints for user-code, but they are not suitable as requirements imposed on implementations to provide certain guarantees for clients of the Library. The guarantees should be written in terms of testable compile-time expressions, e.g. based on negative results of is_default_constructible<hash<X>>::value, std::is_copy_constructible<hash<X>>::value, and possibly also std::is_destructible<hash<X>>::value. How an implementation realizes these negative results shouldn't be specified, though, but the expressions need to be well-formed and well-defined.
Proposed resolution:
This wording is relative to N4582.
Insert a new paragraph after 20.14.14 [unord.hash]/2
-2- The template specializations shall meet the requirements of class template hash (20.12.14).
-?- For any type that is not of integral or enumeration type, or for which neither the library nor the user provides an explicit specialization of the class template hash, the specialization of hash does not meet any of the Hash requirements, and is not DefaultConstructible nor MoveAssignable. [Note: this means that the specialization of hash exists, but any attempts to use it as a Hash will be ill-formed. — end note]
Section: 24.6.3.2 [istreambuf.iterator.cons] Status: New Submitter: S. B. Tam Opened: 2015-10-05 Last modified: 2015-10-23
Priority: 3
View all issues with New status.
Discussion:
N4527 24.6.3.2 [istreambuf.iterator.cons] does not mention what the effect of calling istreambuf_iterator(basic_streambuf<charT, traits>* s) is when s is a null pointer. It should be made clear that this case is well-formed and the result is a end-of-stream iterator.
Daniel: According to 24.6.3 [istreambuf.iterator] p1:[…] The default constructor istreambuf_iterator() and the constructor istreambuf_iterator(0) both construct an end-of-stream iterator object suitable for use as an end-of-range. […]
This indicates that the described constructor creates an end-of-stream iterator, but this wording is part of the introductory wording and I recommend to make 24.6.3.2 [istreambuf.iterator.cons] clearer, because the existing specification is already flawed, e.g. it never specifies when and how the exposition-only-member sbuf_ is initialized. The proposed wording below attempts to solve these problems as well.
Previous resolution [SUPERSEDED]:
This wording is relative to N4527.
Change 24.6.3.2 [istreambuf.iterator.cons] as indicated:
[Editorial note: The proposed wording changes also performs some editorial clean-up of the existing mismatches of the declarations in the class template synopsis and the individual member specifications. The below wording intentionally does not say anything about the concrete value of sbuf_ for end-of-stream iterator values, because that was never specified before; in theory, this could be some magic non-null pointer that can be used in constant expressions. But the wording could be drastically simplified by requiring sbuf_ to be a null pointer for an end-of-stream iterator value, since I have not yet seen any implementation where this requirement does not hold. — end editorial note]constexpr istreambuf_iterator() noexcept;-1- Effects: Constructs the end-of-stream iterator.
istreambuf_iterator(basic_istream<charT,traits>istream_type& s) noexcept;istreambuf_iterator(basic_streambuf<charT,traits>* s) noexcept;-2- Effects: If s.rdbuf() is a null pointer, constructs an end-of-stream iterator; otherwise initializes sbuf_ with s.rdbuf() and constructs an istreambuf_iterator that uses the streambuf_type object *sbuf_
Constructs an istreambuf_iterator<> that uses the basic_streambuf<> object *(s.rdbuf()), or *s, respectively. Constructs an end-of-stream iterator if s.rdbuf() is null.istreambuf_iterator(streambuf_type* s) noexcept;-?- Effects: If s is a null pointer, constructs an end-of-stream iterator; otherwise initializes sbuf_ with s and constructs an istreambuf_iterator that uses the streambuf_type object *sbuf_.
istreambuf_iterator(const proxy& p) noexcept;-3- Effects: Initializes sbuf_ with p.sbuf_ and constructs an istreambuf_iterator that uses the streambuf_type object *sbuf_
Constructs a istreambuf_iterator<> that uses the basic_streambuf<> object pointed to by the proxy object's constructor argument p.
[2015-10-20, Daniel provides alternative wording]
Proposed resolution:
This wording is relative to N4527.
Change 24.6.3.2 [istreambuf.iterator.cons] as indicated:
[Editorial note: The proposed wording changes also performs some editorial clean-up of the existing mismatches of the declarations in the class template synopsis and the individual member specifications. The below wording is simplified by requiring sbuf_ to be a null pointer for an end-of-stream iterator value, since I have not yet seen any implementation where this requirement does not hold. Even if there were such an implementation, this would still be conforming, because concrete exposition-only member values are not part of public API. — end editorial note]For each istreambuf_iterator constructor in this section, an end-of-stream iterator is constructed if and only if the exposition-only member sbuf_ is initialized with a null pointer value.
constexpr istreambuf_iterator() noexcept;-1- Effects: Initializes sbuf_ with nullptr
Constructs the end-of-stream iterator.istreambuf_iterator(basic_istream<charT,traits>istream_type& s) noexcept;istreambuf_iterator(basic_streambuf<charT,traits>* s) noexcept;-2- Effects: Initializes sbuf_ with s.rdbuf()
Constructs an istreambuf_iterator<> that uses the basic_streambuf<> object *(s.rdbuf()), or *s, respectively. Constructs an end-of-stream iterator if s.rdbuf() is null.istreambuf_iterator(streambuf_type* s) noexcept;-?- Effects: Initializes sbuf_ with s.
istreambuf_iterator(const proxy& p) noexcept;-3- Effects: Initializes sbuf_ with p.sbuf_
Constructs a istreambuf_iterator<> that uses the basic_streambuf<> object pointed to by the proxy object's constructor argument p.
Section: 28.13 [re.grammar] Status: New Submitter: Hubert Tong Opened: 2015-10-08 Last modified: 2015-10-20
Priority: 4
View other active issues in [re.grammar].
View all other issues in [re.grammar].
View all issues with New status.
Discussion:
In 28.13 [re.grammar] paragraph 2:
basic_regex member functions shall not call any locale dependent C or C++ API, including the formatted string input functions. Instead they shall call the appropriate traits member function to achieve the required effect.
Yet, the required interface for a regular expression traits class (28.3 [re.req]) does not appear to have any reliable method for determining whether a character as encoded for the locale associated with the traits instance is the same as a character represented by a UnicodeEscapeSequence, e.g., assuming a sane ru_RU.koi8r locale:
#include <stdio.h> #include <stdlib.h> #include <regex> const char data[] = "\xB3"; const char matchCyrillicCaptialLetterYo[] = R"(\u0401)"; int main(void) { try { std::regex myRegex; myRegex.imbue(std::locale("ru_RU.koi8r")); myRegex.assign(matchCyrillicCaptialLetterYo, std::regex_constants::ECMAScript); printf("(%s)\n", std::regex_replace(std::string(data), myRegex, std::string("E")).c_str()); myRegex.assign("[[:alpha:]]", std::regex_constants::ECMAScript); printf("(%s)\n", std::regex_replace(std::string(data), myRegex, std::string("E")).c_str()); } catch (std::regex_error& e) { abort(); } return 0; }
The implementation I tried prints:
(Ё) (E)
Which means that the character class matching worked, but not the matching to the UnicodeEscapeSequence.
Proposed resolution:
Section: 20.14.6 [comparisons], 23.2.1 [container.requirements.general], 30.3.1.1 [thread.thread.id] Status: New Submitter: Matt Austern Opened: 2015-10-08 Last modified: 2015-10-20
Priority: 3
View other active issues in [comparisons].
View all other issues in [comparisons].
View all issues with New status.
Discussion:
A number of places in the library, including 20.14.6 [comparisons]/14, the Optional container requirements in 23.2.1 [container.requirements.general], and 30.3.1.1 [thread.thread.id]/8, use the phrase "total order". Unfortunately, that phrase is ambiguous. In mathematics, the most common definition is that a relation ≤ is a total order if it's total, transitive, and antisymmetric in the sense that x≤y ∧ y≤x ⇒ x=y. What we really want is a strict total order: a relation < is a strict total order if it's total, transitive, and antisymmetric in the sense that exactly one of x<y, y<x, and x=y holds.
The non-normative note in 25.5 [alg.sorting]/4 correctly uses the phrase "strict total ordering" rather than simply "total ordering".
We could address this issue by replacing "total order" with "strict total order" everywhere it appears, since I think there are no cases where we actually want a non-strict total order, or we could add something in Clause 17 saying that we always mean strict total order whenever we say total order.
Proposed resolution:
Section: 27.11 [c.files] Status: New Submitter: Richard Smith Opened: 2015-10-09 Last modified: 2016-06-28
Priority: 3
View all other issues in [c.files].
View all issues with New status.
Discussion:
C's vfscanf function is not present in C++'s <cstdio>, and presumably should be. It looks like this is the only missing member of C's [v]{f,s,sn}[w]{printf,scanf} family.
Proposed resolution:
This wording is relative to N4527.
Modify Table 133 as follows:
ungetc
vfprintf
vfscanf
vprintf
vscanf
Section: 30.6.6 [futures.unique_future] Status: New Submitter: Agustín K-ballo Bergé Opened: 2015-11-05 Last modified: 2016-03-06
Priority: 3
View other active issues in [futures.unique_future].
View all other issues in [futures.unique_future].
View all issues with New status.
Discussion:
future::share() is not noexcept, it has a narrow contact requiring valid() as per the blanket wording in 30.6.6 [futures.unique_future] p3. Its effects, however, are return shared_future<R>(std::move(*this)), which is noexcept as it has a wide contract. If the source future isn't valid then the target shared_future simply won't be valid either. There appears to be no technical reason preventing future::share() from having a wide contract, and thus being noexcept.
Proposed resolution:
This wording is relative to N4567.
Change 30.6.6 [futures.unique_future] as indicated:
-3- The effect of calling any member function other than the destructor, the move-assignment operator, share, or valid on a future object for which valid() == false is undefined. [Note: Implementations are encouraged to detect this case and throw an object of type future_error with an error condition of future_errc::no_state. — end note]
namespace std { template <class R> class future { public: […] shared_future<R> share() noexcept; […] }; }[…]
shared_future<R> share() noexcept;-12- Returns: shared_future<R>(std::move(*this)).
-13- Postcondition: valid() == false.[…]
Section: 99 [fund.ts.v2::optional.object.swap], 99 [fund.ts.v2::propagate_const.modifiers] Status: New Submitter: Daniel Krügler Opened: 2015-11-14 Last modified: 2016-03-06
Priority: 3
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
As pointed out in N4511, the Library fundamentals are affected by a similar problem as described in LWG 2456. First, it is caused by optional's member swap (99 [optional.object.swap]):
void swap(optional<T>& rhs) noexcept(see below);
with
The expression inside noexcept is equivalent to:
is_nothrow_move_constructible_v<T> && noexcept(swap(declval<T&>(), declval<T&>()))
Again, the unqualified lookup for swap finds the member swap instead of the result of a normal argument-depending lookup, making this ill-formed.
A second example of such a problem recently entered the arena with the addition of the propagate_const template with another member swap (99 [propagate_const.modifiers]):constexpr void swap(propagate_const& pt) noexcept(see below);-2- The constant-expression in the exception-specification is noexcept(swap(t_, pt.t_)).
A working approach is presented in N4511. By adding a new trait to the standard library and referencing this by the library fundamentals (A similar approach had been applied in the file system specification where the quoted manipulator from C++14 had been referred to, albeit the file system specification is generally based on the C++11 standard), optional's member swap exception specification could be rephrased as follows:
The expression inside noexcept is equivalent to:
is_nothrow_move_constructible_v<T> && is_nothrow_swappable_v<T>noexcept(swap(declval<T&>(), declval<T&>()))
and propagate_const's member swap exception specification could be rephrased as follows:
constexpr void swap(propagate_const& pt) noexcept(see below);-2- The constant-expression in the exception-specification is is_nothrow_swappable_v<T>
noexcept(swap(t_, pt.t_)).
[2016-02-20, Ville comments]
Feedback from an implementation:
libstdc++ already applies the proposed resolution for propagate_const, but not for optional.[2016-02-20, Daniel comments]
A recent paper update has been provided: P0185R0.
[2016-03, Jacksonville]
Add a link to 2456Proposed resolution:
Section: 20.14.6 [comparisons] Status: New Submitter: Casey Carter Opened: 2015-11-18 Last modified: 2016-05-22
Priority: 3
View other active issues in [comparisons].
View all other issues in [comparisons].
View all issues with New status.
Discussion:
N4567 20.14.6 [comparisons]/14 specifies that the comparison functors provide a total ordering for pointer types:
For templates greater, less, greater_equal, and less_equal, the specializations for any pointer type yield a total order, even if the built-in operators <, >, <=, >= do not.
It notably does not specify:
whether the specializations of all of the named templates for a given pointer type yield the same total order
whether the total order imposed respects the partial order imposed by the built-in operators
whether the total order imposed is consistent with the partition induced by ==
All of which are important for sane semantics and provided by common implementations, since the built-in operators provide a total order and the comparison functors yield that same order.
It would be extremely confusing — if not outright insane — for e.g.:less<int*> and greater<int*> to yield different orders
less<int*> to disagree with < on the relative order of two pointers for which < is defined
less<int*> to order a before b when a == b, i.e., not preserve equality.
Consistent semantics for the various comparison functors and the built-in operators is so intuitive as to be assumed by most programmers.
Related issues: 2450, 2547.Previous resolution [SUPERSEDED]:
This wording is relative to N4567.
Alter 20.14.6 [comparisons]/14 to read:
For templates greater, less, greater_equal, and less_equal, the specializations for any pointer type yield
athe same total order, even if the built-in operators <, >, <=, >= do not. The total order shall respect the partial order imposed by the built-in operators.
[2016-05-20, Casey Carter comments and suggests revised wording]
The new proposed wording is attempting to address the issue raised in the 2016-02-04 telecon.
The real issue I'm trying to address here is ensure that "weird" implementations provide the same kind of consistency for pointer orderings as "normal" implementations that use a flat address spaces and have totally ordered <. If a < b is true for int pointers a and b, then less<int*>(a, b), less_equal<int*>(a, b), less<char*>(a, b), less<void*>(a, b), and greater<int*>(b, a) should all hold. I think this wording is sufficient to provide that.Proposed resolution:
This wording is relative to N4582.
Alter 20.14.6 [comparisons] to read:
-14- For templates greater, less, greater_equal, and less_equal, the specializations for any pointer type yield
athe same total order. That total order is consistent with the partial order imposed by, even ifthe built-in operators <, >, ≤, and >do not. [Note: When a < b is well-defined for pointers a and b of type P, this implies (a < b) == less<P>(a, b), (a > b) == greater<P>(a, b), and so forth. — end note] For template specializations greater<void>, less<void>, greater_equal<void>, and less_equal<void>, if the call operator calls a built-in operator comparing pointers, the call operator yields a total order.
Section: 17.6.5.5 [member.functions] Status: New Submitter: Ville Voutilainen Opened: 2015-11-29 Last modified: 2016-05-22
Priority: 2
View other active issues in [member.functions].
View all other issues in [member.functions].
View all issues with New status.
Discussion:
The combination of 17.6.5.5 [member.functions], paragraphs 2 and 3 that LWG 2259 does seems to drop a requirement that any call behaves as if no overloads were added. Paragraph 3 used to say "A call to a member function signature described in the C ++ standard library behaves as if the implementation declares no additional member function signatures." whereas the new wording says "provided that any call to the member function that would select an overload from the set of declarations described in this standard behaves as if that overload were selected."
This can be read as meaning that if there's no default constructor specified, like for instance for std::ostream, an implementation is free to add it. It can also be read as meaning that an implementation is free to add any overloads that wouldn't change the overload resolution result of any call expression that would select a specified overload. That's vastly different from allowing extensions that add new functions rather than new overloads. Was this relaxation intentional?[2016-04, Issues Telecon]
Ville provides a motivating example.
#include <iostream> class my_stream : std::ostream { }; int main() { my_stream ms; }
This example is accepted by libstdc++, msvc rejects it, and clang+libc++ segfault on melpon.org/wandbox o_O. An earlier clang+libc++ just accepts it. I don't think the implementation divergence is caused by the acceptance of the referred-to 2259, but it certainly seems to increasingly bless the implementation divergence.
[2016-05 Issues Telecom]
This is related to issue 2695.
Proposed resolution:
Section: 99 [fund.ts.v2::func.wrap.func] Status: New Submitter: Tim Song Opened: 2015-12-05 Last modified: 2016-02-05
Priority: 3
View other active issues in [fund.ts.v2::func.wrap.func].
View all other issues in [fund.ts.v2::func.wrap.func].
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
[This is essentially LWG 2370, but deals with the fundamentals TS version rather than the one in the standard]
In 99 [func.wrap.func] of library fundamentals TS, the constructorstemplate<class A> function(allocator_arg_t, const A&) noexcept; template<class A> function(allocator_arg_t, const A&, nullptr_t) noexcept;
must type-erase and store the provided allocator, since the operator= specification requires using the "allocator specified in the construction of" the std::experimental::function object. This may require a dynamic allocation and so cannot be noexcept. Similarly, the following constructors
template<class A> function(allocator_arg_t, const A&, const function&); template<class A> function(allocator_arg_t, const A&, function&&); template<class F, class A> function(allocator_arg_t, const A&, F);
cannot satisfy the C++14 requirement that they "shall not throw exceptions if [the function object to be stored] is a callable object passed via reference_wrapper or a function pointer" if they need to type-erase and store the allocator.
Proposed resolution:
This wording is relative to N4562.
Edit 99 [func.wrap.func], class template function synopsis, as follows:
namespace std { namespace experimental { inline namespace fundamentals_v2 { […] template<class R, class... ArgTypes> class function<R(ArgTypes...)> { public: […] template<class A> function(allocator_arg_t, const A&)noexcept; template<class A> function(allocator_arg_t, const A&, nullptr_t)noexcept; […] }; […] } // namespace fundamentals_v2 } // namespace experimental […] } // namespace std
Insert the following paragraphs after 99 [func.wrap.func.con]/1:
[Drafting note: This just reproduces the wording from C++14 with the "shall not throw exceptions for reference_wrapper/function pointer" provision deleted. — end drafting note]
-1- When a function constructor that takes a first argument of type allocator_arg_t is invoked, the second argument is treated as a type-erased allocator (8.3). If the constructor moves or makes a copy of a function object (C++14 §20.9), including an instance of the experimental::function class template, then that move or copy is performed by using-allocator construction with allocator get_memory_resource().
template <class A> function(allocator_arg_t, const A& a); template <class A> function(allocator_arg_t, const A& a, nullptr_t);-?- Postconditions: !*this.
template <class A> function(allocator_arg_t, const A& a, const function& f);-?- Postconditions: !*this if !f; otherwise, *this targets a copy of f.target().
-?- Throws: May throw bad_alloc or any exception thrown by the copy constructor of the stored callable object. [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable 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]template <class A> function(allocator_arg_t, const A& a, function&& f);-?- Effects: If !f, *this has no target; otherwise, move-constructs the target of f into the target of *this, leaving f in a valid state with an unspecified value.
template <class F, class A> function(allocator_arg_t, const A& a, F f);-?- Requires: F shall be CopyConstructible.
-?- Remarks: This constructor shall not participate in overload resolution unless f is Callable (C++14 §20.9.11.2) for argument types ArgTypes... and return type R. -?- Postconditions: !*this if any of the following hold:-?- Otherwise, *this targets a copy of f initialized with std::move(f). [Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable 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] -?- Throws: May throw bad_alloc or any exception thrown by F's copy or move constructor.
f is a null function pointer value.
f is a null member pointer value.
F is an instance of the function class template, and !f.
-2- In the following descriptions, let ALLOCATOR_OF(f) be the allocator specified in the construction of function f, or allocator<char>() if no allocator was specified.
[…]
Section: 20.15.8 [meta.logical] Status: New Submitter: Tim Song Opened: 2015-12-10 Last modified: 2016-02-07
Priority: 2
View other active issues in [meta.logical].
View all other issues in [meta.logical].
View all issues with New status.
Discussion:
The specification of conjunction and disjunction uses the term BaseCharacteristic, which is problematic in several ways:
That term is defined in 20.15.1 [meta.rqmts], but only for UnaryTypeTraits and BinaryTypeTraits. conjunction and disjunction seem to be neither.
20.15.1 [meta.rqmts] also requires the BaseCharacteristic for both UnaryTypeTraits and BinaryTypeTraits to be a specialization of integral_constant, which is inconsistent with the current design of conjunction and disjunction.
The requirement in 20.15.1 [meta.rqmts] that "member names of the BaseCharacteristic shall not be hidden and shall be unambiguously available" seems impossible to meet in every case, since the arbitrary base class from which a specialization of conjunction or disjunction derives may contain members called conjunction or disjunction that will necessarily be hidden.
Proposed resolution:
Section: 99 [fund.ts.v2::meta.logical] Status: New Submitter: Tim Song Opened: 2015-12-10 Last modified: 2016-02-07
Priority: 2
View other active issues in [fund.ts.v2::meta.logical].
View all other issues in [fund.ts.v2::meta.logical].
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
The specification of conjunction and disjunction uses the term BaseCharacteristic, which is problematic in several ways:
That term is defined in 20.15.1 [meta.rqmts], but only for UnaryTypeTraits and BinaryTypeTraits. conjunction and disjunction seem to be neither.
20.15.1 [meta.rqmts] also requires the BaseCharacteristic for both UnaryTypeTraits and BinaryTypeTraits to be a specialization of integral_constant, which is inconsistent with the current design of conjunction and disjunction.
The requirement in 20.15.1 [meta.rqmts] that "member names of the BaseCharacteristic shall not be hidden and shall be unambiguously available" seems impossible to meet in every case, since the arbitrary base class from which a specialization of conjunction or disjunction derives may contain members called conjunction or disjunction that will necessarily be hidden.
Proposed resolution:
Section: 20.15.8 [meta.logical] Status: New Submitter: Tim Song Opened: 2015-12-11 Last modified: 2016-02-07
Priority: 2
View other active issues in [meta.logical].
View all other issues in [meta.logical].
View all issues with New status.
Discussion:
20.15.8 [meta.logical]/2 and /5 impose the following requirement on the arguments of conjunction and disjunction:
Every template type argument shall be usable as a base class and shall have a static data member value which is convertible to bool, is not hidden, and is unambiguously available in the type.
Since the requirement is unconditional, it applies even to type arguments whose instantiation is not required due to short circuiting. This seems contrary to the design intent, expressed in P0013R1, that it is valid to write conjunction_v<is_class<T>, is_foo<T>> even if instantiating is_foo<T>::value is ill-formed for non-class types.
Proposed resolution:
Section: 99 [fund.ts.v2::meta.logical] Status: New Submitter: Tim Song Opened: 2015-12-11 Last modified: 2016-06-28
Priority: 2
View other active issues in [fund.ts.v2::meta.logical].
View all other issues in [fund.ts.v2::meta.logical].
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
99 [meta.logical]/2 and /5 impose the following requirement on the arguments of conjunction and disjunction:
Every template type argument shall be usable as a base class and shall have a static data member value which is convertible to bool, is not hidden, and is unambiguously available in the type.
Since the requirement is unconditional, it applies even to type arguments whose instantiation is not required due to short circuiting. This seems contrary to the design intent, expressed in P0013R1, that it is valid to write conjunction_v<is_class<T>, is_foo<T>> even if instantiating is_foo<T>::value is ill-formed for non-class types.
[Oulu, 2016-06]
Alisdair has a paper in progress addressing this
Proposed resolution:
Section: 24.2 [iterator.requirements], 24.4.1 [iterator.traits] Status: New Submitter: Ville Voutilainen Opened: 2016-01-05 Last modified: 2016-04-16
Priority: 3
View other active issues in [iterator.requirements].
View all other issues in [iterator.requirements].
View all issues with New status.
Discussion:
See this reflector discussion for background.
24.2 [iterator.requirements] attempts to establish requirements for iterators, but 24.4.1 [iterator.traits]/1 establishes further requirements that must be met in order to author a portable iterator that works with existing implementations. Failing to meet the requirements of the latter will fail to work in practice. The former requirements should reference the latter, normatively.Proposed resolution:
After [iterator.requirements.general]/5, insert the following new paragraph:-?- In addition to these requirements, the nested typedefs specified in ([iterator.traits]) shall be provided for the iterator type. [Note: Either the iterator type must provide the typedefs directly (in which case iterator_traits pick them up automatically), or an iterator_traits specialization must provide them. -end note]
Section: 28.13 [re.grammar] Status: New Submitter: Billy O'Neal III Opened: 2016-01-13 Last modified: 2016-03-06
Priority: 2
View other active issues in [re.grammar].
View all other issues in [re.grammar].
View all issues with New status.
Discussion:
Stephan and I are seeing differences in implementation for how non-special characters should be handled in the IdentityEscape part of the ECMAScript grammar. For example:
#include <stdio.h> #include <iostream> #ifdef USE_BOOST #include <boost/regex.hpp> using namespace boost; #else #include <regex> #endif using namespace std; int main() { try { const regex r("\\z"); cout << "Constructed \\z." << endl; if (regex_match("z", r)) cout << "Matches z" << endl; } catch (const regex_error& e) { cout << e.what() << endl; } }
libstdc++, boost, and browsers I tested with (Microsoft Edge, Google Chrome) all happily interpret \z, which otherwise has no meaning, as an identity character escape for the letter z. libc++ and msvc++ say that this is invalid, and throw regex_error with error_escape.
ECMAScript 3 (which is what C++ currently points to) seems to agree with libc++ and msvc++:IdentityEscape :: SourceCharacter but not IdentifierPart IdentifierPart :: IdentifierStart UnicodeCombiningMark UnicodeDigit UnicodeConnectorPunctuation \ UnicodeEscapeSequence IdentifierStart :: UnicodeLetter $ _ \ UnicodeEscapeSequence
But this doesn't make any sense — it prohibits things like \$ which users absolutely need to be able to escape. So let's look at ECMAScript 6. I believe this says much the same thing, but updates the spec to better handle Unicode by referencing what the Unicode standard says is an identifier character:
IdentityEscape :: SyntaxCharacter / SourceCharacter but not UnicodeIDContinue UnicodeIDContinue :: any Unicode code point with the Unicode property "ID_Continue", "Other_ID_Continue", or "Other_ID_Start"
However, ECMAScript 6 has an appendix B defining "additional features for web browsers" which says:
IdentityEscape :: SourceCharacter but not c
which appears to agree with what libstdc++, boost, and browsers are doing.
What should be the correct behavior here?Proposed resolution:
This wording is relative to N4567.
Change 28.13 [re.grammar]/3 as indicated:
-3- The following productions within the ECMAScript grammar are modified as follows:
ClassAtom :: - ClassAtomNoDash ClassAtomExClass ClassAtomCollatingElement ClassAtomEquivalence IdentityEscape :: SourceCharacter but not c
Section: 20.15.8 [meta.logical] Status: New Submitter: Tim Song Opened: 2016-01-18 Last modified: 2016-02-07
Priority: 3
View other active issues in [meta.logical].
View all other issues in [meta.logical].
View all issues with New status.
Discussion:
The specification of conjunction and disjunction in 20.15.8 [meta.logical] p2 and p5 requires Bi::value to be convertible to bool, but nothing in the specification of the actual behavior of the templates, which instead uses the expressions Bi::value == false and Bi::value != false instead, actually requires this conversion.
If the intention of this requirement is to allow implementations to pass Bi::value directly to std::conditional, like the sample implementation in P0013R1:template<class B1, class B2> struct and_<B1, B2> : conditional_t<B1::value, B2, B1> { };
then it's insufficient in at least two ways:
Nothing in the specification requires the result of comparing Bi::value with false to be consistent with the result of the implicit conversion. This is similar to LWG 2114, though I don't think the BooleanTestable requirements in that issue's P/R covers Bi::value == false and Bi::value != false.
More importantly, the above implementation is ill-formed for, e.g., std::conjunction<std::integral_constant<int, 2>, std::integral_constant<int, 4>>, because converting 2 to bool is a narrowing conversion that is not allowed for non-type template arguments (see 5.20 [expr.const]/4). (Note that GCC currently doesn't diagnose this error at all, and Clang doesn't diagnose it inside system headers.) It's not clear whether such constructs are intended to be supported, but if they are not, the current wording doesn't prohibit it.
Proposed resolution:
Section: 99 [fund.ts.v2::meta.logical] Status: New Submitter: Tim Song Opened: 2016-01-18 Last modified: 2016-02-07
Priority: 3
View other active issues in [fund.ts.v2::meta.logical].
View all other issues in [fund.ts.v2::meta.logical].
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
The specification of conjunction and disjunction in 99 [meta.logical] p2 and p5 requires Bi::value to be convertible to bool, but nothing in the specification of the actual behavior of the templates, which instead uses the expressions Bi::value == false and Bi::value != false instead, actually requires this conversion.
If the intention of this requirement is to allow implementations to pass Bi::value directly to std::conditional, like the sample implementation in P0013R1:template<class B1, class B2> struct and_<B1, B2> : conditional_t<B1::value, B2, B1> { };
then it's insufficient in at least two ways:
Nothing in the specification requires the result of comparing Bi::value with false to be consistent with the result of the implicit conversion. This is similar to LWG 2114, though I don't think the BooleanTestable requirements in that issue's P/R covers Bi::value == false and Bi::value != false.
More importantly, the above implementation is ill-formed for, e.g., std::conjunction<std::integral_constant<int, 2>, std::integral_constant<int, 4>>, because converting 2 to bool is a narrowing conversion that is not allowed for non-type template arguments (see 5.20 [expr.const]/4). (Note that GCC currently doesn't diagnose this error at all, and Clang doesn't diagnose it inside system headers.) It's not clear whether such constructs are intended to be supported, but if they are not, the current wording doesn't prohibit it.
Proposed resolution:
Section: 28.10 [re.results] Status: New Submitter: S. B. Tam Opened: 2016-01-24 Last modified: 2016-05-19
Priority: 3
View other active issues in [re.results].
View all other issues in [re.results].
View all issues with New status.
Discussion:
N4567 28.10 [re.results] p2 mentions
The class template match_results shall satisfy the requirements of an allocator-aware container and of a sequence container, as specifed in 23.2.3, except that only operations defined for const-qualified sequence containers are supported.
However, this is impossible because match_results has a operator== whose semantics differs from the one required in Table 95 — "Container requirements".
Table 95 requires that a == b is an equivalence relation and means equal(a.begin(), a.end(), b.begin(), b.end()). But for match_results, a == b and equal(a.begin(), a.end(), b.begin(), b.end()) can give different results. For example:#include <iostream> #include <regex> #include <string> #include <algorithm> int main() { std::regex re("a*"); std::string target("baaab"); std::smatch a; std::regex_search(target, a, re); std::string target2("raaau"); std::smatch b; std::regex_search(target2, b, re); std::cout << std::boolalpha; std::cout << (a == b) << '\n'; // false std::cout << std::equal(a.begin(), a.end(), b.begin(), b.end()) << '\n'; // true }
[2016-02, Issues Telecon]
Marshall: The submitter is absolutely right, but the proposed resolution is insufficient. We should avoid "shall", for once.2016-05: Marshall cleans up the wording around the change
Proposed resolution:
Previous resolution [SUPERSEDED]:
This wording is relative to N4567.
Change 28.10 [re.results] p2 as indicated:
-2- The class template match_results shall satisfy the requirements of an allocator-aware container and of a sequence container, as specified in 23.2.3, except that only operations defined for const-qualified sequence containers are supported and that the semantics of comparison functions are different from those required for a container.
This wording is relative to N4567.
Change 28.10 [re.results] p2 as indicated:
-2- The class template match_results
shall satisfysatisfies the requirements of an allocator-aware container and of a sequence container,as specified in(23.2.3), except that only operations defined for const-qualified sequence containers are supported and the semantics of comparison functions are different from those required for a container.
Section: 20.14.12.2.5 [func.wrap.func.targ] Status: New Submitter: Daniel Krügler Opened: 2016-01-31 Last modified: 2016-02-07
Priority: 3
View all other issues in [func.wrap.func.targ].
View all issues with New status.
Discussion:
This issue is a spin-off of LWG 2393, it solely focuses on the pre-condition of 20.14.12.2.5 [func.wrap.func.targ] p2:
Requires: T shall be a type that is Callable (20.9.12.2) for parameter types ArgTypes and return type R.
Originally, the author of this issue here had assumed that simply removing the precondition as a side-step of fixing LWG 2393 would be uncontroversial. Discussions on the library reflector indicated that this is not the case, although it seemed that there was agreement on removing the undefined behaviour edge-case.
There exist basically the following positions:The constraint should be removed completely, the function is considered as having a wide contract.
The pre-condition should be replaced by a Remarks element, that has the effect of making the code ill-formed, if T is a type that is not Lvalue-Callable (20.9.11.2) for parameter types ArgTypes and return type R. Technically this approach is still conforming with a wide contract function, because the definition of this contract form depends on runtime constraints.
Not yet explicitly discussed, but a possible variant of bullet (2) could be:
The pre-condition should be replaced by a Remarks element, that has the effect of SFINAE-constraining this member: "This function shall not participate in overload resolution unless T is a type that is Lvalue-Callable (20.9.11.2) for parameter types ArgTypes and return type R".
The following describes a list of some selected arguments that have been provided for one or the other position using corresponding list items. Unless explicitly denoted, no difference has been accounted for option (3) over option (2).
It reflects existing implementation practice, Visual Studio 2015 SR1, gcc 6 libstdc++, and clang 3.8.0 libc++ do accept the following code:
#include <functional> #include <iostream> #include <typeinfo> #include "boost/function.hpp" void foo(int) {} int main() { std::function<void(int)> f(foo); std::cout << f.target<void(*)()>() << std::endl; boost::function<void(int)> f2(foo); std::cout << f2.target<void(*)()>() << std::endl; }
and consistently output the implementation-specific result for two null pointer values.
The current Boost documentation does not indicate any precondition for calling the target function, so it is natural that programmers would expect similar specification and behaviour for the corresponding standard component.
There is a consistency argument in regard to the free function template get_deleter
template<class D, class T> D* get_deleter(const shared_ptr<T>& p) noexcept;
This function also does not impose any pre-conditions on its template argument D.
Programmers have control over the type they're passing to target<T>(). Passing a non-callable type can't possibly retrieve a non-null target, so it seems highly likely to be programmer error. Diagnosing that at compile time seems highly preferable to allowing this to return null, always, at runtime.
If T is a reference type then the return type T* is ill-formed anyway. This implies that one can't blindly call target<T> without knowing what T is.
It has been pointed out that some real world code, boiling down to
void foo() {} int main() { std::function<void()> f = foo; if (f.target<decltype(foo)>()) { // fast path } else { // slow path } }
had manifested as a performance issue and preparing a patch that made the library static_assert in that case solved this problem (Note that decltype(foo) evaluates to void(), but a proper argument of target() would have been the function pointer type void(*)(), because a function type void() is not any Callable type).
It might be worth adding that if use case (2 c) is indeed an often occurring idiom, it would make sense to consider to provide an explicit conversion to a function pointer (w/o template parameters that could be provided incorrectly), if the std::function object at runtime conditions contains a pointer to a real function, e.g.
R(*)(ArgTypes...) target_func_ptr() const noexcept;
Proposed resolution:
This wording is relative to N4567.
Change 20.14.12.2.5 [func.wrap.func.targ] p2 as indicated:
template<class T> T* target() noexcept; template<class T> const T* target() const noexcept;-3- Returns: If target_type() == typeid(T) a pointer to the stored function target; otherwise a null pointer.
-2- Requires: T shall be a type that is Callable (20.14.12.2 [func.wrap.func]) for parameter types ArgTypes and return type R.
Section: 20.17.2 [time.syn] Status: New Submitter: Andy Giese Opened: 2016-02-05 Last modified: 2016-05-17
Priority: 4
View all other issues in [time.syn].
View all issues with New status.
Discussion:
Currently 20.17.2 [time.syn] states
// convenience typedefs typedef duration<signed integer type of at least 64 bits, nano> nanoseconds; typedef duration<signed integer type of at least 55 bits, micro> microseconds; typedef duration<signed integer type of at least 45 bits, milli> milliseconds; typedef duration<signed integer type of at least 35 bits > seconds; typedef duration<signed integer type of at least 29 bits, ratio< 60>> minutes; typedef duration<signed integer type of at least 23 bits, ratio<3600>> hours;
However, a duration_cast<minutes>(seconds::max()) would cause overflow if the underlying signed integers only met the minimums specified.
The standard should specify that implementations guarantee that a duration_cast from any smaller duration in these "convenience typedefs" will not overflow any larger duration. That is, hours should be able to hold the maximum of minutes, which should be able to hold the maximum of seconds and so on. More formally, if the ratio typedef A and typedef B is 1:Y where Y > 1 (e.g., 1 : 60 in case of minutes : seconds), then #bitsA-1 must be at least ceil(log2(2#bitsB-1)/Y)). In the case of minutes : seconds, X = 1, Y = 60. Let #bitsseconds = 32. Therefore:2(#bitsseconds - 1) = 231 = 2147483648
ceil(log2(231 / 60) = 26
#bitsminutes - 1 = 26
#bitsminutes = 27
Therefore, a minimum of 27 bits would be needed to store minutes if 32 were used to store seconds.
I propose to change the definitions of the convenience typedefs as follows:// convenience typedefs typedef duration<signed integer type of at least 64 bits, nano> nanoseconds; typedef duration<signed integer type of at least 55 bits, micro> microseconds; typedef duration<signed integer type of at least 46 bits, milli> milliseconds; typedef duration<signed integer type of at least 37 bits > seconds; typedef duration<signed integer type of at least 32 bits, ratio< 60>> minutes; typedef duration<signed integer type of at least 27 bits, ratio<3600>> hours;
These bits were chosen to satisfy the above formula. Note that minimums only increased, so larger ranges could be held. A nice outcome of this choice is that minutes does not go above 32 bits.
[2016-04-23, Tim Song comments]
The P/R of LWG 2592 doesn't fix the issue it wants to solve, because the actual underlying type will likely have more bits than the specified minimum.
Consider seconds, which the P/R requires to have at least 37 bits. On a typical system this implies using a 64-bit integer. To ensure that casting from seconds::max() to minutes doesn't overflow in such a system, it is necessary for the latter to have at least 59 bits (which means, in practice, 64 bits too), not just 32 bits. Thus, just changing the minimum number of bits will not be able to provide the desired guarantee that casting from a smaller unit to a larger one never overflow. If such a guarantee is to be provided, it needs to be spelled out directly. Note that the difference here is 9 bits (for the 1000-fold case) and 5 bits (for the 60-fold case), which is less than the size difference between integer types on common systems, so such a requirement would effectively require those convenience typedefs to use the same underlying integer type.Proposed resolution:
This wording is relative to N4567.
Change 20.17.2 [time.syn], header <chrono> synopsis, as indicated
[…] // convenience typedefs typedef duration<signed integer type of at least 64 bits, nano> nanoseconds; typedef duration<signed integer type of at least 55 bits, micro> microseconds; typedef duration<signed integer type of at least 4645bits, milli> milliseconds; typedef duration<signed integer type of at least 3735bits > seconds; typedef duration<signed integer type of at least 3229bits, ratio< 60>> minutes; typedef duration<signed integer type of at least 2723bits, ratio<3600>> hours; […]
Section: 17.6.3.5 [allocator.requirements] Status: New Submitter: David Krauss Opened: 2016-02-19 Last modified: 2016-06-20
Priority: 4
View other active issues in [allocator.requirements].
View all other issues in [allocator.requirements].
View all issues with New status.
Discussion:
17.6.3.5 [allocator.requirements] suggests that the moved-from state of an allocator may be unequal to its previous state. Such a move constructor would break most container implementations, which move-construct the embedded allocator along with a compressed pair. Even if a moved-from container is empty, it should still subsequently allocate from the same resource pool as it did before.
std::vector<int, pool> a(500, my_pool); auto b = std::move(a); // b uses my_pool too. a.resize(500); // should still use my_pool.
[2016-02, Jacksonville]
Marshall will see if this can be resolved editorially.
After discussion, the editors and I decided that this could not be handled editorially. The bit about a moved-from state of an allocator being the same as the original state is a normative change. I submitted a pull request to handle the mismatched variables in the table.
Previous resolution [SUPERSEDED]:
This wording is relative to N4567.
Change 17.6.3.5 [allocator.requirements], Table 28 — "Allocator requirements" as indicated:
Note there's an editorial error in Table 28 in that line and the surrounding ones. The left column was apparently updated to use u and the right column is still using a/a1/b.
Table 28 — Allocator requirements Expression Return type Assertion/note
pre-/post-conditionDefault … X u(move(a));
X u = move(a);Shall not exit via an exception. post: u is equal to a and equal to the prior value of a a1 equals the prior value of a.…
[2016-06-20, Oulu, Daniel comments]
According to the current working draft, the situation has changed due to changes performed by the project editor, the revised resolution has been adjusted to N4594.
Proposed resolution:
This wording is relative to N4594.
Change 17.6.3.5 [allocator.requirements], Table 28 — "Allocator requirements" as indicated:
Table 28 — Allocator requirements Expression Return type Assertion/note
pre-/post-conditionDefault … X u(std::move(a));
X u = std::move(a);Shall not exit via an exception. post: u is equal to a and equal to the prior value of a u is equal to the prior value of a..…
Section: 20.11.2.2 [util.smartptr.shared] Status: New Submitter: Kazutoshi Satoda Opened: 2016-02-20 Last modified: 2016-06-20
Priority: 3
View other active issues in [util.smartptr.shared].
View all other issues in [util.smartptr.shared].
View all issues with New status.
Discussion:
Latest draft (N4567) 20.11.2.2 [util.smartptr.shared] p1 says:
A shared_ptr object is empty if it does not own a pointer.
Please note that it says "own a pointer". This definition was added as the resolution for LWG defect 813.
20.11.2.2.1 [util.smartptr.shared.const] p8 says about the effect of shared_ptr(nullptr_t p, D d):Effects: Constructs a shared_ptr object that owns the object p and the deleter d.
Please note that it says "owns the object". This was intentionally changed from "the pointer" as a part of resolution for LWG defect 758, to cover nullptr_t case.
Since shared_ptr(nullptr, d) owns an object of type nullptr_t, but does not own a pointer, it is said as "empty" by a strict reading of the above mentioned definition in 20.11.2.2 [util.smartptr.shared] p1. These cause a contradiction: 20.11.2.2.1 [util.smartptr.shared.const] p9 sets a postcondition use_count() == 1 on shared_ptr(nullptr, d). But 20.11.2.2.5 [util.smartptr.shared.obs] p7 says that the return value of use_count() is "0 when *this is empty".Proposed wording changes:
Replace the last 2 words in 20.11.2.2 [util.smartptr.shared] p1 from[…] empty if it does not own a pointer.
to
[…] empty if it does not own an object.
Note that shared_ptr(nullptr_t) is defined to be empty in synopsis in 20.11.2.2 [util.smartptr.shared].
constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { }
It could be less confusing if shared_ptr(nullptr, d) could be defined to be empty. But it seems too late to change that (which means changing whether the deleter is called or not, see this Stackoverflow article). Then I'm proposing just fix the contradiction.
Proposed resolution:
This wording is relative to N4594.
Change 20.11.2.2 [util.smartptr.shared] p1 as indicated:
-1- The shared_ptr class template stores a pointer, usually obtained via new. shared_ptr implements semantics of shared ownership; the last remaining owner of the pointer is responsible for destroying the object, or otherwise releasing the resources associated with the stored pointer. A shared_ptr object is empty if it does not own an object
a pointer.
Section: 24.5.1.1 [reverse.iterator], 24.5.1.3.12 [reverse.iter.opindex] Status: New Submitter: Robert Haberlach Opened: 2016-02-28 Last modified: 2016-04-15
Priority: 3
View all other issues in [reverse.iterator].
View all issues with New status.
Discussion:
Issue 386 changed the return type of reverse_iterator::operator[] to unspecified. However, as of N3066, the return type of a random access iterator's operator[] shall be convertible to reference; thus the return type of reverse_iterator::operator[] should be reference (and it is in all common implementations).
Suggested resolution: Adjust 24.5.1.1 [reverse.iterator]'s synopsis and 24.5.1.3.12 [reverse.iter.opindex] to use reference instead of unspecified.Proposed resolution:
This wording is relative to N4582.
Edit 24.5.1.1 [reverse.iterator], class template synopsis, as indicated:
namespace std { template <class Iterator> class reverse_iterator { public: […] typedef typename iterator_traits<Iterator>::reference reference; […] constexpr referenceunspecifiedoperator[](difference_type n) const; […] }; }
Change 24.5.1.3.12 [reverse.iter.opindex] before p1 as indicated:
constexpr referenceunspecifiedoperator[]( typename reverse_iterator<Iterator>::difference_type n) const;
Section: 26.5.8 [complex.transcendentals] Status: New Submitter: Thomas Koeppe Opened: 2016-03-01 Last modified: 2016-04-15
Priority: 3
View all other issues in [complex.transcendentals].
View all issues with New status.
Discussion:
The current specification of std::log is inconsistent for complex numbers, specifically, the Returns clause (26.5.8 [complex.transcendentals]). On the one hand, it states that the imaginary part of the return value lies in the closed interval [-i π, +i π]. On the other hand, it says that "the branch cuts are along the negative real axis" and "the imaginary part of log(x) is +π when x is a negative real number".
The inconsistency lies in the difference between the mathematical concept of a branch cut and the nature of floating point numbers in C++. The corresponding specification in the C standard makes it clearer that if x is a real number, then log(x + 0i) = +π, but log(x - 0i) = -π, i.e. they consider positive and negative zero to represent the two different limits of approaching the branch cut from opposite directions. In other words, the term "negative real number" is misleading, and in fact there are two distinct real numbers, x + 0i and x - 0i, that compare equal but whose logarithms differ by 2 π i.
The resolution should consist of two parts:
Double-check that our usage and definition of "branch cut" is sufficiently unambiguous. The C standard contains a lot more wording around this that we don't have in C++.
Change the Returns clause of log appropriately. For example: "When x is a negative real number, imag(log(x + 0i)) is π, and imag(log(x - 0i)) is -π."
Current implementations seem to behave as described in (2). (Try-it-at-home link)
Proposed resolution:
Section: 20.10.10.1 [specialized.addressof] Status: New Submitter: Brent Friedman Opened: 2016-03-06 Last modified: 2016-04-15
Priority: 3
View all other issues in [specialized.addressof].
View all issues with New status.
Discussion:
LWG issue 970 removed the rvalue reference overload for addressof. This allows const prvalues to bind to a call to addressof, which is dissimilar from the behavior of operator&.
const vector<int> a(); void b() { auto x = addressof(a()); // "ok" auto y = addressof<const int>(0); // "ok" auto z = &a(); //error: cannot take address of a temporary }
Proposed resolution:
This wording is relative to N4582.
Change 20.10.2 [memory.syn], header <memory> synopsis, as indicated:
[…] // 20.9.12, specialized algorithms: template <class T> constexpr T* addressof(T& r) noexcept; template <class T> const T* addressof(const T&& elem) = delete; […]
Change 20.10.10.1 [specialized.addressof] p1 as indicated:
template <class T> constexpr T* addressof(T& r) noexcept; template <class T> const T* addressof(const T&& elem) = delete;-1- Returns: The actual address of the object or function referenced by r, even in the presence of an overloaded operator&.
Section: 20.2.6 [declval], 20.11.1 [unique.ptr], 20.11.1.1.1 [unique.ptr.dltr.general], 20.11.2.2 [util.smartptr.shared], 20.11.2.3 [util.smartptr.weak], 20.11.2.5 [util.smartptr.enab] Status: New Submitter: Zhihao Yuan Opened: 2016-03-08 Last modified: 2016-04-15
Priority: 3
View all issues with New status.
Discussion:
Currently the phrase to grant this permission is:
The template parameter T of LibraryTemplate may be an incomplete type.
Two problems:
The timing is unclear. We always allow specializations like LibraryTemplate<Incomp>* p;
To the users of a template, the correct terminology should be "argument" rather than "parameter".
Suggested resolution:
In an instantiation of LibraryTemplate, an incomplete type may be used as the template argument for the template parameter T.
as shown here.
Or, to copy N4510's wording:An incomplete type T may be used when instantiating LibraryTemplate.
Proposed resolution:
directory_entry
Section: 27.10.15.14 [fs.op.file_size] Status: New Submitter: Gor Nishanov Opened: 2014-05-22 Last modified: 2016-04-14
Priority: 2
View all issues with New status.
Discussion:
On Windows, the
structure, which is the underlying data type for FindFileData WIN32_FIND_DATAdirectory_entry
, contains the file size as one of the fields.
Thus efficient enumeration of files and getting their sizes is possible without doing a separate query for the file size.
[17 Jun 2014 Rapperswil LWG will investigate issue at a subsequent meeting.]
[23 Nov 2015 Editorally correct name of data structure mentioned in discussion.]
[Mar 2016 Jacksonville Beman to provide paper about this]
[Apr 2016 Issue updated to address the C++ Working Paper. Previously addressed File System TS]
Proposed resolution:
In 27.10.12 [class.directory_entry] Class directory_entry
add the
following observer declarations:
uintmax_t file_size(); uintmax_t file_size(error_code& ec) noexcept;
In directory_entry
observers 27.10.12.3 [directory_entry.obs]
add the following:
uintmax_t file_size(); uintmax_t file_size(error_code& ec) noexcept;Returns: if
*this
contains a cached file size, return it. Otherwise returnfile_size(path())
orfile_size(path(), ec)
respectively.Throws: As specified in Error reporting (7).
operator/
(and other append) semantics not useful if argument has rootSection: 27.10.8.4.3 [path.append], 27.10.8.6 [path.non-member] Status: New Submitter: Peter Dimov Opened: 2014-05-30 Last modified: 2016-07-06
Priority: 2
View other active issues in [path.append].
View all other issues in [path.append].
View all issues with New status.
Discussion:
In a recent discussion on the Boost developers mailing list, the semantics of operator /
and other append operations were questioned:
p1 / p2
is required to concatenate the
lexical representation of p1
and p2
, inserting a
preferred separator as needed.
This means that, for example, "c:\x" / "d:\y"
gives
"c:\x\d:\y"
, and that "c:\x" / "\\server\share"
gives "c:\x\\server\share"
. This is rarely, if ever, useful.
An alternative interpretation of p1 / p2
could be that it yields a
path that is the approximation of what p2
would mean if interpreted
in an environment in which p1
is the starting directory.
Under this interpretation, "c:\x" / "d:\y"
gives "d:\y"
,
which is more likely to match what was intended.
I am not saying that this second interpretation is the right one, but I do say
that we have reasons to suspect that the first one (lexical concatenation using
a separator) may not be entirely correct.
This leads me to think that the behavior of p1 / p2
, when p2
has a root, needs to be left implementation-defined, so that implementations are
not required to do the wrong thing, as above.
This change will not affect the ordinary use case in which p2
is a
relative, root-less, path.
[17 Jun 2014 Rapperswil LWG will investigate issue at a subsequent meeting.]
[2016-02, Jacksonville]
Beman to provide wording.
[2016-06-13, Beman provides wording and rationale]
Rationale: The purpose of the append operations is to provide a simple concatenation facility for users
wishing to extend a path by appending one or more additional elements, and to do so without worrying about the
details of when a separator is needed. In that context it makes no sense to provide an argument that has a
root-name. The simplest solution is simply to require !p.has_root_name()
.
The other suggested solutions IMO twist the functions into something harder to reason about
yet any advantages for users are purely speculative. The concatenation functions can
be used instead for corner cases.
[Apr 2016 Issue updated to address the C++ Working Paper. Previously addressed File System TS]
[2016-07-03, Daniel comments]
The same wording area is touched by LWG 2732.
Proposed resolution:
This wording is relative to N4594.
Change 27.10.8.4.3 [path.append] path appends as indicated:
path& operator/=(const path& p);
-?- Requires:
!p.has_root_name()
.-2- Effects: Appends
path::preferred_separator
to pathname unless:
an added directory-separator would be redundant, or
an added directory-separator would change a relative path to an absolute path [Note: An empty path is relative. — end note], or
p.empty()
is true, or
*p.native().cbegin()
is a directory-separator.Then appends
p.native()
to pathname.-3- Returns:
*this
.template <class Source>
path& operator/=(const Source& source);
template <class Source>
path& append(const Source& source);
template <class InputIterator>
path& append(InputIterator first, InputIterator last);-?- Requires:
-4- Effects: Appends!source.has_root_name()
or!*first.has_root_name()
, respectively.path::preferred_separator
to pathname, converting format and encoding if required (27.10.8.2 [path.cvt]), unless:
an added directory-separator would be redundant, or
an added directory-separator would change a relative path to an absolute path, or
source.empty()
is true, or
*source.native().cbegin()
is a directory-separator.Then appends the effective range of
-5- Returns:source
(27.10.8.3 [path.req]) or the range[first, last)
topathname
, converting format and encoding if required (27.10.8.2 [path.cvt]).*this
.
Change 27.10.8.6 [path.non-member] path non-member functions as indicated:
path operator/(const path& lhs, const path& rhs);
-?- Requires:
!rhs.has_root_name()
.-13- Returns:
path(lhs) /= rhs
.
remove_filename()
post condition is incorrectSection: 27.10.8.4.5 [path.modifiers] Status: New Submitter: Eric Fiselier Opened: 2014-06-07 Last modified: 2016-04-15
Priority: 3
View all issues with New status.
Discussion:
remove_filename()
specifies !has_filename()
as the post condition.
This post condition is not correct. For example the path "/foo"
has a filename of "foo"
. If we remove the filename we get "/"
,
and "/"
has a filename of "/"
.
[2014-06-08 Beman supplies an Effects: element.]
[17 Jun 2014 Rapperswil LWG will investigate issue at a subsequent meeting.]
[Apr 2016 Issue updated to address the C++ Working Paper. Previously addressed File System TS]
[2016-04, Issues Telecon]
There was concern that the effects wording is not right. Jonathan provided updated wording.
Previous resolution [SUPERSEDED]:
path& remove_filename();
Postcondition:!has_filename()
.Effects:
*this = parent_path()
, except that ifparent_path() == root_path()
,clear()
.Returns:
*this
.[Example:
std::cout << path("/foo").remove_filename();
// outputs "/
" std::cout << path("/").remove_
filename(); // outputs ""—end example]
Proposed resolution:
path& remove_filename();
Postcondition: !has_filename().Effects: If
*this == root_path()
, thenclear()
. Otherwise,*this = parent_path()
.Returns:
*this
.[Example:
std::cout << path("/foo").remove_filename();
// outputs "/
" std::cout << path("/").remove_
filename(); // outputs ""
—end example]
path::operator+=
is defined, but not operator+
Section: 27.10.8 [class.path] Status: New Submitter: Jonathan Wakely Opened: 2014-07-03 Last modified: 2016-04-14
Priority: 3
View all other issues in [class.path].
View all issues with New status.
Discussion:
This doesn't seem to be in Boost.Filesystem, so maybe it isn't needed,
but since path += path2
works then it seems reasonable to expect
path1 + path2
to work as well.
[04 Jul 2014 Beman Dawes comments:]
The 12 overloads required by
basic_string operator+
scared me off, and I never came back to the issue.
[22 Nov 2015 Beman supplies proposed resolution wording.]
[Apr 2016 Issue updated to address the C++ Working Paper. Previously addressed File System TS]
Proposed resolution:
NAD, Future.
It is not necessary to provide every possible string operation for class
path
because it is always possible to convert to a string, perform the operation, and convert back to a path. The most commonly needed string operations are provided for classpath
as a convenience, but every added function comes at the cost of increased interface complexity. In this case, the cost is judged to outweigh the convenience.Future changes to the language, such as concepts, and changes to the library, such as
basic_string_view
, may allow reduction of the complexity of the class path interface. The LWG may wish to reconsider this issue at that time.
Section: 27.10.15.19 [fs.op.is_empty] Status: New Submitter: Jonathan Wakely Opened: 2014-08-01 Last modified: 2016-05-17
Priority: 3
View all issues with New status.
Discussion:
The descriptions of most functions are explicit about the use of the ec
argument,
either saying something like "foo(p)
or foo(p, ec)
, respectively" or using the
ec
argument like foo(p[, ec])
, but is_empty
does not.
27.10.15.19 [fs.op.is_empty]/2 refers to ec
unconditionally, but more importantly
27.10.15.19 [fs.op.is_empty]/3 doesn't pass ec
to the directory_iterator
constructor or the file_size
function.
[ 9 Oct 2014 Beman supplies proposed wording. ]
[Apr 2016 Issue updated to address the C++ Working Paper. Previously addressed File System TS]
Proposed resolution:
bool is_empty(const path& p); bool is_empty(const path& p, error_code& ec) noexcept;Effects:
- Determine
file_status s
, as if bystatus(p)
orstatus(p, ec)
, respectively.- For the signature with argument
ec
, returnfalse
if an error occurred.- Otherwise, if
is_directory(s)
:
- Create
directory_iterator itr
, as if bydirectory_iterator(p)
ordirectory_iterator(p, ec)
, respectively.- For the signature with argument
ec
, returnfalse
if an error occurred.- Otherwise, return
itr == directory_iterator()
.- Otherwise:
- Determine
uintmax_t sz
, as if byfile_size(p)
orfile_size(p, ec)
, respectively .- For the signature with argument
ec
, returnfalse
if an error occurred.- Otherwise, return
sz == 0
.Remarks: The temporary objects described in Effects are for exposition only. Implementations are not required to create them.
Returns:
See Effects.is_directory(s) ? directory_iterator(p) == directory_iterator() : file_size(p) == 0;
The signature with argumentec
returnsfalse
if an error occurs.Throws: As specified in Error reporting (7).
Section: 27.5.3.6 [ios.base.callback] Status: New Submitter: David Krauss Opened: 2016-03-14 Last modified: 2016-04-15
Priority: 3
View all issues with New status.
Discussion:
register_callback allocates memory and so it can fail, but the case is unspecified. libc++ sets badbit, which is consistent with iword and pword. libstdc++ throws std::bad_alloc.
Proposed resolution:
filesystem::path
overloads for File-based streamsSection: 27.9 [file.streams] Status: New Submitter: Beman Dawes Opened: 2016-03-16 Last modified: 2016-04-15
Priority: 2
View all other issues in [file.streams].
View all issues with New status.
Discussion:
The constructor and open
functions for File-based streams in
27.9 [file.streams] currently provide overloads for const char*
and
const string&
arguments that specify the path for the file to be opened.
With the addition of the File System TS to the standard library, these
constructors and open
functions need to be overloaded for
const filesystem::path&
so that file-based streams can take advantage of class filesystem::path
features such as support for strings of character types wchar_t
,
char16_t
, and char32_t
.
The
const filesystem::path& p
overload for these functions is like the
existing const string&
overload; it simply calls the overload of
the same function that takes a C-style string.
For operating systems like POSIX
that traffic in char
strings for filenames, nothing more is
required. For operating systems like Windows that traffic in wchar_t
strings for filenames, an additional C-style string overload is required.
The overload's character type needs to be specified as
std::filesystem::path::value_type
to also support possible future
operating systems that traffic in char16_t
or char32_t
characters.
Not recommended:
Given the proposed constructor and open
signatures taking
const filesystem::path&
, it would in theory be possible to remove
some of the other signatures. This is not proposed because it would break ABI's, break
user code depending on user-defined automatic conversions to the existing argument types,
and invalidate existing documentation, books, and tutorials.
Implementation experience:
The Boost Filesystem library has provided header <boost/filesystem/fstream.hpp>
implementing the proposed resolution for over a decade.
The Microsoft/Dinkumware
implementation of standard library header <fstream>
has provided
the const wchar_t*
overloads for many years.
Proposed resolution:
At the end of 27.9.1 File streams [fstreams] add a paragraph:
In this subclause, member functions taking arguments of
const std::filesystem::path::value_type*
shall only be provided on systems
where std::filesystem::path::value_type
([class.path]) is not
char
. [Note: These functions enable class path
support for systems with a wide native path character type, such as
wchar_t
. — end note]
Change 27.9.1.1 Class template basic_filebuf [filebuf] as indicated:
basic_filebuf<charT,traits>* open(const char* s, ios_base::openmode mode); basic_filebuf<charT,traits>* open(const std::filesystem::path::value_type* s, ios_base::openmode mode); // wide systems only; see [fstreams] basic_filebuf<charT,traits>* open(const string& s, ios_base::openmode mode); basic_filebuf<charT,traits>* open(const filesystem::path& p, ios_base::openmode mode);
Change 27.9.1.4 Member functions [filebuf.members] as indicated:
basic_filebuf<charT,traits>* open(const char* s, ios_base::openmode mode); basic_filebuf<charT,traits>* open(const std::filesystem::path::value_type* s, ios_base::openmode mode); // wide systems only; see [fstreams]
To 27.9.1.4 Member functions [filebuf.members] add:
basic_filebuf<charT,traits>* open(const filesystem::path& p, ios_base::openmode mode);
Returns:
open(p.c_str(), mode);
Change 27.9.1.6 Class template basic_ifstream [ifstream] as indicated:
explicit basic_ifstream(const char* s, ios_base::openmode mode = ios_base::in); explicit basic_ifstream(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::in); // wide systems only; see [fstreams] explicit basic_ifstream(const string& s, ios_base::openmode mode = ios_base::in); explicit basic_ifstream(const filesystem::path& p, ios_base::openmode mode = ios_base::in); ... void open(const char* s, ios_base::openmode mode = ios_base::in); void open(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::in); // wide systems only; see [fstreams] void open(const string& s, ios_base::openmode mode = ios_base::in); void open(const filesystem::path& p, ios_base::openmode mode = ios_base::in);
Change 27.9.1.7 basic_ifstream constructors [ifstream.cons] as indicated:
explicit basic_ifstream(const char* s, ios_base::openmode mode = ios_base::in); explicit basic_ifstream(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::in); // wide systems only; see [fstreams]
To 27.9.1.7 basic_ifstream constructors [ifstream.cons] add:
explicit basic_ifstream(const filesystem::path& p, ios_base::openmode mode = ios_base::in);
Effects: the same as
basic_ifstream(p.c_str(), mode)
.
Change 27.9.1.9 Member functions [ifstream.members] as indicated:
void open(const char* s, ios_base::openmode mode = ios_base::in); void open(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::in); // wide systems only; see [fstreams]
To 27.9.1.9 Member functions [ifstream.members] add:
void open(const filesystem::path& p, ios_base::openmode mode = ios_base::in);
Effects: calls
open(p.c_str(), mode)
.
Change 27.9.1.10 Class template basic_ofstream [ofstream] as indicated:
explicit basic_ofstream(const char* s, ios_base::openmode mode = ios_base::out); explicit basic_ofstream(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::out); // wide systems only; see [fstreams] explicit basic_ofstream(const string& s, ios_base::openmode mode = ios_base::out); explicit basic_ofstream(const filesystem::path& p, ios_base::openmode mode = ios_base::out); ... void open(const char* s, ios_base::openmode mode = ios_base::out); void open(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::out); // wide systems only; see [fstreams] void open(const string& s, ios_base::openmode mode = ios_base::out); void open(const filesystem::path& p, ios_base::openmode mode = ios_base::out);
Change 27.9.1.11 basic_ofstream constructors [ofstream.cons] as indicated:
explicit basic_ofstream(const char* s, ios_base::openmode mode = ios_base::out); explicit basic_ofstream(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::out); // wide systems only; see [fstreams]
To 27.9.1.11 basic_ofstream constructors [ofstream.cons] add:
explicit basic_ofstream(const filesystem::path& p, ios_base::openmode mode = ios_base::out);
Effects: the same as
basic_ofstream(p.c_str(), mode)
.
Change 27.9.1.13 Member functions [ofstream.members] as indicated:
void open(const char* s, ios_base::openmode mode = ios_base::out); void open(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::out); // wide systems only; see [fstreams]
To 27.9.1.13 Member functions [ofstream.members] add:
void open(const filesystem::path& p, ios_base::openmode mode = ios_base::out);
Effects: calls
open(p.c_str(), mode)
.
Change 27.9.1.14 Class template basic_fstream [fstream] as indicated:
explicit basic_fstream(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out); explicit basic_fstream(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::in|ios_base::out); // wide systems only; see [fstreams] explicit basic_fstream(const string& s, ios_base::openmode mode = ios_base::in|ios_base::out); explicit basic_fstream(const filesystem::path& p, ios_base::openmode mode = ios_base::in|ios_base::out); ... void open(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out); void open(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::in|ios_base::out); // wide systems only; see [fstreams] void open(const string& s, ios_base::openmode mode = ios_base::in|ios_base::out); void open(const filesystem::path& p, ios_base::openmode mode = ios_base::in|ios_base::out);
Change 27.9.1.15 basic_fstream constructors [fstream.cons] as indicated:
explicit basic_fstream(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out); explicit basic_fstream(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::in|ios_base::out); // wide systems only; see [fstreams]
To 27.9.1.15 basic_fstream constructors [fstream.cons] add:
explicit basic_fstream(const filesystem::path& p, ios_base::openmode mode = ios_base::in|ios_base::out);
Effects: the same as
basic_fstream(p.c_str(), mode)
.
Change 27.9.1.17 Member functions [fstream.members] as indicated:
void open(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out); void open(const std::filesystem::path::value_type* s, ios_base::openmode mode = ios_base::in|ios_base::out); // wide systems only; see [fstreams]
To 27.9.1.17 Member functions [fstream.members] add:
void open(const filesystem::path& p, ios_base::openmode mode = ios_base::in|ios_base::out);
Effects: calls
open(p.c_str(), mode)
.
directory_entry::status
is not allowed to be
cached as a quality-of-implementation issueSection: 27.10.12.3 [directory_entry.obs] Status: New Submitter: Billy O'Neal Opened: 2016-03-03 Last modified: 2016-04-15
Priority: 2
View all issues with New status.
Discussion:
To fix multi-threading problems in directory_entry
, caching behavior
was removed from the type. This is bad for performance reasons,
because the values can no longer be cached from the result of readdir
on POSIX platforms, or from FindFirstFile/FindNextFile
on Windows.
It appears that the intent was to allow implementers to fill in the
values for directory_entry::status
inside directory_iterator
, but as
currently specified:
Returns: status(path()[, ec])
.
This is not allowed to be cached, because the target of the path can change. For example, consider the following program:
#include <stdio.h> #include <stdlib.h> #include <filesystem> #include <fstream> using namespace std; namespace fs = ::std::filesystem; void verify(const bool b, const char * const msg) { if (!b) { printf("fail: %s\n", msg); exit(1); } } void touch_file(const char * const filename) { ofstream f(filename); f << '\n' << endl; verify(f.good(), "File write failed"); } int main() { fs::remove_all("subDir"); fs::create_directory("subDir"); fs::create_directory("subDir/child"); touch_file("subDir/child/child"); fs::current_path("./subDir"); fs::directory_iterator dir("."); ++dir; fs::directory_entry entry = *dir; verify(entry == "./child", "unexpected subdirectory"); //enumerating "subDir" returned the directory "child" fs::file_status status = entry.status(); verify(status.type() == fs::file_type::directory, "subDir/child was not a directory"); fs::current_path("./child"); status = entry.status(); // REQUIRED to re-stat() on POSIX, // GetFileAttributes() on Windows verify(status.type() == fs::file_type::regular, "subDir/child/child was not a regular file"); return 0; }
directory_entry
should be re-specified to allow implementers to cache
the value of status(path)
at the time irectory_iterator
was
incremented to avoid repeated stat()
/ GetFileAttributes
calls. (This
may mean additional constructors are necessary for directory_entry
as well)
[2016-04, Issues Telecon]
Beman is working on a paper to address this.
Proposed resolution:
Section: 27.10.10.1 [enum.file_type], 27.10.10.2 [enum.copy_options], 27.10.10.4 [enum.directory_options] Status: New Submitter: Richard Smith Opened: 2016-03-19 Last modified: 2016-05-17
Priority: 3
View all issues with New status.
Discussion:
The enum class std::filesystem::file_type specifies a collection of enumerators for describing the type of a file, but also specifies (1) the numeric values of the enumerators, and (2) that an implementation cannot extend the list with additional types known to it (and by extension, the standard cannot extend the list in future versions without breakage).
These both seem like mistakes in the design. I would suggest we remove the specification of numerical values, and maybe also
we allow implementations to add new implementation-defined value
we replace std::filesystem::file_type::unknown with a value that only means "exists but type could not be determined" and use implementation-specific values for other possibilities.
Similar overspecification exists for std::filesystem::copy_options and std::filesystem::directory_options, where again precise numerical values are specified.
Proposed resolution:
Strike the "Value" column from:
27.10.10.1 [enum.file_type], Table 145 Enum class file_type
27.10.10.2 [enum.copy_options], Table 146 Enum class copy_options
27.10.10.4 [enum.directory_options], Table 148 Enum class enum.directory_options
.
Change 27.10.10.1 [enum.file_type] Table 145 Enum class file_type
as indicated:
Constant Meaning none
The type of the file has not been determined or an error occurred while trying to determine the type. not_found
Pseudo-type indicating the file was not found. [ Note: The file not being found is not considered an error while determining the type of a file. —end note ] regular
Regular file directory
Directory file symlink
Symbolic link file block
Block special file character
Character special file fifo
FIFO or pipe file socket
Socket file implementation-
definedImplementations that support file systems having file types in addition to the above file_type types shall supply implementation-defined file_type
constants to separately identify each of those additional file typesunknown
The file does exist, but is of an operating system dependent type not covered by any of the other cases or the process does not have permission to query the file typeThe file exists but the type could not be determined
Section: 17.5.1.4 [structure.specifications] Status: New Submitter: Dawn Perchik Opened: 2016-04-14 Last modified: 2016-05-17
Priority: 3
View other active issues in [structure.specifications].
View all other issues in [structure.specifications].
View all issues with New status.
Discussion:
Some Effects: were intended to include the "Equivalent to" phrasing where no or different words were used. See examples in std::filesystem and throughout the library.
The wording in [structure.specifications]/4 is incorrect and Effects: throughout the library which use (or were intended to use) the "Equivalent to" phrasing need to be checked to make sure they fit the intended wording.
[2016-04, Issues Telecon]
The PR is fine; but bullet #1 in the report really should have a list of places to change.
Jonathan checked throughout the library for bullet #2 and found two problems in [string.access], which have been added to the PR.Proposed resolution:
Change [structure.specifications]/4 as indicated:
"[…] if F has no Returns: element, a non-void return from F is specified by the
Returns: elementsreturn statements in the code sequence."
Add two return keywords to [string.access]:
const charT& front() const; charT& front();-7- Requires:
!empty()
.-8- Effects: Equivalent to
return operator[](0)
.const charT& back() const; charT& back();-9- Requires:
!empty()
.-10- Effects: Equivalent to
return operator[](size() - 1)
.
Section: 27.10 [filesystems] Status: New Submitter: Beman Dawes Opened: 2016-04-15 Last modified: 2016-05-17
Priority: 2
View all issues with New status.
Discussion:
The IS project editors have requested that filesystem Effects elements specified purely as C++ code include the phrase "Equivalent to" if this is the intent. They do not consider this change to be editorial.
Proposed resolution:
path& replace_filename(const path& replacement);Effects: Equivalent to:
remove_filename(); operator/=(replacement);
void swap(path& lhs, path& rhs) noexcept;Effects: Equivalent to:
lhs.swap(rhs)
template <class charT, class traits> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const path& p);Effects: Equivalent to:
os << quoted(p.string<charT, traits>())
.
template <class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, path& p);Effects: Equivalent to:
basic_string<charT, traits> tmp; is >> quoted(tmp); p = tmp;
void copy(const path& from, const path& to); void copy(const path& from, const path& to, error_code& ec) noexcept;Effects: Equivalent to:
copy(from, to, copy_options::none)
orcopy(from, to, copy_options::none, ec)
, respectively.
void copy_symlink(const path& existing_symlink, const path& new_symlink); void copy_symlink(const path& existing_symlink, const path& new_symlink, error_code& ec) noexcept;Effects: Equivalent to:
function(read_symlink(existing_symlink), new_symlink)
orfunction(read_symlink(existing_symlink, ec), new_symlink, ec)
, respectively, wherefunction
iscreate_symlink
orcreate_directory_symlink
, as appropriate.
filesystem::copy()
cannot copy symlinksSection: 27.10.15.3 [fs.op.copy] Status: New Submitter: Jonathan Wakely Opened: 2016-04-19 Last modified: 2016-06-20
Priority: 2
View other active issues in [fs.op.copy].
View all other issues in [fs.op.copy].
View all issues with New status.
Discussion:
27.10.15.3 [fs.op.copy] paragraph 3 bullet (3.4) says that if is_symlink(f)
and
copy_options
is set in options then it should copy a symlink, but that
case cannot be reached.
is_symlink(f)
can only be true if f was set using
symlink_status(from)
, but that doesn't happen unless one of
create_symlinks
or skip_symlinks
is set in options. It should depend
on copy_symlinks
too.
I'm not sure what the correct behaviour is, but I don't think we
want to simply add a check for copy_symlinks
in bullet (3.1) because
that would mean that t = symlink_status(to)
, and I don't think we want
that. Consider 'touch file; mkdir dir; ln -s dir link;'
and then
filesystem::copy("file", "link",
filesystem::copy_options::copy_symlinks)
. If t = symlink_status(to)
then is_directory(t) == false
, and we don't use bullet (3.5.4) but go
to (3.5.5) instead and fail. So when copy_symlinks
is set we still
need to use t = status(to)
as specified today.
[2016-05 Issues Telecom]
This is related to 2682; and should be considered together.
Proposed resolution:
Modify paragraph 3 of 27.10.15.3 [fs.op.copy] as shown:
Effects: Before the first use of
f
andt
:
— If(options & copy_options::create_symlinks) != copy_options::none || (options & copy_options::skip_symlinks) != copy_options::none
thenauto f = symlink_status(from)
and if neededauto t = symlink_status(to)
.
— Otherwise, if(options & copy_options::copy_symlinks) != copy_options::none
thenauto f = symlink_status(from)
and if neededauto t = status(to)
.
— Otherwise,auto f = status(from)
and if neededauto t = status(to)
.
filesystem::copy()
won't create a symlink to a directorySection: 27.10.15.3 [fs.op.copy] Status: New Submitter: Jonathan Wakely Opened: 2016-04-19 Last modified: 2016-05-22
Priority: 2
View other active issues in [fs.op.copy].
View all other issues in [fs.op.copy].
View all issues with New status.
Discussion:
(First raised in c++std-lib-38544)
filesystem::copy
doesn't create a symlink to a directory in this case:
copy("/", "root", copy_options::create_symlinks);
If the first path is a file then a symlink is created, but I think my
implementation is correct to do nothing for a directory. We get to
bullet 27.10.15.3 [fs.op.copy] (3.6) where is_directory(f)
is true, but options
== create_symlinks
, so we go to the next bullet (3.7) which says
"Otherwise, no effects."
I think the case above should either create a symlink, or should
report an error. GNU cp -s gives an error in this case, printing
"omitting directory '/'". An error seems reasonable, you can use
create_symlink
to create a symlink to a directory.
[2016-05 Issues Telecom]
This is related to 2681; and should be considered together.
Proposed resolution:
Add a new bullet following (3.6) in 27.10.15.3 [fs.op.copy] as shown:
If
!exists(t)
, thencreate_directory(to, from)
.Then, iterate over the files in
from
, as if byfor (directory_entry& x : directory_iterator(from))
, and for each iterationcopy(x.path(), to/x.path().filename(), options | copy_options::unspecified )
Otherwise, if
is_directory(f) && (options & copy_options::create_symlinks) != copy_options::none
, then report an error with anerror_code
argument equal tomake_error_code(errc::is_a_directory)
.Otherwise, no effects.
Section: 19.5.1 [system_error.syn] Status: New Submitter: Tim Song Opened: 2016-05-03 Last modified: 2016-05-22
Priority: 3
View all issues with New status.
Discussion:
Both error_code and error_condition have an operator< overload, enabling their use in associative containers without having to write a custom comparator.
However, only error_code has a std::hash specialization. So it's possible to have a set<error_code>, a set<error_condition>, an unordered_set<error_code>, but not an unordered_set<error_condition>. This seems...odd.
Proposed resolution:
This wording is relative to N4582.
Edit 19.5.1 [system_error.syn], header <system_error> synopsis, as indicated:
namespace std { // ... // 19.5.6 Hash support template<class T> struct hash; template<> struct hash<error_code>; template<> struct hash<error_condition>; // ... }
Edit 19.5.6 [syserr.hash] as indicated:
template <> struct hash<error_code>; template <> struct hash<error_condition>;-1- The template specializations shall meet the requirements of class template hash (20.12.14).
Section: 22.4.6.3 [locale.moneypunct] Status: New Submitter: Hubert Tong Opened: 2016-04-12 Last modified: 2016-05-22
Priority: 3
View all other issues in [locale.moneypunct].
View all issues with New status.
Discussion:
The description of money_base::space is that "at least one space is required at that position." (N4582 subclause 22.4.6.3 [locale.moneypunct] paragraph 2)
When formatting for output (22.4.6.2.2 [locale.money.put.virtuals]), it is not clear that"the number of characters generated for the specified format" (excluding fill padding) includes exactly one character for money_base::space (if present), and
all characters corresponding to money_base::space (excluding fill padding) are copies of fill.
In particular, there is implementation divergence over point (b) as to whether U+0020 or fill should be used. Further, should a character other than fill be used, it is unclear when "the fill characters are placed where none or space appears in the formatting pattern", whether the fill characters are placed at the beginning or the end of the "space field".
I believe that a strict interpretation of the current wording supports U+0020; however, fill is more likely to be the pragmatic choice.Proposed resolution:
This wording is relative to N4582.
Change 22.4.6.3 [locale.moneypunct] paragraph 2 as indicated:
-2- Where none or space appears, white space is permitted in the format, except where none appears at the end, in which case no white space is permitted. For input, the value space indicates that at least one space is required at that position. For output, the value space indicates one instance of the fill character (22.4.6.2.2 [locale.money.put.virtuals]).
The value space indicates that at least one space is required at that position. Where symbol appears, the sequence of characters returned by curr_symbol() is permitted, and can be required. Where sign appears, the first (if any) of the sequence of characters returned by positive_sign() or negative_sign() (respectively as the monetary value is non-negative or negative) is required. Any remaining characters of the sign sequence are required after all other format components. Where value appears, the absolute numeric monetary value is required.
Section: 26.5 [complex.numbers] Status: New Submitter: Oliver Rosten Opened: 2016-04-14 Last modified: 2016-05-22
Priority: 3
View other active issues in [complex.numbers].
View all other issues in [complex.numbers].
View all issues with New status.
Discussion:
This modification will allow complex-number arithmetic to be performed at compile time. From a mathematical standpoint, it is natural (and desirable) to treat complex numbers on the same footing as the reals. From a programming perspective, this change will broaden the scope in which std::complex can be used, allowing it to be smoothly incorporated into classes exploiting constexpr.
Suggested resolution: The following functions in the std::complex namespace should be made constexpr:Section 26.5.5 [complex.member.ops]: The member (arithmetic) operators {+=, -=, /=, *=}
Section 26.5.6 [complex.ops]: The arithmetic operators unary operators {+, -} and binary operators {+, -, /, *};
Section 26.5.7 [complex.value.ops]: The 'value' operators abs, norm, and conj.
In terms of modification of the standard, all that is required is the insertion of the constexpr specifier in the relevant parts of:
Section 26.5.1 [complex.syn] (the Header synopsis), together with the corresponding entries in sections 26.5.6 [complex.ops] and 26.5.7 [complex.value.ops].
Sections 26.5.2 [complex], 26.5.3 [complex.special] (class template and specializations), together with the corresponding entries in section 26.5.5 [complex.member.ops].
[2016-05 Issues Telecom]
This kind of work (new feature) has been being done via papers rather than via the issues list.
Walter believes that he knows someone who would be willing to write such a paper.
Proposed resolution:
Section: 22.3.1.1.2 [locale.facet] Status: New Submitter: Tim Song Opened: 2016-04-15 Last modified: 2016-05-22
Priority: 3
View all other issues in [locale.facet].
View all issues with New status.
Discussion:
[locale.facet]/1 (then-called [lib.locale.facet]) read in C++03:
Class facet is the base class for locale feature sets. A class is a facet if it is publicly derived from another facet, or if it is a class derived from locale::facet and containing a publicly-accessible declaration as follows: [Footnote: This is a complete list of requirements; there are no other requirements. Thus, a facet class need not have a public copy constructor, assignment, default constructor, destructor, etc.]
static ::std::locale::id id;Template parameters in this clause which are required to be facets are those named Facet in declarations. A program that passes a type that is not a facet, as an (explicit or deduced) template parameter to a locale function expecting a facet, is ill-formed.
LWG 436's intent appears to be to ban volatile-qualified facets and permitting const-qualified ones. The PR was somewhat poorly worded, however, and the editor in applying it deleted the whole first half of the paragraph, including the definition of facet and the requirement for a static data member named id.
As a result, we have things like 22.3.1.1.3 [locale.id]/2 and 22.3.2 [locale.global.templates]/1 referring to the id member that's not defined anywhere in the working draft.Proposed resolution:
This wording is relative to N4582.
Insert the following paragraph before 22.3.1.1.2 [locale.facet]/1:
-?- Class facet is the base class for locale feature sets. A class is a facet if it is publicly derived from another facet, or if it is a class derived from locale::facet and containing a publicly accessible declaration as follows: [Footnote: This is a complete list of requirements; there are no other requirements. Thus, a facet class need not have a public copy constructor, assignment, default constructor, destructor, etc.]
static ::std::locale::id id;-1- Template parameters in this Clause which are required to be facets are those named Facet in declarations. A program that passes a type that is not a facet, or a type that refers to a volatile-qualified facet, as an (explicit or deduced) template parameter to a locale function expecting a facet, is ill-formed. A const-qualified facet is a valid template argument to any locale function that expects a Facet template parameter.
Section: 17.6.5.5 [member.functions] Status: New Submitter: Hubert Tong Opened: 2016-04-15 Last modified: 2016-05-22
Priority: 3
View other active issues in [member.functions].
View all other issues in [member.functions].
View all issues with New status.
Discussion:
In N4582 subclause 17.6.5.5 [member.functions], the requirement that:
any call to the member function that would select an overload from the set of declarations described in this standard behaves as if that overload were selected
is unclear in the extent of the "as if". For example, in providing:
basic_string(const charT* s);
for a one-argument call to:
basic_string(const charT* s, const Allocator& a = Allocator());
it can be read that an implementation may be required to call the copy constructor for the allocator since the core language rules for copy elision would not allow the "a" argument to be constructed directly into the member used to store the allocator.
Clarification (even if just a note) would be appreciated.[2016-05 Issues Telecom]
This is related to issue 2563.
Proposed resolution:
Section: 20.11.2.2.6 [util.smartptr.shared.create] Status: New Submitter: Joe Gottman Opened: 2016-04-02 Last modified: 2016-05-22
Priority: 2
View other active issues in [util.smartptr.shared.create].
View all other issues in [util.smartptr.shared.create].
View all issues with New status.
Discussion:
For each public constructor of std::shared_ptr, the standard says that constructor enables shared_from_this if that constructor is expected to initialize the internal weak_ptr of a contained enable_shared_from_this<X> object. But there are other ways to construct a shared_ptr than by using a public constructor. The template functions make_shared and allocate_shared both require calling a private constructor, since no public constructor can fulfill the requirement that at most one allocation is made. The standard does not specify that that private constructor enables shared_from_this; therefore in the following code:
struct Foo : public std::enable_shared_from_this<Foo> {}; int main() { auto p = std::make_shared<Foo>(); assert(p == p->shared_from_this()); return 0; }
it is unspecified whether the assertion will fail.
[2016-05 Issues Telecom]
Jonathan Wakely to provide updated wording.
Proposed resolution:
This wording is relative to N4582.
Change 20.11.2.2.6 [util.smartptr.shared.create] indicated:
template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args); template<class T, class A, class... Args> shared_ptr<T> allocate_shared(const A& a, Args&&... args);[…]
-6- Remarks: The shared_ptr constructor called by this function enables shared_from_this with the address of the newly constructed object of type T. Implementations should perform no more than one memory allocation. [Note: This provides efficiency equivalent to an intrusive smart pointer. — end note]
Section: 99 [concurr.ts::futures.unique_future], 99 [concurr.ts::futures.shared_future] Status: New Submitter: Tim Song Opened: 2016-04-22 Last modified: 2016-05-22
Priority: 2
View other active issues in [concurr.ts::futures.unique_future].
View all other issues in [concurr.ts::futures.unique_future].
View all issues with New status.
Discussion:
Addresses: concurr.ts
In the concurrency TS, the future/shared_future unwrapping constructors
future(future<future<R>>&&) noexcept; shared_future(future<shared_future<R>>&& rhs) noexcept;
appear to implicitly require rhs be valid (e.g., by referring to its shared state, and by requiring a valid() == true postcondition). However, they are also marked noexcept, suggesting that they are wide-contract, and also makes the usual suggested handling for invalid futures, throwing a future_error, impossible.
Either the noexcept should be removed, or the behavior with an invalid future should be specified.Proposed resolution:
This wording is relative to N4577.
Alternative 1:
Strike the noexcept on these constructors in 99 [futures.unique_future]/1-2 and 99 [futures.shared_future]/1-2, and optionally add a Requires: rhs.valid() == true paragraph.
Alternative 2:
Specify that an empty (shared_)future object is constructed if rhs is invalid, and adjust the postcondition accordingly.
Edit 99 [futures.unique_future] as indicated:
future(future<future<R>>&& rhs) noexcept;-3- Effects: If rhs.valid() == false, constructs an empty future object that does not refer to a shared state. Otherwise, c
Constructs a future object from the shared state referred to by rhs. The future becomes ready when one of the following occurs:
Both the rhs and rhs.get() are ready. The value or the exception from rhs.get() is stored in the future's shared state.
rhs is ready but rhs.get() is invalid. An exception of type std::future_error, with an error condition of std::future_errc::broken_promise is stored in the future's shared state.
-4- Postconditions:
valid() == truevalid() returns the same value as rhs.valid() prior to the constructor invocation..rhs.valid() == false.
Edit 99 [futures.shared_future] as indicated:
shared_future(future<shared_future<R>>&& rhs) noexcept;-3- Effects: If rhs.valid() == false, constructs an empty shared_future object that does not refer to a shared state. Otherwise, c
Constructs a shared_future object from the shared state referred to by rhs. The shared_future becomes ready when one of the following occurs:
Both the rhs and rhs.get() are ready. The value or the exception from rhs.get() is stored in the shared_future's shared state.
rhs is ready but rhs.get() is invalid. The shared_future stores an exception of type std::future_error, with an error condition of std::future_errc::broken_promise.
-4- Postconditions:
valid() == truevalid() returns the same value as rhs.valid() prior to the constructor invocation..rhs.valid() == false.
Section: 26.3 [numeric.requirements] Status: New Submitter: Hubert Tong Opened: 2016-05-03 Last modified: 2016-05-22
Priority: 3
View all issues with New status.
Discussion:
In N4582 subclause 26.3 [numeric.requirements], the "considerable flexibility in how arrays are initialized" do not appear to allow for replacement of calls to the default constructor with calls to the copy constructor with an appropriate source value.
Proposed resolution:
This wording is relative to N4582.
Adjust 26.3 [numeric.requirements]/1 as indicated:
-1- The complex and valarray components are parameterized by the type of information they contain and manipulate. […]
(1.1) — T is not an abstract class (it has no pure virtual member functions);
[…]
(1.8) — If T is a class, its assignment operator, copy and default constructors, and destructor shall correspond to each other in the following sense: Initialization of raw storage using the copy constructor on the value of T(), however obtained, is semantically equivalent to value initialization of the same raw storage. Initialization of raw storage using the default constructor, followed by assignment, is semantically equivalent to initialization of raw storage using the copy constructor. Destruction of an object, followed by initialization of its raw storage using the copy constructor, is semantically equivalent to assignment to the original object. [Note: This rule states, in part, that there shall not be any subtle differences in the semantics of initialization versus assignment. This gives an implementation considerable flexibility in how arrays are initialized. [Example: An implementation is allowed to initialize a valarray by allocating storage using the new operator (which implies a call to the default constructor for each element) and then assigning each element its value. Or the implementation can allocate raw storage and use the copy constructor to initialize each element. — end example] If the distinction between initialization and assignment is important for a class, or if it fails to satisfy any of the other conditions listed above, the programmer should use vector (23.3.11) instead of valarray for that class; — end note]
Section: 99 [memory.resource.prot] Status: New Submitter: Jonathan Wakely Opened: 2016-05-04 Last modified: 2016-05-22
Priority: 3
View all issues with New status.
Discussion:
99 [memory.resource.prot] says:
virtual void* do_allocate(size_t bytes, size_t alignment) = 0;-1- Requires: Alignment shall be a power of two.
Is that supposed to be a requirement on the alignment specified by the alignment parameter? If so the word "Alignment" should not be capitalized and in text font.
[2016-05 Issues Telecom]
The resolution should reference [basic.align]. Jonathan to update the wording.
Proposed resolution:
Modify 99 [memory.resource.prot] paragraph 1 as shown:
virtual void* do_allocate(size_t bytes, size_t alignment) = 0;-1- Requires:
AlignmentThe alignment specified byalignment
shall be a power of two.
Section: 22.4.2.2.2 [facet.num.put.virtuals] Status: New Submitter: Hubert Tong Opened: 2016-05-07 Last modified: 2016-05-22
Priority: 3
View other active issues in [facet.num.put.virtuals].
View all other issues in [facet.num.put.virtuals].
View all issues with New status.
Discussion:
The call to do_put(out, str, fill, (int)val) in N4582 subclause 22.4.2.2.2 [facet.num.put.virtuals] paragraph 6 cannot select a best viable function in overload resolution given the overloads listed for do_put in 22.4.2.2 [locale.nm.put].
There is implementation divergence:Some implementations call the long overload (as overriden);
some implementations call the unsigned long overload (as overriden);
some implementations call something else.
It appears that the resolution to DR 359 attempted a fix; however, the relevant portion of the change was not applied to the WP.
Proposed resolution:
Section: 22.4.2.2.2 [facet.num.put.virtuals] Status: New Submitter: Hubert Tong Opened: 2016-05-07 Last modified: 2016-05-22
Priority: 3
View other active issues in [facet.num.put.virtuals].
View all other issues in [facet.num.put.virtuals].
View all issues with New status.
Discussion:
N4582 subclause 22.4.2.2.2 [facet.num.put.virtuals] paragraph 6 makes no provision for fill-padding in its specification of the behaviour when (str.flags() & ios_base::boolalpha) != 0.
Proposed resolution:
Section: 23.2.3 [sequence.reqmts] Status: New Submitter: Kazutoshi Satoda Opened: 2016-05-08 Last modified: 2016-05-22
Priority: 3
View other active issues in [sequence.reqmts].
View all other issues in [sequence.reqmts].
View all issues with New status.
Discussion:
Please look through the following modifications on a value v of type vector<T>:
assert(v.size() > 0); v.push_back(v[0]); v.insert(v.begin(), v[0]); v.resize(v.size() * 2, v[0]); v.assign(v.size() * 2, v[0]);
All of these use an element of itself which may be moved or destroyed by the modification.
From what I see so far, the first three are required to work. Please see library issue 526 for validity of them. But only the last one is undefined because it violates a precondition of a sequence container operation. I think this is too subtle. Should it be like that, really? The precondition is in Table 107 "Sequence container requirements" at the next of 23.2.3 [sequence.reqmts] p3.In Tables 107 and 108, X denotes a sequence container class, a denotes a value of X containing elements of type T, […] n denotes a value of X::size_type, […] t denotes an lvalue or a const rvalue of X::value_type, […]
[…]
Table 107 — Sequence container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-condition[…] a.assign(n, t) void Requires: T shall be CopyInsertable into X and CopyAssignable.
pre: t is not a reference into a.
Replaces elements in a with n copies of t.
I looked into the following implementations:
libc++ relies on the precondition.
It deallocates first on n > capacity() case, see here.libstdc++ doesn't rely on the precondition.
It creates temporary vector(n, t) and swap() on n > capacity() case, see here.MSVC relies on the precondition.
It unconditionally does clear() and then insert(begin(), n, t). I looked into my local "%PROGRAMFILES(X86)%/Microsoft Visual Studio 14.0/VC/include/vector".One drawback of libstdc++ implementation, I could find so far, is possibly increased peek memory usage (both old and new buffer exist at the same time). But, because the same can happen on the most other modifications, it seems a reasonable trade-off to remove the precondition to fill the subtle gap. Users who really needs less memory usage can do clear() and insert() by themselves.
I also found that basic_string::assign(n, c) is safe on this point. At 21.3.1.6.3 [string::assign] p17:basic_string& assign(size_type n, charT c);Effects: Equivalent to assign(basic_string(n, c)).
Returns: *this.
This can be seen as another gap.
Looking back on the history, I found that the definition of assign(n, t) was changed at C++14 for library issue 2209. There were more restricting definitions like this:void assign(size_type n, const T& t);Effects:
erase(begin(), end()); insert(begin(), n, t);
I think the precondition was probably set to accept this old definition and is not required inherently. And if the less memory usage was really intended, the standard is now underspecifying about that.
[2016-05 Issues Telecom]
Howard believes this should be NAD, but we tabled the discussion.
Proposed resolution:
This wording is relative to N4582.
In 23.2.3 [sequence.reqmts], edit Table 107 (Sequence container requirements) as indicated:
Table 107 — Sequence container requirements (in addition to container) Expression Return type Assertion/note
pre-/post-condition[…] a.assign(n, t) void Requires: T shall be CopyInsertable into X and CopyAssignable.
pre: t is not a reference into a.
Replaces elements in a with n copies of t.
Section: 27.10.14.1 [rec.dir.itr.members] Status: New Submitter: Eric Fiselier Opened: 2016-05-09 Last modified: 2016-05-22
Priority: 2
View all other issues in [rec.dir.itr.members].
View all issues with New status.
Discussion:
The current specification of recursion_pending() says (27.10.14.1 [rec.dir.itr.members]/24):
Returns: true if disable_recursion_pending() has not been called subsequent to the prior construction or increment operation, otherwise false.
This language does not take into account cases where the prior construction was a copy construction from a iterator, it, where it.recursion_pending() == false.
Proposed resolution:
This wording is relative to N4582.
Change 27.10.14.1 [rec.dir.itr.members] as indicated:
explicit recursive_directory_iterator(const path& p); recursive_directory_iterator(const path& p, directory_options options); recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept; recursive_directory_iterator(const path& p, error_code& ec) noexcept;[…]
-3- Postcondition:options() == options for the signatures with a directory_options argument, otherwise options() == directory_options::none.
options() == options for the signatures with a directory_options argument, otherwise options() == directory_options::none.
recursion_pending() == true.
[…]
[Drafting note: The following changes the specification of recursion_pending() seemingly recursive. Perhaps it would be easier to specify recursion_pending() in terms of a exposition only member in recursive_directory_iterator.]
bool recursion_pending() const;[…]
-24- Returns:true if disable_recursion_pending() has not been called subsequent to the prior construction or increment operation, otherwise falsefalse if disable_recursion_pending() has been called subsequent to the prior construction or increment operation, otherwise the value of recursion_pending() set by that operation. […]recursive_directory_iterator& operator++(); recursive_directory_iterator& increment(error_code& ec) noexcept;[…]
-27- Effects: As specified by Input iterators (24.2.3), except that: […] -?- Postcondition: recursion_pending() == true.
Section: 27.10.15.4 [fs.op.copy_file] Status: New Submitter: Eric Fiselier Opened: 2016-05-10 Last modified: 2016-06-08
Priority: 2
View all issues with New status.
Discussion:
There are a number of error cases that copy_file(from, to, ...) does not take into account. Specifically the cases where:
These error cases should be specified as such.
[2016-05 Issues Telecom]
Eric to provide wording.
[2016-05-28, Eric Fiselier provides wording]
Proposed resolution:
This wording is relative to N4582.
Modify 27.10.15.4 [fs.op.copy_file] as indicated:
bool copy_file(const path& from, const path& to, copy_options options); bool copy_file(const path& from, const path& to, copy_options options, error_code& ec) noexcept;-3- Requires: At most one constant from each copy_options option group (27.10.10.2) is present in options.
-4- Effects: Report a file already exists error as specified in Error reporting (27.5.6.5) if:
- !is_regular_file(from), or
- exists(to) and !is_regular_file(to), or
- exists(to) and equivalent(from, to), or
- exists(to) and (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none.
Section: 23.5 [unord] Status: New Submitter: Billy Robert O'Neal III Opened: 2016-05-20 Last modified: 2016-06-28
Priority: 3
View all other issues in [unord].
View all issues with New status.
Discussion:
The resolution of LWG 2210 missed constructors accepting a range or initializer list and allocator.
Previous resolution [SUPERSEDED]:
This wording is relative to N4582.
Add to the synopsis in 23.5.4.1 [unord.map.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_map { public: […] unordered_map(size_type n, const hasher& hf, const allocator_type& a) : unordered_map(n, hf, key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a) : unordered_map(f, l, see below, hasher(), key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_map(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_map(f, l, n, hf, key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a) : unordered_map(il, see below, hasher(), key_equal(), a) { } unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_map(il, n, hasher(), key_equal(), a) { } […] }; }Add to the synopsis in 23.5.5.1 [unord.multimap.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_multimap { public: […] unordered_multimap(size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multimap(f, l, see below, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multimap(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(f, l, n, hf, key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a) : unordered_multimap(il, see below, hasher(), key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multimap(il, n, hasher(), key_equal(), a) { } […] }; }Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_set { public: […] unordered_set(size_type n, const hasher& hf, const allocator_type& a) : unordered_set(n, hf, key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a) : unordered_set(f, l, see below, hasher(), key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_set(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_set(f, l, n, hf, key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a) : unordered_set(il, see below, hasher(), key_equal(), a) { } unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_set(il, n, hasher(), key_equal(), a) { } […] }; }Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_multiset { public: […] unordered_multiset(size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multiset(f, l, see below, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multiset(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(f, l, n, hf, key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a) : unordered_multiset(il, see below, hasher(), key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multiset(il, n, hasher(), key_equal(), a) { } […] }; }
[2016-06, Oulu — Daniel comments and provides new wording]
During the LWG discussion of this issue it has been observed, that the interpretation of the embedded see below is not really clear and that we should split declaration and definition of the new overloads, so that we have a place that allows us to specify what "see below" stands for. In addition, the new wording wraps the "see below" as "size_type(see below)" to clarify the provided expression type, similar as we did for the default constructor of unordered_map.
[Oulu, 2016-06]
Alisdair to review wording.
Proposed resolution:
This wording is relative to N4594.
Add to the synopsis in 23.5.4.1 [unord.map.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_map { public: […] unordered_map(size_type n, const hasher& hf, const allocator_type& a) : unordered_map(n, hf, key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_map(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_map(f, l, n, hf, key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a); unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_map(il, n, hasher(), key_equal(), a) { } […] }; }
Insert the following new prototype specification just after 23.5.4.2 [unord.map.cnstr] p2
template <class InputIterator> unordered_map(InputIterator f, InputIterator l, const allocator_type& a) : unordered_map(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_map(initializer_list<value_type> il, const allocator_type& a) : unordered_map(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The number of buckets is implementation-defined.
Add to the synopsis in 23.5.5.1 [unord.multimap.overview] p3:
namespace std { template <class Key, class T, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<std::pair<const Key, T> > > { class unordered_multimap { public: […] unordered_multimap(size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multimap(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multimap(f, l, n, hf, key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a); unordered_multimap(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multimap(il, n, hasher(), key_equal(), a) { } […] }; }
Insert the following new prototype specification just after 23.5.5.2 [unord.multimap.cnstr] p2
template <class InputIterator> unordered_multimap(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multimap(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_multimap(initializer_list<value_type> il, const allocator_type& a) : unordered_multimap(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The number of buckets is implementation-defined.
Add to the synopsis in 23.5.6.1 [unord.set.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_set { public: […] unordered_set(size_type n, const hasher& hf, const allocator_type& a) : unordered_set(n, hf, key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_set(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_set(f, l, n, hf, key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a); unordered_set(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_set(il, n, hasher(), key_equal(), a) { } […] }; }
Insert the following new prototype specification just after 23.5.6.2 [unord.set.cnstr] p2
template <class InputIterator> unordered_set(InputIterator f, InputIterator l, const allocator_type& a) : unordered_set(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_set(initializer_list<value_type> il, const allocator_type& a) : unordered_set(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The number of buckets is implementation-defined.
Add to the synopsis in 23.5.7.1 [unord.multiset.overview] p3:
namespace std { template <class Key, class Hash = hash<Key>, class Pred = std::equal_to<Key>, class Allocator = std::allocator<Key> > { class unordered_multiset { public: […] unordered_multiset(size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(n, hf, key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a); template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_multiset(f, l, n, hasher(), key_equal(), a) { } template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_multiset(f, l, n, hf, key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a); unordered_multiset(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_multiset(il, n, hasher(), key_equal(), a) { } […] }; }
Insert the following new prototype specification just after 23.5.7.2 [unord.multiset.cnstr] p2
template <class InputIterator> unordered_multiset(InputIterator f, InputIterator l, const allocator_type& a) : unordered_multiset(f, l, size_type(see below), hasher(), key_equal(), a) { } unordered_multiset(initializer_list<value_type> il, const allocator_type& a) : unordered_multiset(il, size_type(see below), hasher(), key_equal(), a) { }-?- Remarks: The number of buckets is implementation-defined.
Section: 26.5.6 [complex.ops] Status: New Submitter: Tim Song Opened: 2016-05-23 Last modified: 2016-06-20
Priority: 3
View all other issues in [complex.ops].
View all issues with New status.
Discussion:
The specification of operator>>(istream&, complex<T>&) is extremely short on details. It currently reads, in its entirety (26.5.6 [complex.ops]/12-15):
template<class T, class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, complex<T>& x);Effects: Extracts a complex number x of the form: u, (u), or (u,v), where u is the real part and v is the imaginary part (27.7.2.2 [istream.formatted]).
Requires: The input values shall be convertible to T. If bad input is encountered, calls is.setstate(ios_base::failbit) (which may throw ios::failure (27.5.5.4 [iostate.flags])). Returns: is. Remarks: This extraction is performed as a series of simpler extractions. Therefore, the skipping of whitespace is specified to be the same for each of the simpler extractions.
It is completely unclear:
Proposed resolution:
Drafting note: the following wording is based on:
- Characters are extracted using operator>> and compared using traits::eq.
- Mismatched characters are returned to the stream.
This wording is relative to N4582.
Replace 26.5.6 [complex.ops]/12-15 with the following paragraphs:
template<class T, class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, complex<T>& x);-?- Effects: First, extracts a character from is.
In the description above, characters are extracted from is as if by operator>> (27.7.2.2.3 [istream::extractors]), and returned to the stream as if by basic_istream::putback (27.7.2.3 [istream.unformatted]). Character equality is determined using traits::eq. An object t of type T is extracted from is as if by is >> t. If any extraction operation fails, no further operation is performed and the whole extraction fails. On failure, calls is.setstate(ios_base::failbit) (which may throw ios::failure (27.5.5.4 [iostate.flags])). -?- Returns: is. -?- [Note: This extraction is performed as a series of simpler extractions. Therefore, the skipping of whitespace is specified to be the same for each of the simpler extractions. — end note]
- If the character extracted is equal to is.widen('('), extracts an object u of type T from is, then extracts a character from is.
- If this character is equal to is.widen(')'), then assigns complex<T>(u) to x.
- Otherwise, if this character is equal to is.widen(','), extracts an object v of type T from is, then extracts a character from is. If this character is equal to is.widen(')'), then assigns complex<T>(u, v) to x; otherwise returns the character to is and the extraction fails.
- Otherwise, returns the character to is and the extraction fails.
- Otherwise, returns the character to is, extracts an object u of type T from is, and assigns complex<T>(u) to x.
Section: 29.5 [atomics.types.generic] Status: New Submitter: S. B. Tam Opened: 2016-05-24 Last modified: 2016-06-20
Priority: 3
View other active issues in [atomics.types.generic].
View all other issues in [atomics.types.generic].
View all issues with New status.
Discussion:
29.5 [atomics.types.generic] mentions 'aggregate initialization syntax'. It's unclear what it stands for, especially since atomic types are actually not aggregate types (they have user-provided constructors).
Proposed resolution:
Section: 27.10.15.13 [fs.op.equivalent] Status: New Submitter: Eric Fiselier Opened: 2016-05-28 Last modified: 2016-07-06
Priority: 3
View all issues with New status.
Discussion:
The spec for equivalent has a throws clause which reads: [fs.op.equivalent]/5
Throws: filesystem_error if (!exists(s1) && !exists(s2)) || (is_other(s1) && is_other(s2)), otherwise as specified in Error reporting (27.10.7).
This explicit requirement to throw is incorrect for the equivalent overload which takes an error_code.
Previous resolution [SUPERSEDED]:
This wording is relative to N4582.
Modify 27.10.15.13 [fs.op.equivalent] as follows:
bool equivalent(const path& p1, const path& p2); bool equivalent(const path& p1, const path& p2, error_code& ec) noexcept;-1- Effects: Determines file_status s1 and s2, as if by status(p1) and status(p2), respectively.
-2- Returns: If (!exists(s1) && !exists(s2)) || (is_other(s1) && is_other(s2)) an error is reported (27.10.7 [fs.err.report]). Otherwise true, if s1 == s2 and p1 and p2 resolve to the same file system entity, else false. The signature with argument ec returns false if an error occurs. -3- Two paths are considered to resolve to the same file system entity if two candidate entities reside on the same device at the same location. This is determined as if by the values of the POSIX stat structure, obtained as if by stat() for the two paths, having equal st_dev values and equal st_ino values. -4- Throws:filesystem_error if (!exists(s1) && !exists(s2)) || (is_other(s1) && is_other(s2)), otherwise aAs specified inEerror reporting (27.10.7 [fs.err.report]).
[2016-06, Oulu — Daniel provides wording improvements]
mc: do we have an error reporting clause?
jw: there is no such clause
gr: it should go into the effects clause
dk: we have the same issue for file_size
dk: the right place is the effects clause
Proposed resolution:
This wording is relative to N4594.
Modify 27.10.15.13 [fs.op.equivalent] as follows:
bool equivalent(const path& p1, const path& p2); bool equivalent(const path& p1, const path& p2, error_code& ec) noexcept;-1- Let s1 and s2 be file_statuss, determined as if by status(p1) and status(p2), respectively.
-2- Effects: Determines s1 and s2. If (!exists(s1) && !exists(s2)) || (is_other(s1) && is_other(s2)) an error is reported (27.10.7 [fs.err.report]). -3- Returns: true, if s1 == s2 and p1 and p2 resolve to the same file system entity, else false. The signature with argument ec returns false if an error occurs. -4- Two paths are considered to resolve to the same file system entity if two candidate entities reside on the same device at the same location. This is determined as if by the values of the POSIX stat structure, obtained as if by stat() for the two paths, having equal st_dev values and equal st_ino values. -5- Throws:filesystem_error if (!exists(s1) && !exists(s2)) || (is_other(s1) && is_other(s2)), otherwise aAs specified inEerror reporting (27.10.7 [fs.err.report]).
Section: 20.4.2 [pairs.pair], 20.5.2.2 [tuple.assign] Status: New Submitter: Richard Smith Opened: 2016-06-07 Last modified: 2016-07-06
Priority: Not Prioritized
View other active issues in [pairs.pair].
View all other issues in [pairs.pair].
View all issues with New status.
Discussion:
std::is_copy_assignable<std::pair<int, std::unique_ptr<int>>>::value is true, and should be false. We're missing a "shall not participate in overload resolution unless" for pair's operator=, and likewise for tuple.
Proposed resolution:
Section: 18.3.2.1 [limits.numeric] Status: New Submitter: Richard Smith Opened: 2016-06-09 Last modified: 2016-07-06
Priority: Not Prioritized
View all issues with New status.
Discussion:
I've received this report at the project editor mail alias, and it seems like it may be worthy of a LWG issue:
I recently had this problem:
- I was storing data in a vector of __uint128_ts
- I used a sorting library which used numeric_limits<T>::max() as a sentinel value
- GCC's libstdc++ provides a numeric_limits specialisation for that type, but
- Clang's libc++ does not.
This broke the sorting for me on different platforms, and it was quite difficult to determine why. If the default numeric_limits didn't default to 0s and false values (18.3.2.4 of N4582), and instead static_asserted, causing my code to not compile, I would have found the solution immediately.
I know that __uint128_t is non-standard, so neither GCC nor Clang is doing the wrong thing nor the right thing here. I could just submit a patch to libc++ providing the specialisations, but it doesn't fix the problem at its core. I am wondering, what is the rationale behind the defaults being 0 and false? It seems like it is inviting a problem for any future numeric types, whether part of a library, compiler extension, and possibly even future updates to C++'s numeric types. I think it would be much better to prevent code that tries to use unspecified numeric_limits from compiling.
An alternative to this suggestion would be to still define the primary template, but not provide any of the members except is_specialized. Either way, this would make numeric_limits members SFINAEable.
Along the same lines, one might wonder why the members that only make sense for floating-point types are required to be defined to nonsense values for integer types.Proposed resolution:
Section: 30.4.2.1 [thread.lock.guard] Status: New Submitter: Eric Fiselier Opened: 2016-06-13 Last modified: 2016-07-06
Priority: Not Prioritized
View all other issues in [thread.lock.guard].
View all issues with New status.
Discussion:
In the synopsis of 30.4.2.1 [thread.lock.guard] the mutex_type typedef is specified as follows:
template <class... MutexTypes> class lock_guard { public: typedef Mutex mutex_type; // If MutexTypes... consists of the single type Mutex […] };
The comment seems ambiguous as it could mean either:
I originally took the language to mean (2), but upon further review it seems that (1) is the intended interpretation, as suggested in the LEWG discussion in Lenexa.
I think the language should be clarified to prevent implementation divergence.Proposed resolution:
This wording is relative to N4594.
Edit 30.4.2.1 [thread.lock.guard]/1, class template lock_guard synopsis, as indicated:
template <class... MutexTypes> class lock_guard { public: typedef Mutex mutex_type; // Only iIf MutexTypes...consists of theexpands to a single type Mutex […] };
Section: 27.10.8.4.3 [path.append] Status: New Submitter: Tim Song Opened: 2016-06-14 Last modified: 2016-07-06
Priority: Not Prioritized
View other active issues in [path.append].
View all other issues in [path.append].
View all issues with New status.
Discussion:
The current specification of operator/= taking a const Source& parameter, and of path::append in 27.10.8.4.3 [path.append] appears to require Source to have a native() and an empty() member, and seemingly requires different behavior for append(empty_range) and append(first, last) when first == last (the last two bullet points being specified with source alone), which doesn't make any sense.
It appears that these overloads can just be specified using the operator/=(const path&) overload.[2016-07-03, Daniel comments]
The same wording area is affected by LWG 2664.
Proposed resolution:
This wording is relative to N4594.
Edit 27.10.8.4.3 [path.append]/4-5 as indicated:
template <class Source> path& operator/=(const Source& source); template <class Source> path& append(const Source& source);-?- Effects: operator/=(path(source))
-?- Returns: *this.template <class InputIterator> path& append(InputIterator first, InputIterator last);-4- Effects:
Appends path::preferred_separator to pathname, converting format and encoding if required (27.10.8.2 [path.cvt]), unless:
an added directory-separator would be redundant, or
an added directory-separator would change an relative path to an absolute path, or
source.empty() is true, or
*source.native().cbegin() is a directory-separator.-5- Returns: *this.
Then appends the effective range of source (27.10.8.3 [path.req]) or the range [first, last) to pathname, converting format and encoding if required (27.10.8.2 [path.cvt])operator/=(path(first, last)).
Section: 99 [fund.ts.v2::numeric.ops.gcd], 99 [fund.ts.v2::numeric.ops.lcm] Status: New Submitter: Richard Smith Opened: 2016-06-15 Last modified: 2016-07-06
Priority: Not Prioritized
View all issues with New status.
Discussion:
Addresses: fund.ts.v2
According to N4562, gcd and lcm support bool as the operand type. The wording doesn't appear to cover the behavior for that case, since bool does not have a zero value and gcd / lcm are not normally mathematically defined over {false, true}.
Presumably gcd and lcm shouldn't accept arguments of type boolProposed resolution:
Section: 27.10.8.4.4 [path.concat] Status: New Submitter: Tim Song Opened: 2016-06-16 Last modified: 2016-07-06
Priority: Not Prioritized
View all issues with New status.
Discussion:
27.10.8.4.4 [path.concat] specifies that the postcondition for
path& operator+=(const path& x); path& operator+=(const string_type& x); path& operator+=(const value_type* x); path& operator+=(value_type x); template<class Source> path& operator+=(const Source& x); template<class EcharT> path& operator+=(EcharT x); template<class Source> path& concat(const Source& x); template<class InputIterator> path& concat(InputIterator first, InputIterator last);
is
native() == prior_native + effective-argument
where effective-argument is
It also says that
If the value type of effective-argument would not be path::value_type, the actual argument or argument range is first converted (27.10.8.2.2 [path.type.cvt]) so that effective-argument has value type path::value_type.
There are several problems with this specification:
First, there is no overload taking "source" (note the lower case); all single-argument overloads take "x". Second, there's nothing that defines what it means to use operator+ on a string and an iterator range; clearly concatentation is intended but there is no wording to that effect. Third, the final portion uses "value type", but the "value type" of a single character is not a defined concept. Also, the reference only to 27.10.8.2.2 [path.type.cvt] seems to imply that any format conversion specified in 27.10.8.2.1 [path.fmt.cvt] will not be performed, in seeming contradiction to the rule that native() is to return the native pathname format (27.10.8.4.6 [path.native.obs]/1). Is that intended?Proposed resolution:
Section: 26.9 [c.math] Status: New Submitter: Jörn Heusipp Opened: 2016-06-16 Last modified: 2016-07-06
Priority: Not Prioritized
View other active issues in [c.math].
View all other issues in [c.math].
View all issues with New status.
Discussion:
Consider this C++98 program:
#include <cmath> #include <cstdlib> int main() { return std::abs(static_cast<short>(23)) % 42; }
This works fine with C++98 compilers. At the std::abs(short) call, short gets promoted to int and std::abs(int) is called.
C++11 added the following wording on page 1083 §26.9 p15 b2 [c.math]:Otherwise, if any argument of arithmetic type corresponding to a double parameter has type double or an integer type, then all arguments of arithmetic type corresponding to double parameters are effectively cast to double.
C++17 draft additionally adds on page 1080 §26.9 p10 [c.math]:
If abs() is called with an argument of type X for which is_unsigned<X>::value is true and if X cannot be converted to int by integral promotion (4.5), the program is ill-formed. [Note: Arguments that can be promoted to int are permitted for compatibility with C. — end note]
It is somewhat confusing and probably even contradictory to on the one hand specify abs() in terms of integral promotion in §26.9 p10 and on the other hand demand all integral types to be converted to double in §26.9 p15 b2.
Most compilers (each with their own respective library implementation) I tested (MSVC, Clang, older GCC) appear to not consider §26.9 p15 b2 for std::abs and compile the code successfully. GCC 4.5-5.3 (for std::abs but not for ::abs) as well as GCC >=6.0 (for both std::abs and ::abs) fail to compile in the following way: Taking §26.9 p15 b2 literally and applying it to abs() (which is listed in §26.9 p12) results in abs(short) returning double, and with operator% not being specified for double, this makes the programm ill-formed. I do acknowledge the reason for the wording and semantics demanded by §26.9 p15 b2, i.e. being able to call math functions with integral types or with partly floating point types and partly integral types. Converting integral types to double certainly makes sense here for all the other floating point math functions. However, abs() is special. abs() has overloads for the 3 wider integral types which return integral types. abs() originates in the C standard in stdlib.h and had originally been specified for integral types only. Calling it in C with a short argument returns an int. Calling std::abs(short) in C++98 also returns an int. Calling std::abs(short) in C++11 and later with §26.9 p15 b2 applied to abs() suddenly returns a double. Additionally, this behaviour also breaks third-party C headers which contain macros or inline functions calling abs(short). As per discussion on std-discussion, my reading of the standard as well as GCC's interpretation seem valid. However, as can be seen, this breaks existing code. In addition to the compatibilty concerns, having std::abs(short) return double is also very confusing and unintuitive. The other (possibly, depending on their respective size relative to int) affected types besides short are signed char, unsigned char and unsigned short, and also char, char16_t, char32_t and wchar_t, (all of these are or may be promotable to int). Wider integral types are not affected because explicit overloads are specified for those types by §26.9 p6, §26.9 p7 and §26.9 p9. div() is also not affected because it is neither listed in §26.9 p12, nor does it actually provide any overload for double at all. As far as I can see, the proposed or implemented solutions for LWG 2294, 2192 and/or 2086 do not resolve this issue. I think both, §26.9 p10 [c.math] and §26.9 p15 [c.math] need some correction and clarification. (Note: These changes would explicitly render the current implementation in GCC's libstdc++ non-conforming, which would be a good thing, as outlined above.)Proposed resolution:
This wording is relative to N4594.
Modify 26.9 [c.math] as indicated:
-10- If abs() is called with an argument of type X for which is_unsigned<X>::value is true and if X cannot be converted to int by integral promotion (4.5), the program is ill-formed. If abs() is called with an argument of type X which can be converted to int by integral promotion (4.5), the argument is promoted to int. [Note: Arguments that can be promoted to int are promoted to int in order to keep
[…] -15- Moreover, there shall be additional overloads for these functions, with the exception of abs(), sufficient to ensure:permitted forcompatibility with C. — end note]
If any argument of arithmetic type corresponding to a double parameter has type long double, then all arguments of arithmetic type (3.9.1) corresponding to double parameters are effectively cast to long double.
Otherwise, if any argument of arithmetic type corresponding to a double parameter has type double or an integer type, then all arguments of arithmetic type corresponding to double parameters are effectively cast to double.
Otherwise, all arguments of arithmetic type corresponding to double parameters have type float.
See also: ISO C 7.5, 7.10.2, 7.10.6.
[Note: abs() is exempted from these rules in order to stay compatible with C. — end note]
Section: 20.6.4 [optional.nullopt] Status: New Submitter: Tim Song Opened: 2016-06-17 Last modified: 2016-07-06
Priority: Not Prioritized
View all issues with New status.
Discussion:
20.6.4 [optional.nullopt]/2 requires of nullopt_t that
Type nullopt_t shall not have a default constructor. It shall be a literal type. Constant nullopt shall be initialized with an argument of literal type.
This does not appear sufficient to foreclose the following implementation:
struct nullopt_t { constexpr nullopt_t(const nullopt_t&) = default; }; constexpr nullopt_t nullopt(nullopt_t{});
But such a nullopt_t is still constructible from {} and so still makes opt = {} ambiguous.
Proposed resolution:
Section: 18.6.2.1 [new.delete.single] Status: New Submitter: Clark Nelson Opened: 2016-06-21 Last modified: 2016-07-06
Priority: Not Prioritized
View all other issues in [new.delete.single].
View all issues with New status.
Discussion:
It should be considered whether the description of the single-object allocation functions should say "or smaller", like the array allocation functions. For example, according to 18.6.2.1 [new.delete.single] p1 (emphasis mine):
The allocation function (3.7.4.1) called by a new-expression (5.3.4) to allocate size bytes of storage suitably aligned to represent any object of that size.
In contrast to this, 18.6.2.2 [new.delete.array] p1 says (emphasis mine):
The allocation function (3.7.4.1) called by the array form of a new-expression (5.3.4) to allocate size bytes of storage suitably aligned to represent any array object of that size or smaller. (footnote: It is not the direct responsibility of operator new[](std::size_t) or operator delete[](void*) to note the repetition count or element size of the array. Those operations are performed elsewhere in the array new and delete expressions. The array new expression, may, however, increase the size argument to operator new[](std::size_t) to obtain space to store supplemental information.)
Proposed resolution:
Section: 20.15.4.3 [meta.unary.prop] Status: New Submitter: S. B. Tam Opened: 2016-06-22 Last modified: 2016-07-06
Priority: Not Prioritized
View other active issues in [meta.unary.prop].
View all other issues in [meta.unary.prop].
View all issues with New status.
Discussion:
LWG 2560 mention that there is no variable of function type. There's also no variable of void type, so should 20.15.4.3 [meta.unary.prop] also explicitly say that for a void type T, is_constructible<T, Args...>::value is false?
[2016-07-03, Daniel provides wording]
Proposed resolution:
This wording is relative to N4594.
Change 20.15.4.3 [meta.unary.prop], Table 52 — "Type property predicates", as indicated:
Table 52 — Type property predicates Template Condition Preconditions … template <class T, class... Args>
struct is_constructible;For a function type T
or for a (possibly cv-qualified) void type T,
is_constructible<T, Args...>::value
is false, otherwise see belowT and all types in the
parameter pack Args shall
be complete types,
(possibly cv-qualified)
void, or arrays of
unknown bound.…
Section: 20.17.6.5 [time.point.nonmember] Status: New Submitter: Michael Winterberg Opened: 2016-06-23 Last modified: 2016-07-06
Priority: Not Prioritized
View all other issues in [time.point.nonmember].
View all issues with New status.
Discussion:
In N4594, 20.17.6.5 [time.point.nonmember], operator-(time_point, duration) is specified as:
template <class Clock, class Duration1, class Rep2, class Period2> constexpr time_point<Clock, common_type_t<Duration1, duration<Rep2, Period2>>> operator-(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs);-3- Returns: lhs + (-rhs).
When Rep2 is an unsigned integral type, the behavior is quite different with arithmetic of the underlying integral types because of the requirement to negate the incoming duration and then add that. It also ends up producing different results than the underlying durations as well as the non-member time_point::operator-=.
Consider this program:#include <chrono> #include <iostream> #include <cstdint> using namespace std; using namespace std::chrono; int main() { const duration<uint32_t> unsignedSeconds{5}; auto someValue = system_clock::from_time_t(200); cout << system_clock::to_time_t(someValue) << '\n'; cout << system_clock::to_time_t(someValue - unsignedSeconds) << '\n'; someValue -= unsignedSeconds; cout << system_clock::to_time_t(someValue) << '\n'; std::chrono::seconds signedDur{200}; cout << signedDur.count() << '\n'; cout << (signedDur - unsignedSeconds).count() << '\n'; signedDur -= unsignedSeconds; cout << signedDur.count() << '\n'; }
The goal of the program is to compare the behavior of time_point non-member operator-, time_point member operator-=, duration non-member operator-, and duration member operator-= with basically the same inputs.
libc++ produces this output, which appears mandated by the standard:200 4294967491 195 200 195 195
On the other hand, libstdc++ produces this output, which is what I "intuitively" expect and behaves more consistently:
200 195 195 200 195 195
Given the seemingly brief coverage of durations with unsigned representations in the standard, this seems to be an oversight rather than a deliberate choice for this behavior. Additionally, there may be other "unexpected" behaviors with durations with an unsigned representation, this is just the one that I've come across.
Proposed resolution:
This wording is relative to N4594.
Change 20.17.6.5 [time.point.nonmember] as indicated:
template <class Clock, class Duration1, class Rep2, class Period2> constexpr time_point<Clock, common_type_t<Duration1, duration<Rep2, Period2>>> operator-(const time_point<Clock, Duration1>& lhs, const duration<Rep2, Period2>& rhs);-3- Returns:
lhs + (-rhs)CT(lhs.time_since_epoch() - rhs), where CT is the type of the return value.
Section: 20.6.3.5 [optional.object.observe] Status: New Submitter: Agustín K-ballo Bergé Opened: 2016-07-02 Last modified: 2016-07-06
Priority: Not Prioritized
View all issues with New status.
Discussion:
optional<T>::operator->s are constrained to be constexpr functions only when T is not a type with an overloaded unary operator&. This constrain comes from the need to use addressof (or a similar mechanism), and the inability to do so in a constant expression in C++14. Given that addressof is now constexpr, this constrain is no longer needed.
Proposed resolution:
This wording is relative to N4594.
Modify 20.6.3.5 [optional.object.observe] as indicated:
constexpr T const* operator->() const; constexpr T* operator->();-1- Requires: *this contains a value.
-2- Returns: val. -3- Throws: Nothing. -4- Remarks:Unless T is a user-defined type with overloaded unary operator&, tThese functions shall be constexpr functions.
Section: 25.4.14 [alg.partitions] Status: New Submitter: Jonathan Wakely Opened: 2016-07-06 Last modified: 2016-07-10
Priority: Not Prioritized
View all other issues in [alg.partitions].
View all issues with New status.
Discussion:
Requires: InputIterator's value type shall be convertible to Predicate's argument type.
This seems to date from the days of adaptable function objects with an argument_type typedef, but in modern C++ the predicate might not have an argument type. It could have a function template that accepts various arguments, so it doesn't make sense to state requirements in terms of a type that isn't well defined.
Proposed resolution:
This wording is relative to N4594.
Edit 25.4.14 [alg.partitions] as indicated:
template <class InputIterator, class Predicate> bool is_partitioned(InputIterator first, InputIterator last, Predicate pred);-1- Requires:
[…]InputIterator's value type shall be convertible to Predicate's argument typeThe expression pred(*i) shall be well-formed for all i in [first, last).template <class InputIterator, class OutputIterator1, class OutputIterator2, class Predicate> pair<OutputIterator1, OutputIterator2> partition_copy(InputIterator first, InputIterator last, OutputIterator1 out_true, OutputIterator2 out_false, Predicate pred);-12- Requires: InputIterator's value type shall be CopyAssignable, and shall be writable (24.2.1 [iterator.requirements.general]) to the out_true and out_false OutputIterators, and
[…]shall be convertible to Predicate's argument typethe expression pred(*i) shall be well-formed for all i in [first, last). The input range shall not overlap with either of the output ranges.template<class ForwardIterator, class Predicate> ForwardIterator partition_point(ForwardIterator first, ForwardIterator last, Predicate pred);-16- Requires:
[…]ForwardIterator's value type shall be convertible to Predicate's argument typeThe expression pred(*i) shall be well-formed for all i in [first, last). [first, last) shall be partitioned by pred, i.e. all elements that satisfy pred shall appear before those that do not.
Section: 21.3.1.2 [string.cons] Status: New Submitter: Richard Smith Opened: 2016-07-06 Last modified: 2016-07-10
Priority: Not Prioritized
View all other issues in [string.cons].
View all issues with New status.
Discussion:
Generally, basic_string has a constructor matching each assign function and vice versa (except the constructor takes an allocator where assign does not). P0254R2 violates this by adding an assign(basic_string_view, size_type pos, size_type n = npos) but no corresponding constructor.
Proposed resolution:
Section: 23.1.1.1 [container.node.overview] Status: New Submitter: Richard Smith Opened: 2016-07-08 Last modified: 2016-07-10
Priority: Not Prioritized
View all issues with New status.
Discussion:
The private members of node_handle are missing the usual "exposition only" comment. As a consequence, ptr_ and alloc_ now appear to be names defined by the library (so programs defining these names as macros before including a library header have undefined behavior).
Presumably this is unintentional and these members should be considered to be for exposition only. It's also not clear whether the name node_handle is reserved for library usage or not; 23.1.1.1 [container.node.overview]/3 says the implementation need not provide a type with this name, but doesn't seem to rule out the possibility that an implementation will choose to do so regardless.Daniel:
A similar problem seems to exist for the exposition-only type call_wrapper from p0358r1, which exposes a private data member named fd and a typedef FD.Proposed resolution: