Doc. no. N3930
Date: 2014-02-14
Project: Programming Language C++
Reply to: Alisdair Meredith <lwgchair@gmail.com>

Immediate Issues


1450. Contradiction in regex_constants

Section: 28.5.2 [re.matchflag] Status: Immediate Submitter: BSI Opened: 2010-08-25 Last modified: 2014-02-13

View all issues with Immediate status.

Discussion:

Addresses GB-127

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

[ Resolution proposed by ballot comment: ]

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

[ 2010-10-31 Daniel comments: ]

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

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

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

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

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

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

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

[ 2011 Bloomington ]

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

Pete Becker has also provided an alternative resolution.

Ammend 17.5.2.1.3 [bitmask.types]:

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

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

to

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

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

Change bullet 3 of paragraph 4:

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

[2014-02-13 Issaquah:]

Proposed resolution:

Ammend 17.5.2.1.3 [bitmask.types] p3:

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

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

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


2003. String exception inconsistency in erase.

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

View other active issues in [string.require].

View all other issues in [string.require].

View all issues with Immediate status.

Discussion:

Clause 21.4.1 [string.require]p3 states:

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

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

Throws: out_of_range if pos > size().

[2011-03-24 Madrid meeting]

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

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

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

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

[2014-02-12 Issaquah meeting]

Jeffrey: Madrid meeting's proposed wording wasn't applied, and it's better than the original proposed wording. However, this sentence is only doing 3 functions' worth of work, unlike the similar paragraphs in 23.2.1 [container.requirements.general]. Suggest just putting "Throws: Nothing" on the 3 functions.

[2014-02-13 Issaquah meeting]

Move as Immmediate

Proposed resolution:

Remove [string.require]p/3:

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

Add to the specifications of iterator erase(const_iterator p);, iterator erase(const_iterator first, const_iterator last);, and void pop_back(); in 21.4.6.5 [string::erase]:

Throws: Nothing


2104. unique_lock move-assignment should not be noexcept

Section: 30.4.2.2 [thread.lock.unique] Status: Immediate Submitter: Anthony Williams Opened: 2011-11-27 Last modified: 2014-02-14

View all issues with Immediate status.

Discussion:

I just noticed that the unique_lock move-assignment operator is declared noexcept. This function may call unlock() on the wrapped mutex, which may throw.

Suggested change: remove the noexcept specification from unique_lock::operator=(unique_lock&&) in 30.4.2.2 [thread.lock.unique] and 30.4.2.2.1 [thread.lock.unique.cons].

Daniel:

I think the situation is actually a bit more complex as it initially looks.

First, the effects of the move-assignment operator are (emphasize mine):

Effects: If owns calls pm->unlock().

Now according to the BasicLockable requirements:

m.unlock()

3 Requires: The current execution agent shall hold a lock on m.

4 Effects: Releases a lock on m held by the current execution agent.

Throws: Nothing.

This shows that unlock itself is a function with narrow contract and for this reasons no unlock function of a mutex or lock itself does have a noexcept specifier according to our mental model.

Now the move-assignment operator attempts to satisfy these requirement of the function and calls it only when it assumes that the conditions are ok, so from the view-point of the caller of the move-assignment operator it looks as if the move-assignment operator would in total a function with a wide contract.

The problem with this analysis so far is, that it depends on the assumed correctness of the state "owns".

Looking at the construction or state-changing functions, there do exist several ones that depend on caller-code satisfying the requirements and there is one guy, who looks most suspicious:

unique_lock(mutex_type& m, adopt_lock_t);

11 Requires: The calling thread own the mutex.
[…]
13 Postconditions: pm == &m and owns == true.

because this function does not even call lock() (which may, but is not required to throw an exception if the calling thread does already own the mutex). So we have in fact still a move-assignment operator that might throw an exception, if the mutex was either constructed or used (call of lock) incorrectly.

The correct fix seems to me to also add a "Throws: Nothing" element to the move-assignment operator, because using it correctly shall not throw an exception.

[Issaquah 20014-10-11: Move to Immediate after SG1 review]

Proposed resolution:

This wording is relative to the FDIS.

  1. Change 30.4.2.2 [thread.lock.unique], class template unique_lock synopsis as indicated:

    namespace std {
      template <class Mutex>
      class unique_lock {
      public:
        typedef Mutex mutex_type;
        […]
        unique_lock(unique_lock&& u) noexcept;
        unique_lock& operator=(unique_lock&& u) noexcept;
        […]
      };
    }
    
  2. Change 30.4.2.2.1 [thread.lock.unique.cons] around p22 as indicated:

    unique_lock& operator=(unique_lock&& u) noexcept;
    

    -22- Effects: If owns calls pm->unlock().

    -23- Postconditions: pm == u_p.pm and owns == u_p.owns (where u_p is the state of u just prior to this construction), u.pm == 0 and u.owns == false.

    -24- [Note: With a recursive mutex it is possible for both *this and u to own the same mutex before the assignment. In this case, *this will own the mutex after the assignment and u will not. — end note]

    -??- Throws: Nothing.


2112. User-defined classes that cannot be derived from

Section: 17.6.5 [conforming], 20.7.8 [allocator.traits], 20.13.1 [allocator.adaptor.syn] Status: Immediate Submitter: Daniel Krügler Opened: 2011-11-30 Last modified: 2014-02-14

View all other issues in [conforming].

View all issues with Immediate status.

Discussion:

It is a very established technique for implementations to derive internally from user-defined class types that are used to customize some library component, e.g. deleters and allocators are typical candidates. The advantage of this approach is to possibly take advantage of the empty-base-class optimization (EBCO).

Whether or whether not libraries did take advantage of such a detail didn't much matter in C++03. Even though there did exist a portable idiom to prevent that a class type could be derived from, this idiom has never reached great popularity: The technique required to introduce a virtual base class and it did not really prevent the derivation, but only any construction of such a type. Further, such types are not empty as defined by the std::is_empty trait, so could easily be detected by implementations from TR1 on.

With the new C++11 feature of final classes and final member functions it is now very easy to define an empty, but not derivable from class type. From the point of the user it is quite natural to use this feature for types that he or she did not foresee to be derivable from.

On the other hand, most library implementations (including third-party libraries) often take advantage of EBCO applied to user-defined types used to instantiate library templates internally. As the time of submitting this issue the following program failed to compile on all tested library implementations:

#include <memory>

struct Noop final {
 template<class Ptr>
 void operator()(Ptr) const {}
};

std::unique_ptr<int, Noop> up;

In addition, many std::tuple implementations with empty, final classes as element types failed as well, due to a popular inheritance-based implementation technique. EBCO has also a long tradition to be used in library containers to efficiently store potentially stateless, empty allocators.

It seems that both user and library did the best they could: None of the affected types did impose explicit requirements on the corresponding user-defined types to be derivable from (This capability was not part of the required operations), and libraries did apply EBCO whereever possible to the convenience of the customer.

Nonetheless given the existence of non-derivable-from class types in C++11, libraries have to cope with failing derivations. How should that problem be solved?

It would certainly be possible to add weazel wording to the allocator requirements similar to what we had in C++03, but restricted to derivation-from requirements. I consider this as the bad solution, because it would add new requirements that never had existed before in this explicit form onto types like allocators.

Existing libraries presumably will need internal traits like __is_final or __is_derivable to make EBCO possible in the current form but excluding non-derivable class types. As of this writing this seems to happen already. Problem is that without a std::is_derivable trait, third-party libraries have no portable means to do the same thing as standard library implementations. This should be a good reason to make such a trait public available soon, but seems not essential to have now. Further, this issue should also be considered as a chance to recognice that EBCO has always been a very special corner case (There exist parallels to the previously existing odd core language rule that did make the interplay between std::auto_ptr and std::auto_ptr_ref possible) and that it would be better to provide explicit means for space-efficient storage, not necessarily restricted to inheritance relations, e.g. by marking data members with a special attribute.

At least two descriptions in the current standard should be fixed now for better clarification:

  1. As mentioned by Ganesh, 20.7.8 [allocator.traits] p1 currently contains a (non-normative) note "Thus, it is always possible to create a derived class from an allocator." which should be removed.

  2. As pointed out by Howard, the specification of scoped_allocator_adaptor as of 20.13.1 [allocator.adaptor.syn] already requires derivation from OuterAlloc, but only implies indirectly the same for the inner allocators due to the exposition-only description of member inner. This indirect implication should be normatively required for all participating allocators.

[2012, Kona]

What we really need is a type trait to indicate if a type can be derived from. Howard reports Clang and libc++ have had success with this approach.

Howard to provide wording, and AJM to alert Core that we may be wanting to add a new trait that requires compiler support.

[2014-02, Issaquah: Howard and Daniel comment and provide wording]

Several existing C++11 compilers do already provide an internal __is_final intrinsic (e.g. clang and gcc) and therefore we believe that this is evidence enough that this feature is implementable today.

We believe that both a simple and clear definition of the is_final query should result in a true outcome if and only if the current existing language definition holds that a complete class type (either union or non-union) has been marked with the class-virt-specifier final — nothing more.

The following guidelines lead to the design decision and the wording choice given below:

It has been expressed several times that a high-level trait such as "is_derivable" would be preferred and would be more useful for non-experts. One problem with that request is that it is astonishingly hard to find a common denominator for what the precise definition of this trait should be, especially regarding corner-cases. Another example of getting very differing points of view is to ask a bunch of C++ experts what the best definition of the is_empty trait should be (which can be considered as a kind of higher-level trait).

Once we have a fundamental trait like is_final available, we can easily define higher-level traits in the future on top of this by a proper logical combination of the low-level traits.

A critical question is whether providing such a low-level compile-time introspection might be considered as disadvantageous, because it could constrain the freedom of existing implementations even further and whether a high-level trait would solve this dilemma. We assert that since C++11 the static introspection capabilities are already very large and we believe that making the presence or absence of the final keyword testable does not make the current situation worse.

Below code example demonstrates the intention and the implementability of this feature:

#include <type_traits>

namespace std
{

template <class T>
struct is_final
  : public integral_constant<bool, __is_final(T)>
{};

}  // std

// test it

union FinalUnion final { };

union NonFinalUnion { };

class FinalClass final { };

struct NonFinalClass { };

class Incomplete;

int main()
{
  using std::is_final;
  static_assert( is_final<const volatile FinalUnion>{}, "");
  static_assert(!is_final<FinalUnion[]>{}, "");
  static_assert(!is_final<FinalUnion[1]>{}, "");
  static_assert(!is_final<NonFinalUnion>{}, "");
  static_assert( is_final<FinalClass>{}, "");
  static_assert(!is_final<FinalClass&>{}, "");
  static_assert(!is_final<FinalClass*>{}, "");
  static_assert(!is_final<NonFinalClass>{}, "");
  static_assert(!is_final<void>{}, "");
  static_assert(!is_final<int>{}, "");
  static_assert(!is_final<Incomplete>{}, ""); // error incomplete type 'Incomplete' used in type trait expression
}

[2014-02-14, Issaquah: Move to Immediate]

This is an important issue, that we really want to solve for C++14.

Move to Immediate after polling LEWG, and then the NB heads of delegation.

Proposed resolution:

This wording is relative to N3797.

  1. Change 20.10.2 [meta.type.synop], header <type_traits> synopsis, as indicated

    namespace std {
      […]
      // 20.10.4.3, type properties:
      […]
      template <class T> struct is_empty;
      template <class T> struct is_polymorphic;
      template <class T> struct is_abstract;
      template <class T> struct is_final;
    
      […]
    }
    
  2. Change 20.10.4.3 [meta.unary.prop], Table 49 — Type property predicates, as indicated

    Table 49 — Type property predicates
    Template Condition Preconditions
    template <class T>
    struct is_abstract;
    […] […]
    template <class T>
    struct is_final;
    T is a class type marked with the class-virt-specifier final (9 [class]).
    [Note: A union is a class type that can be marked with final. — end note]
    If T is a class type, T shall be a complete type
  3. After 20.10.4.3 [meta.unary.prop] p5 add one further example as indicated:

    [Example:

    // Given:
    struct P final { };
    union U1 { };
    union U2 final { };
    
    // the following assertions hold:
    static_assert(!is_final<int>::value, "Error!");
    static_assert( is_final<P>::value, "Error!");
    static_assert(!is_final<U1>::value, "Error!");
    static_assert( is_final<U2>::value, "Error!");
    

    end example]


2132. std::function ambiguity

Section: 20.9.11.2.1 [func.wrap.func.con] Status: Immediate Submitter: Ville Voutilainen Opened: 2012-02-28 Last modified: 2014-02-14

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

View all issues with Immediate status.

Discussion:

Consider the following:

#include <functional>

void f(std::function<void()>) {}
void f(std::function<void(int)>) {}

int main() {
  f([]{});
  f([](int){});
}

The calls to f in main are ambiguous. Apparently because the conversion sequences to std::function from the lambdas are identical. The standard specifies that the function object given to std::function "shall be Callable (20.8.11.2) for argument types ArgTypes and return type R." It doesn't say that if this is not the case, the constructor isn't part of the overload set.

Daniel: During the preparation of N3123 it turned out that there are no longer reasons to refer to INVOKE as a conceptually entity alone, its real implementation as a function template invoke is possible but was deferred for a later point in time. Defining a type trait for the Callable requirement would also be possible, so there seem to be no technical reasons why the template constructor of std::function should not be constrained. The below suggested wording does this without introducing a special trait for this. This corresponds to the way that has been used to specify the result_of trait. Note that the definition of the Callable requirement is perfectly suitable for this, because it is a pure syntactically based requirement and can be directly transformed into a constrained template.

The suggested resolution also applies such wording to the "perfectly forwarding" assignment operator

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

The positive side-effect of this is that it automatically implements a solution to a problem similar to that mentioned in issue 1234.

It would be possible to apply similar constraints to the member signatures

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

template<class F, class A> void assign(F&&, const A&);

as well. At this point there does not seem to be a pestering reason to do so.

[2012-10 Portland: Move to Review]

STL: This is a real issue, but does not like a resolution relying on a SFINAEable metafunction that is not specified and available to the users.

packaged_task has the same issue.

STL strongly wants to see an is_callable type trait to clarify the proposed wording.

Jeremiah concerned about holding up what appears to be a correct resolution for a hypothetical better one later - the issue is real.

Why must f by CopyConstructible? Surely MoveConstructible would be sufficient?

Answer: because function is CopyConstructible, and the bound functor is type-erased so must support all the properties of function itself.

Replace various applications of declval in the proposed resolution with simply using the passed functor object, f.

Alisdair to apply similar changes to packaged_task.

[2012-11-09, Vicente J. Botet Escriba provides another example]

Consider the following:

class AThreadWrapper {
public:
  explicit operator std::thread();
  ...
};
std::thread th = std::thread(AThreadWrapper); // call to conversion operator intended

The call to the conversion operator is overloaded with the thread constructor. But thread constructor requirement makes it fail as AThreadWrapper is not a Callable and the compiler tries to instantiate the thread constructor and fails.

[2014-02-14 Issaquah meeting: Move to Immediate]

Proposed resolution:

This wording is relative to N3376.

  1. Change the following paragraphs in 20.9.11.2.1 [func.wrap.func.con]: [Editorial comment: The removal of the seemingly additional no-throw requirements of copy constructor and destructor of A is recommended, because they are already part of the Allocator requirements. Similar clean-up has been suggested by 2070end comment]

    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. f shall be Callable (20.9.11.2 [func.wrap.func]) for argument types ArgTypes and return type R. The copy constructor and destructor of A shall not throw exceptions.

    -?- Remarks: These constructors shall not participate in overload resolution unless f is Callable (20.9.11.2 [func.wrap.func]) for argument types ArgTypes... and return type R.

    […]

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

    -18- Effects: function(std::forward<F>(f)).swap(*this);

    -19- Returns: *this

    -?- Remarks: This assignment operator shall not participate in overload resolution unless declval<typename decay<F>::type&>() is Callable (20.9.11.2 [func.wrap.func]) for argument types ArgTypes... and return type R.


2182. Container::[const_]reference types are misleadingly specified

Section: 23.2.1 [container.requirements.general] Status: Immediate Submitter: Daniel Krügler Opened: 2012-08-20 Last modified: 2014-02-12

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

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

View all issues with Immediate status.

Discussion:

According to Table 96 (Container requirements) the return type of X::reference and X::const_reference is "lvalue of T" and "const lvalue of T", respectively. This does not make much sense, because an lvalue is an expression category, not a type. It could also refer to an expression that has a type, but this doesn't make sense either in this context, because obviously X::[const_]reference are intended to refer to types.

Given the fact that vector<bool> has no real reference type for X::[const_]reference and this definition presumably is intended to cover such situations as well, one might think that the wording is just a sloppy form of "type that represents a [const] lvalue of T". But this is also problematic, because basically all proxy reference expressions are rvalues.

It is unclear what the intention is. A straightward way of fixing this wording could make X::[const_]reference identical to [const] T&. This holds for all Library containers except for vector<bool>.

Another way of solving this definition problem would be to impose a requirement that holds for both references and reference-like proxies. Both X::reference and X::const_reference would need to be convertible to const T&. Additionally X::reference would need to support for a mutable container an assignment expression of the form declval<X::reference>() = declval<T>() (this presentation intentionally does not require declval<X::reference&>() = declval<T>()).

Further, the Table 96 does not impose any relations between X::reference and X::const_reference. It seems that at least X::reference needs to be convertible to X::const_reference.

A related question is whether X::reference is supposed to be a mutable reference-like type, irrespective of whether the container is an immutable container or not. The way, type match_results defines reference identical to const_reference indicates one specific interpretation (similarly, the initializer_list template also defines member type reference equal to const value_type&). Note that this can be a different decision as that for iterator and const_iterator, e.g. for sets the type X::reference still is a mutable reference, even though iterator is described as constant iterator.

The proposed resolution is incomplete in regard to the last question.

[2013-03-15 Issues Teleconference]

Moved to Review.

Alisdair notes that this looks like wording in the right direction. Wonders about congruence of these typedefs and the similar ones for iterators.

[2013-09 Chicago]

Moved to Ready.

Consensus that the requirements should require real references, just like iterators, as containers are required to support at least ForwardIterators, which have the same restriction on references.

Matt will file a new issue for some additional concerns with regex match_results.

[2014-02-10, Daniel comments]

The new issue opened by Matt is LWG 2306.

[Issaquah 20014-10-11: Move to Immediate]

Issue should have been Ready in pre-meeting mailing.

Proposed resolution:

This wording is relative to N3376.

  1. Change Table 96 — "Container requirements" as indicated:

    Table p6 — Container requirements
    Expression Return type Operational
    Semantics
    Assertion/note
    pre-/post-condition
    Complexity
    X::reference lvalue of T&   compile time
    X::const_reference const lvalue ofconst T&   compile time

2186. Incomplete action on async/launch::deferred

Section: 30.6.8 [futures.async] Status: Immediate Submitter: Vicente J. Botet Escriba Opened: 2012-09-20 Last modified: 2014-02-14

View other active issues in [futures.async].

View all other issues in [futures.async].

View all issues with Immediate status.

Discussion:

The description of the effects of async when the launch policy is launch::deferred doesn't state what is done with the result of the deferred function invocation and the possible exceptions as it is done for the asynchronous function when the policy is launch::async.

[2012, Portland: move to Open]

Detlef: agree with the problem but not with the resolution. The wording should be applied to all launch policies rather than having to be separately specified for each one.

Hans: we should redraft to factor out the proposed text outside the two bullets. Needs to be carefully worded to be compatible with the resolution of 2120 (see above).

Moved to open

[Issaquah 20014-10-11: Move to Immediate after SG1 review]

Proposed resolution:

[This wording is relative to N3376.]

  1. Change 30.6.8 [futures.async] p3 bullet 2 as indicated:

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

    -2- Requires: […]

    -3- Effects:: The first function behaves the same as a call to the second function with a policy argument of launch::async | launch::deferred and the same arguments for F and Args. […] The further behavior of the second function depends on the policy argument as follows (if more than one of these conditions applies, the implementation may choose any of the corresponding policies):

    • if policy & launch::async is non-zero […]

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


2188. Reverse iterator does not fully support targets that overload operator&

Section: 24.5.1.3.5 [reverse.iter.opref] Status: Immediate Submitter: Alisdair Meredith Opened: 2012-09-23 Last modified: 2014-02-14

View all other issues in [reverse.iter.opref].

View all issues with Immediate status.

Discussion:

The specification for reverse_iterator::operator-> returns the address of the object yielded by dereferencing with operator*, but does not have the usual wording about returning the true address of the object. As reverse_iterator requires the adapted iterator have at least the bidirectional iterator category, we know that the returned reference is a true reference, and not a proxy, hence we can use std::addressof on the reference to get the right answer.

This will most likely show itself as an issue with a list or vector of a type with such an overloaded operator, where algorithms are likely to work with a forward iteration, but not with reverse iteration.

[2013-04-20, Bristol]

Resolution: Goes to open now and move to review as soon as Daniel proposes a new wording.

[2014-02-12 Issaquah meeting]

Use std::addressof as the library uses elsewhere, then move as Immediate.

Proposed resolution:

Revise 24.5.1.3.5 [reverse.iter.opref] p1, as indicated:

Returns: addressof&(operator*()).


2193. Default constructors for standard library containers are explicit

Section: 23 [containers] Status: Immediate Submitter: Richard Smith Opened: 2012-10-04 Last modified: 2014-02-14

View other active issues in [containers].

View all other issues in [containers].

View all issues with Immediate status.

Discussion:

Most (all?) of the standard library containers have explicit default constructors. Consequently:

std::set<int> s1 = { 1, 2 }; // ok
std::set<int> s2 = { 1 }; // ok
std::set<int> s3 = {}; // ill-formed, copy-list-initialization selected an explicit constructor

Note that Clang + libc++ rejects the declaration of s3 for this reason. This cannot possibly match the intent.

Suggested fix: apply this transformation throughout the standard library:

set() : set(Compare()) {}
explicit set(const Compare& comp = Compare(),
             const Allocator& = Allocator());

[ 2012-10-06: Daniel adds concrete wording. ]

[2012, Portland: Move to Open]

This may be an issue better solved by a core language tweak. Throw the issue over to EWG and see whether they believe the issue is better resolved in Core or Library.

AJM suggest we spawn a new status of 'EWG' to handle such issues - and will move this issue appropriately when the software can record such resolutions.

[2013-08-27, Joaquín M López Muñoz comments:]

For the record, I'd like to point out that the resolution proposed by the submitter, namely replacing

explicit basic_string(const Allocator& a = Allocator());

by

basic_string() : basic_string(Allocator()) {}
explicit basic_string(const Allocator& a);

(and similarly for other container and container-like classes) might introduce a potential backwards-compatibility problem related with explicit instantiation. Consider for instance

struct my_allocator
{
  my_allocator(...); // no default ctor
  ...
};

template class std::basic_string<char, std::char_traits<char>, my_allocator<char>>;

This (which I understand is currently a valid explicit instantiation of std::basic_string) will break if std::basic_string ctors are modified as proposed by this issue, since my_allocator doesn't have a default ctor.

[2013-10-06, Daniel comments:]

Issue 2303 describes the more general problem related to explicit instantiation requests in the current library and may help to solve this problem here as well.

[2014-02-13, Issaquah, Jonathan revises wording]

Previous resolution from Daniel [SUPERSEDED]:

This wording is relative to N3376.

The more general criterion for performing the suggested transformation was: Any type with an initializer-list constructor that also has an explicit default constructor.

  1. Change class template basic_string synopsis, 21.4 [basic.string] p5 as indicated:

    basic_string() : basic_string(Allocator()) {}
    explicit basic_string(const Allocator& a = Allocator());
    
  2. Change 21.4.2 [string.cons] before p1 as indicated:

    explicit basic_string(const Allocator& a = Allocator());
    
  3. Change class template deque synopsis, 23.3.3.1 [deque.overview] p2 as indicated:

    deque() : deque(Allocator()) {}
    explicit deque(const Allocator& = Allocator());
    
  4. Change 23.3.3.2 [deque.cons] before p1 as indicated:

    explicit deque(const Allocator& = Allocator());
    
  5. Change class template forward_list synopsis, 23.3.4.1 [forwardlist.overview] p3 as indicated:

    forward_list() : forward_list(Allocator()) {}
    explicit forward_list(const Allocator& = Allocator());
    
  6. Change 23.3.4.2 [forwardlist.cons] before p1 as indicated:

    explicit forward_list(const Allocator& = Allocator());
    
  7. Change class template list synopsis, 23.3.5.1 [list.overview] p2 as indicated:

    list() : list(Allocator()) {}
    explicit list(const Allocator& = Allocator());
    
  8. Change 23.3.5.2 [list.cons] before p1 as indicated:

    explicit list(const Allocator& = Allocator());
    
  9. Change class template vector synopsis, 23.3.6.1 [vector.overview] p2 as indicated:

    vector() : vector(Allocator()) {}
    explicit vector(const Allocator& = Allocator());
    
  10. Change 23.3.6.2 [vector.cons] before p1 as indicated:

    explicit vector(const Allocator& = Allocator());
    
  11. Change class template specialization vector<bool> synopsis, 23.3.7 [vector.bool] p1 as indicated:

    vector() : vector(Allocator()) {}
    explicit vector(const Allocator& = Allocator());
    
  12. Change class template map synopsis, 23.4.4.1 [map.overview] p2 as indicated:

    map() : map(Compare()) {}
    explicit map(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  13. Change 23.4.4.2 [map.cons] before p1 as indicated:

    explicit map(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  14. Change class template multimap synopsis, 23.4.5.1 [multimap.overview] p2 as indicated:

    multimap() : multimap(Compare()) {}
    explicit multimap(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  15. Change 23.4.5.2 [multimap.cons] before p1 as indicated:

    explicit multimap(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  16. Change class template set synopsis, 23.4.6.1 [set.overview] p2 as indicated:

    set() : set(Compare()) {}
    explicit set(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  17. Change 23.4.6.2 [set.cons] before p1 as indicated:

    explicit set(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  18. Change class template multiset synopsis, 23.4.7.1 [multiset.overview] p2 as indicated:

    multiset() : multiset(Compare()) {}
    explicit multiset(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  19. Change 23.4.7.2 [multiset.cons] before p1 as indicated:

    explicit multiset(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  20. Change class template unordered_map synopsis, 23.5.4.1 [unord.map.overview] p3 as indicated:

    unordered_map() : unordered_map(see below) {}
    explicit unordered_map(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  21. Change 23.5.4.2 [unord.map.cnstr] before p1 as indicated:

    unordered_map() : unordered_map(see below) {}
    explicit unordered_map(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  22. Change class template unordered_multimap synopsis, 23.5.5.1 [unord.multimap.overview] p3 as indicated:

    unordered_multimap() : unordered_multimap(see below) {}
    explicit unordered_multimap(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  23. Change 23.5.5.2 [unord.multimap.cnstr] before p1 as indicated:

    unordered_multimap() : unordered_multimap(see below) {}
    explicit unordered_multimap(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  24. Change class template unordered_set synopsis, 23.5.6.1 [unord.set.overview] p3 as indicated:

    unordered_set() : unordered_set(see below) {}
    explicit unordered_set(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  25. Change 23.5.6.2 [unord.set.cnstr] before p1 as indicated:

    unordered_set() : unordered_set(see below) {}
    explicit unordered_set(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  26. Change class template unordered_multiset synopsis, 23.5.7.1 [unord.multiset.overview] p3 as indicated:

    unordered_multiset() : unordered_multiset(see below) {}
    explicit unordered_multiset(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  27. Change 23.5.7.2 [unord.multiset.cnstr] before p1 as indicated:

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

[Issaquah 20014-10-11: Move to Immediate after final review]

Proposed resolution:

This wording is relative to N3376.

The more general criterion for performing the suggested transformation was: Any type with an initializer-list constructor that also has an explicit default constructor.

  1. Change class template basic_string synopsis, 21.4 [basic.string] p5 as indicated:

    basic_string() : basic_string(Allocator()) { }
    explicit basic_string(const Allocator& a = Allocator());
    
  2. Change 21.4.2 [string.cons] before p1 as indicated:

    explicit basic_string(const Allocator& a = Allocator());
    
  3. Change class template deque synopsis, 23.3.3.1 [deque.overview] p2 as indicated:

    deque() : deque(Allocator()) { }
    explicit deque(const Allocator& = Allocator());
    
  4. Change 23.3.3.2 [deque.cons] before p1 as indicated:

    explicit deque(const Allocator& = Allocator());
    
  5. Change class template forward_list synopsis, 23.3.4.1 [forwardlist.overview] p3 as indicated:

    forward_list() : forward_list(Allocator()) { }
    explicit forward_list(const Allocator& = Allocator());
    
  6. Change 23.3.4.2 [forwardlist.cons] before p1 as indicated:

    explicit forward_list(const Allocator& = Allocator());
    
  7. Change class template list synopsis, 23.3.5.1 [list.overview] p2 as indicated:

    list() : list(Allocator()) { }
    explicit list(const Allocator& = Allocator());
    
  8. Change 23.3.5.2 [list.cons] before p1 as indicated:

    explicit list(const Allocator& = Allocator());
    
  9. Change class template vector synopsis, 23.3.6.1 [vector.overview] p2 as indicated:

    vector() : vector(Allocator()) { }
    explicit vector(const Allocator& = Allocator());
    
  10. Change 23.3.6.2 [vector.cons] before p1 as indicated:

    explicit vector(const Allocator& = Allocator());
    
  11. Change class template specialization vector<bool> synopsis, 23.3.7 [vector.bool] p1 as indicated:

    vector() : vector(Allocator()) { }
    explicit vector(const Allocator& = Allocator());
    
  12. Change class template map synopsis, 23.4.4.1 [map.overview] p2 as indicated:

    map() : map(Compare()) { }
    explicit map(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  13. Change 23.4.4.2 [map.cons] before p1 as indicated:

    explicit map(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  14. Change class template multimap synopsis, 23.4.5.1 [multimap.overview] p2 as indicated:

    multimap() : multimap(Compare()) { }
    explicit multimap(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  15. Change 23.4.5.2 [multimap.cons] before p1 as indicated:

    explicit multimap(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  16. Change class template set synopsis, 23.4.6.1 [set.overview] p2 as indicated:

    set() : set(Compare()) { }
    explicit set(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  17. Change 23.4.6.2 [set.cons] before p1 as indicated:

    explicit set(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  18. Change class template multiset synopsis, 23.4.7.1 [multiset.overview] p2 as indicated:

    multiset() : multiset(Compare()) { }
    explicit multiset(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  19. Change 23.4.7.2 [multiset.cons] before p1 as indicated:

    explicit multiset(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  20. Change class template unordered_map synopsis, 23.5.4.1 [unord.map.overview] p3 as indicated:

    unordered_map();
    explicit unordered_map(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  21. Change 23.5.4.2 [unord.map.cnstr] before p1 as indicated:

    unordered_map() : unordered_map(size_type(see below)) { }
    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 func-
    tion, and allocator, and using at least n buckets. If n is not provided, For the default constructor
    the number of buckets is implementation-defined. max_load_factor() returns 1.0.
  22. Change class template unordered_multimap synopsis, 23.5.5.1 [unord.multimap.overview] p3 as indicated:

    unordered_multimap();
    explicit unordered_multimap(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  23. Change 23.5.5.2 [unord.multimap.cnstr] before p1 as indicated:

    unordered_multimap() : unordered_multimap(size_type(see below)) { }
    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, For the default constructor
    the number of buckets is implementation-defined. max_load_factor() returns 1.0.
  24. Change class template unordered_set synopsis, 23.5.6.1 [unord.set.overview] p3 as indicated:

    unordered_set();
    explicit unordered_set(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  25. Change 23.5.6.2 [unord.set.cnstr] before p1 as indicated:

    unordered_set() : unordered_set(size_type(see below)) { }
    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 func-
    tion, and allocator, and using at least n buckets. If n is not provided, For the default constructor
    the number of buckets is implementation-defined. max_load_factor() returns 1.0.
  26. Change class template unordered_multiset synopsis, 23.5.7.1 [unord.multiset.overview] p3 as indicated:

    unordered_multiset();
    explicit unordered_multiset(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  27. Change 23.5.7.2 [unord.multiset.cnstr] before p1 as indicated:

    unordered_multiset() : unordered_multiset(size_type(see below)) { }
    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, For the default constructor
    the number of buckets is implementation-defined. max_load_factor() returns 1.0.

2205. Problematic postconditions of regex_match and regex_search

Section: 28.11.2 [re.alg.match], 28.11.3 [re.alg.search] Status: Immediate Submitter: Pete Becker Opened: 2012-10-24 Last modified: 2014-02-12

View other active issues in [re.alg.match].

View all other issues in [re.alg.match].

View all issues with Immediate status.

Discussion:

Table 142 lists post-conditions on the match_results object when a call to regex_match succeeds. regex_match is required to match the entire target sequence. The post-condition for m[0].matched is "true if a full match was found." Since these are conditions for a successful search which is, by definition, a full match, the post-condition should be simply "true".

There's an analogous probem in Table 143: the condition for m[0].matched is "true if a match was found, false otherwise." But Table 143 gives post-conditions for a successful match, so the condition should be simply "true".

Furthermore, they have explicit requirements for m[0].first, m[0].second, and m[0].matched. They also have requirements for the other elements of m, described as m[n].first, m[n].second, and m[n].matched, in each case qualifying the value of n as "for n < m.size()". Since there is an explicit description for n == 0, this qualification should be "for 0 < n < m.size()" in all 6 places.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3376.

  1. Change Table 142 as indicated:

    Table 142 — Effects of regex_match algorithm
    Element Value
    m[0].first first
    m[0].second last
    m[0].matched true if a full match was found.
    m[n].first For all integers 0 < n < m.size(), the start of the sequence that matched sub-expression n.
    Alternatively, if subexpression n did not participate in the match, then last.
    m[n].second For all integers 0 < n < m.size(), the end of the sequence that matched sub-expression n.
    Alternatively, if sub-expression n did not participate in the match, then last.
    m[n].matched For all integers 0 < n < m.size(), true if sub-expression n participated in the match, false otherwise.
  2. Change Table 143 as indicated:

    Table 143 — Effects of regex_search algorithm
    Element Value
    m[0].first The start of the sequence of characters that matched the regular expression
    m[0].second The end of the sequence of characters that matched the regular expression
    m[0].matched true if a match was found, and false otherwise.
    m[n].first For all integers 0 < n < m.size(), the start of the sequence that matched sub-expression n.
    Alternatively, if subexpression n did not participate in the match, then last.
    m[n].second For all integers 0 < n < m.size(), the end of the sequence that matched sub-expression n.
    Alternatively, if sub-expression n did not participate in the match, then last.
    m[n].matched For all integers 0 < n < m.size(), true if sub-expression n participated in the match, false otherwise.

2213. Return value of std::regex_replace

Section: 28.11.4 [re.alg.replace] Status: Immediate Submitter: Pete Becker Opened: 2012-11-08 Last modified: 2014-02-12

View other active issues in [re.alg.replace].

View all other issues in [re.alg.replace].

View all issues with Immediate status.

Discussion:

In 28.11.4 [re.alg.replace], the first two variants of std::regex_replace take an output iterator named "out" as their first argument. Paragraph 2 of that section says that the functions return "out". When I first implemented this, many years ago, I wrote it to return the value of the output iterator after all the insertions (cf. std::copy), which seems like the most useful behavior. But looking at the requirement now, it like the functions should return the original value of "out" (i.e. they have to keep a copy of the iterator for no reason except to return it). Is that really what was intended?

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3485.

  1. Edit 28.11.4 [re.alg.replace] as indicated:

    template <class OutputIterator, class BidirectionalIterator,
      class traits, class charT, class ST, class SA>
    OutputIterator
    regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last,
      const basic_regex<charT, traits>& e, const basic_string<charT, ST, SA>& fmt,
      regex_constants::match_flag_type flags = regex_constants::match_default);
    template <class OutputIterator, class BidirectionalIterator,
      class traits, class charT>
    OutputIterator
    regex_replace(OutputIterator out, BidirectionalIterator first, BidirectionalIterator last,
      const basic_regex<charT, traits>& e, const charT* fmt,
      regex_constants::match_flag_type flags = regex_constants::match_default);
    

    -1- Effects: Constructs a regex_iterator object i as if by regex_iterator<BidirectionalIterator, charT, traits> i(first, last, e, flags), and uses i to enumerate through all of the matches m of type match_results<BidirectionalIterator> that occur within the sequence [first, last). If no such matches are found and !(flags & regex_constants ::format_no_copy) then calls out = std::copy(first, last, out). If any matches are found then, for each such match, if !(flags & regex_constants::format_no_copy), calls out = std::copy(m.prefix().first, m.prefix().second, out), and then calls out = m.format(out, fmt, flags) for the first form of the function and out = m.format(out, fmt, fmt + char_traits<charT>::length(fmt), flags) for the second. Finally, if such a match is found and !(flags & regex_constants ::format_no_copy), calls out = std::copy(last_m.suffix().first, last_m.suffix().second, out) where last_m is a copy of the last match found. If flags & regex_constants::format_first_only is non-zero then only the first match found is replaced.

    -2- Returns: out.


2258. a.erase(q1, q2) unable to directly return q2

Section: 23.2.4 [associative.reqmts] Status: Immediate Submitter: Geoff Alexander Opened: 2013-05-11 Last modified: 2014-02-14

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with Immediate status.

Discussion:

Section 23.2.4 [associative.reqmts], Table 102, page 743 of the C++ 2011 Standard states that a.erase(q1, q2) returns q2. The problem is that a.erase(q1, q2) cannot directly return q2 as the return type, iterator, differs from that of q2, const_iterator.

[2013-09 Chicago (evening issues group)]

The wording looks good, but is worded slightly differently to how we say the same for sequence containers, and for unordered associative containers. We should apply consistent wording in all three cases.

Alisdair to provide the wording.

[2014-02-12 Issaquah meeting]

Move a Immediate.

Proposed resolution:

  1. In the specification of a.erase(q1, q2) in sub-clause 23.2.4 [associative.reqmts], Table 102 change as indicated:

    Table 102 — Associative container requirements (in addition to container) (continued)
    Expression Return type Assertion/note pre-/post-condition Complexity
    a.erase(q1, q2) iterator erases all the elements in the range [q1,q2). Returns q2 an iterator pointing to the element pointed to by q2 prior to any elements being erased. If no such element exists, a.end() is returned. log(a.size()) + N where N has the value distance(q1, q2).

2263. Comparing iterators and allocator pointers with different const-character

Section: 17.6.3.5 [allocator.requirements], 23.2 [container.requirements] Status: Immediate Submitter: Howard Hinnant Opened: 2013-06-25 Last modified: 2014-02-14

View other active issues in [allocator.requirements].

View all other issues in [allocator.requirements].

View all issues with Immediate status.

Discussion:

This ancient issue 179 says one ought to be able to compare iterators with const_iterators from any given container. I'm having trouble finding words that guarantee this in C++11. This impacts not only a container's iterators, but also the allocator requirements in allocator.requirements] surrounding pointer, const_pointer, void_pointer and const_void_pointer. E.g. can one compare a pointer with a const_pointer?

Since allocator::pointer and const_pointer are required to be random access iterators, one could expect that the 179 guarantees apply for them as well.

[ Daniel comments: ]

The wording for 179 was part of several working drafts (e.g. also in N3092) over some time and suddenly got lost in N3242, presumably by accident. Whatever we decide for allocator pointers, I expect that we need to restore the 179 wording as part of the overall resolution:

Reinsert after 23.2 [container.requirements] p6:

-6- begin() returns an iterator referring to the first element in the container. end() returns an iterator which is the past-the-end value for the container. If the container is empty, then begin() == end();

-?- In the expressions

i == j
i != j
i < j
i <= j
i >= j
i > j
i - j

where i and j denote objects of a container's iterator type, either or both may be replaced by an object of the container's const_iterator type referring to the same element with no change in semantics.

[2014-02-13 Issaquah, Daniel comments and suggests wording]

First, I didn't originally move the seemingly lost wording to the resolution section because I wanted to ensure that the committee double-checks the reason of this loss.

Second, albeit restoring this wording will restore the comparability of const_iterator and iterator of containers specified in Clause 23, but this alone would not imply that this guarantee automatically extends to all other iterators, simply because there is no fundamental relation between a mutable iterator and a constant iterator by itself. This relation only exists under specific conditions, for example for containers which provide two such typedefs of that kind. Thus the wording restoration would not ensure that allocator pointer and const_pointer would be comparable with each other. To realize that, we would need additional guarantees added to the allocator requirements. In fact, it is crucial to separate these things, because allocators are not restricted to be used within containers, they have their own legitimate use for other places as well (albeit containers presumably belong to the most important use-cases), and this is also stated in the introduction of 17.6.3.5 [allocator.requirements], where it says:

All of the string types (Clause 21), containers (Clause 23) (except array), string buffers and string streams (Clause 27), and match_results (Clause 28) are parameterized in terms of allocators.

[2014-02-12 Issaquah meeting]

Move a Immediate.

Proposed resolution:

  1. Insert after 17.6.3.5 [allocator.requirements] p4 as indicated:

    -4- An allocator type X shall satisfy the requirements of CopyConstructible (17.6.3.1). The X::pointer, X::const_pointer, X::void_pointer, and X::const_void_pointer types shall satisfy the requirements of NullablePointer (17.6.3.3). No constructor, comparison operator, copy operation, move operation, or swap operation on these types shall exit via an exception. X::pointer and X::const_pointer shall also satisfy the requirements for a random access iterator (24.2).

    -?- Let x1 and x2 denote objects of (possibly different) types X::void_pointer, X::const_void_pointer, X::pointer, or X::const_pointer. Then, x1 and x2 are equivalently-valued pointer values, if and only if both x1 and x2 can be explicitly converted to the two corresponding objects px1 and px2 of type X::const_pointer, using a sequence of static_casts using only these four types, and the expression px1 == px2 evaluates to true.

    Drafting note: This wording uses the seemingly complicated route via X::const_pointer, because these are (contrary to X::const_void_pointer) random access iterators and we can rely here for dereferenceable values on the fundamental pointee equivalence of 24.2.5 [forward.iterators] p6:

    If a and b are both dereferenceable, then a == b if and only if *a and *b are bound to the same object.

    while for null pointer values we can rely on the special equality relation induced by 17.6.3.3 [nullablepointer.requirements].

    -?- Let w1 and w2 denote objects of type X::void_pointer. Then for the expressions

    w1 == w2
    w1 != w2
    

    either or both objects may be replaced by an equivalently-valued object of type X::const_void_pointer with no change in semantics.

    -?- Let p1 and p2 denote objects of type X::pointer. Then for the expressions

    p1 == p2
    p1 != p2
    p1 < p2
    p1 <= p2
    p1 >= p2
    p1 > p2
    p1 - p2
    

    either or both objects may be replaced by an equivalently-valued object of type X::const_pointer with no change in semantics.

  2. Reinsert after 23.2 [container.requirements] p6:

    -6- begin() returns an iterator referring to the first element in the container. end() returns an iterator which is the past-the-end value for the container. If the container is empty, then begin() == end();

    -?- In the expressions

    i == j
    i != j
    i < j
    i <= j
    i >= j
    i > j
    i - j
    

    where i and j denote objects of a container's iterator type, either or both may be replaced by an object of the container's const_iterator type referring to the same element with no change in semantics.


2293. Wrong facet used by num_put::do_put

Section: 22.4.2.2.2 [facet.num.put.virtuals] Status: Immediate Submitter: Juan Soulie Opened: 2013-09-04 Last modified: 2014-02-12

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

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

View all issues with Immediate status.

Discussion:

At the end of 22.4.2.2.2 [facet.num.put.virtuals] (in p6), the return value is said to be obtained by calling truename or falsename on the wrong facet: ctype should be replaced by numpunct.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit 22.4.2.2.2 [facet.num.put.virtuals] p6 as indicated:

    -6- Returns: If (str.flags() & ios_base::boolalpha) == 0 returns do_put(out, str, fill, (int)val), otherwise obtains a string s as if by

    string_type s =
      val ? use_facet<ctypenumpunct<charT> >(loc).truename()
          : use_facet<ctypenumpunct<charT> >(loc).falsename();
    

    and then inserts each character c of s into out via *out++ = c and returns out.


2299. [CD] Effects of inaccessible key_compare::is_transparent type are not clear

Section: 23.2.4 [associative.reqmts] Status: Immediate Submitter: Daniel Krügler Opened: 2013-09-24 Last modified: 2014-02-13

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with Immediate status.

Discussion:

Addresses ES 16

The condition "X::key_compare::is_transparent exists" does not specify that the type be publicly accessible.

Consider the public accessibility of X::key_compare::is_transparent and whether its potential inaccessibility should be banned for a compliant key_compare type.

[2013-09-24 Daniel provides resolution suggestion]

[2013-09-25 Chicago]

Daniel's wording is good, advance to Immediate to respond to NB comment.

[2013-09-26 Chicago]

Moved back to Review as Daniel would like another look at the words, and to confirm implementability.

Previous resolution from Daniel [SUPERSEDED]:

  1. Change 23.2.4 [associative.reqmts] p8 as indicated:

    -8- In Table 102, X denotes an associative container class, a denotes a value of X, a_uniq denotes a value of X when X supports unique keys, a_eq denotes a value of X when X supports multiple keys, a_tran denotes a value of X when thea publicly accessible type X::key_compare::is_transparent exists whose name is unambiguous and not hidden, […]

  2. Change 23.2.4 [associative.reqmts] p13 as indicated:

    The member function templates find, count, lower_bound, upper_bound, and equal_range shall not participate in overload resolution unless thea publicly accessible type Compare::is_transparent exists whose name is unambiguous and not hidden.

[2014-02-10 Daniel comments provides alternative wording]

I could confirm that my previous concerns were unwarranted, because they turned out to be due to a compiler-bug. Nonetheless I would suggest to replace the previously suggested replication of core-wording situations (access, ambiguity, hidden) by a single more robust phrase based on "valid type".

[2014-02-12 Issaquah: Move to Immediate]

STL: This uses "valid type", which is a Phrase Of Power in Core, and Daniel has a citation for the term.

Jonathan: It's nice to rely on Core.

Proposed resolution:

This wording is relative to N3797.

  1. Change 23.2.4 [associative.reqmts] p8 as indicated:

    -8- In Table 102, X denotes an associative container class, a denotes a value of X, a_uniq denotes a value of X when X supports unique keys, a_eq denotes a value of X when X supports multiple keys, a_tran denotes a value of X when the typequalified-id X::key_compare::is_transparent existsis valid and denotes a type (14.8.2 [temp.deduct]), […]

  2. Change 23.2.4 [associative.reqmts] p13 as indicated:

    The member function templates find, count, lower_bound, upper_bound, and equal_range shall not participate in overload resolution unless the typequalified-id Compare::is_transparent existsis valid and denotes a type (14.8.2 [temp.deduct]).


2301. Why is std::tie not constexpr?

Section: 20.4.2.4 [tuple.creation] Status: Immediate Submitter: Rein Halbersma Opened: 2013-09-11 Last modified: 2014-02-13

View all other issues in [tuple.creation].

View all issues with Immediate status.

Discussion:

In N3471, a bunch of routines from header <tuple> were made constexpr.

make_tuple/tuple_cat/get<>(tuple)/relational operators — all these were "constexpr-ified".

But not tie. This is similar to Issue 2275, where the same observation was made about forward_as_tuple.

[2014-02-13 Issaquah: Move as Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Change the header <tuple> synopsis, 20.4.1 [tuple.general] p2 as indicated:

    template<class... Types>
      constexpr tuple<Types&...> tie(Types&...) noexcept;
    
  2. Change 20.4.2.4 [tuple.creation] around p7 as indicated:

    template<class... Types>
      constexpr tuple<Types&...> tie(Types&... t) noexcept;
    

2304. Complexity of count in unordered associative containers

Section: 23.2.5 [unord.req] Status: Immediate Submitter: Joaquín M López Muñoz Opened: 2013-09-20 Last modified: 2014-02-13

View other active issues in [unord.req].

View all other issues in [unord.req].

View all issues with Immediate status.

Discussion:

Table 103 in 23.2.5 [unord.req] states that the complexity of b.count(k) is average case 𝒪(1) rather than linear with the number of equivalent elements, which seems to be a typo as this requires holding an internal count of elements in each group of equivalent keys, something which hardly looks the intent of the standard and no (known by the submitter) stdlib implementation is currently doing.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Change Table 103 as indicated:

    Table 103 — Unordered associative container requirements (in addition to container)
    Expression Return type Assertion/note pre-/post-condition Complexity
    b.count(k) size_type Returns the number of elements with key equivalent to k. Average case 𝒪(1b.count(k)), worst case 𝒪(b.size()).

2306. match_results::reference should be value_type&, not const value_type&

Section: 28.10 [re.results] Status: Immediate Submitter: Matt Austern Opened: 2013-09-25 Last modified: 2014-02-12

View other active issues in [re.results].

View all other issues in [re.results].

View all issues with Immediate status.

Discussion:

The match_results class synopsis has

typedef const value_type& const_reference;
typedef const_reference reference;

We're getting too enthusiastic about types here by insisting that reference is a const reference, even though match_results is a read-only container. In the container requirements table (Table 96, in section 23.2.1 [container.requirements.general] we say that Container::reference is "lvalue of T" and Container::const_reference is "const lvalue of T".

That phrasing in the container requirements table is admittedly a little fuzzy and ought to be clarified (as discussed in lwg issue 2182), but in context it's clear that Container::reference ought to be a T& even for constant containers. In the rest of Clause 23 we see that Container::reference is T&, not const T&, even for const-qualified containers and that it's T&, not const T&, even for containers like set and unordered_set that provide const iterators only.

The way we handle const containers is just that in the case of a const-qualified container (including match_results) there are no operations that return Container::reference. That's already the case, so this issue is complaining about an unused typedef.

[2013-10-17: Daniel comments]

The std::initializer_list synopsis, 18.9 [support.initlist] shows a similar problem:

template<class E> class initializer_list {
public:
  typedef E value_type;
  typedef const E& reference;
  typedef const E& const_reference;
  […]
}

Given the fact that std::initializer_list doesn't meet the container requirements anyway (and is such a core-language related type) I recommend to stick with the current state.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Change the class template match_results header synopsis, 28.10 [re.results] p4 as indicated:

    typedef const value_type& const_reference;
    typedef const_referencevalue_type& reference;
    

2308. Clarify container destructor requirements w.r.t. std::array

Section: 23.2.1 [container.requirements.general] Status: Immediate Submitter: Jonathan Wakely Opened: 2013-09-26 Last modified: 2014-02-13

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

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

View all issues with Immediate status.

Discussion:

It has been suggested that Table 96 — "Container requirements" makes confusing requirements for the destructor of std::array:

"note: the destructor is applied to every element of a; all the memory is deallocated."

Since std::array obtains no memory, there is none to deallocate, arguably making it unclear what the requirement means for std::array::~array().

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Change in 23.2.1 [container.requirements.general], Table 96 — "Container requirements", the "Assertion/note/pre-/post-condition" for the expression "(&a)->~X()" as indicated:

    note: the destructor is applied to every element of a; all theany memory obtained is deallocated.


2313. tuple_size should always derive from integral_constant<size_t, N>

Section: 20.4.2.5 [tuple.helper] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-12

View all other issues in [tuple.helper].

View all issues with Immediate status.

Discussion:

In 20.4.2.5 [tuple.helper], the "primary template" is depicted as:

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

However, 20.3.4 [pair.astuple]/1-2 and 23.3.2.9 [array.tuple]/1-2 are underspecified, saying:

tuple_size<pair<T1, T2> >::value

Returns: Integral constant expression.

Value: 2.

tuple_size<array<T, N> >::value

Return type: integral constant expression.

Value: N

They should be required to behave like the "primary template". This is more than a stylistic decision — it allows tuple_size to be passed to a function taking integral_constant.

LWG 1118 noticed this underspecification, but instead of correcting it, the resolution changed 20.4.2.5 [tuple.helper]/3 to require tuple_size<cv T> to derive from integral_constant<remove_cv<decltype(TS::value)>::type, TS::value>. This is unnecessarily overgeneralized. tuple_size is primarily for tuples, where it is required to be size_t, and it has been extended to handle pairs and arrays, which (as explained above) should also be guaranteed to be size_t. tuple_size<cv T> works with cv-qualified tuples, pairs, arrays, and user-defined types that also want to participate in the tuple_size system. It would be far simpler and perfectly reasonable to expect that user-defined types supporting the "tuple-like protocol" should have tuple_sizes of size_t.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit 20.3.4 [pair.astuple]/1-2 as indicated:

    tuple_size<pair<T1, T2> >::value
    template <class T1, class T2>
    struct tuple_size<pair<T1, T2>>
      : integral_constant<size_t, 2> { };
    

    -1- Returns: Integral constant expression.

    -2- Value: 2.

  2. Edit 23.3.2.9 [array.tuple]/1-2 as indicated:

    tuple_size<array<T, N> >::value
    template <class T, size_t N>
    struct tuple_size<array<T, N>>
      : integral_constant<size_t, N> { };
    

    -1- Returns: Integral constant expression.

    -2- Value: N.

  3. Edit 20.4.2.5 [tuple.helper]/p1-p3 as indicated:

    template <class T> struct tuple_size;
    

    -?- Remarks: All specializations of tuple_size<T> shall meet the UnaryTypeTrait requirements (20.10.1 [meta.rqmts]) with a BaseCharacteristic of integral_constant<size_t, N> for some N.

    template <class... Types>
    struct tuple_size<tuple<Types...> >
      : integral_constant<size_t, sizeof...(Types)> { };
      
    template <size_t I, class... Types>
    class tuple_element<I, tuple<Types...> > {
    public:
      typedef TI type;
    };
    

    -1- Requires: I < sizeof...(Types). The program is ill-formed if I is out of bounds.

    […]

    template <class T> class tuple_size<const T>;
    template <class T> class tuple_size<volatile T>;
    template <class T> class tuple_size<const volatile T>;
    

    -3- Let TS denote tuple_size<T> of the cv-unqualified type T. Then each of the three templates shall meet the UnaryTypeTrait requirements (20.10.1 [meta.rqmts]) with a BaseCharacteristic of

    integral_constant<remove_cv<decltype(TS::value)>::typesize_t, TS::value>
    

2314. apply() should return decltype(auto) and use decay_t before tuple_size

Section: 20.5.1 [intseq.general] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-12

View all issues with Immediate status.

Discussion:

The example in 20.5.1 [intseq.general]/2 depicts apply_impl() and apply() as returning auto. This is incorrect because it will trigger decay and will not preserve F's return type. For example, if invoking the functor returns const int&, apply_impl() and apply() will return int. decltype(auto) should be used for "perfect returning".

Additionally, this depicts apply() as taking Tuple&&, then saying "std::tuple_size<Tuple>::value". This is incorrect because when apply() is called with lvalue tuples, perfect forwarding will deduce Tuple to be cv tuple&, but 20.4.2.5 [tuple.helper] says that tuple_size handles only cv tuple, not references to tuples. Using remove_reference_t would avoid this problem, but so would decay_t, which has a significantly shorter name. (The additional transformations that decay_t does are neither beneficial nor harmful here.)

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit the example code in 20.5.1 [intseq.general]/2 as indicated:

    template<class F, class Tuple, std::size_t... I>
      autodecltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
        return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
      }
    template<class F, class Tuple>
      autodecltype(auto) apply(F&& f, Tuple&& t) {
        using Indices = make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>;
        return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices());
      }
    

2315. weak_ptr should be movable

Section: 20.8.2.3 [util.smartptr.weak] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-13

View all other issues in [util.smartptr.weak].

View all issues with Immediate status.

Discussion:

Like shared_ptr, weak_ptr should be movable to avoid unnecessary atomic increments/decrements of the weak refcount.

[2014-02-13 Issaquah: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit 20.8.2.3 [util.smartptr.weak]/1, class template weak_ptr synopsis, as indicated:

    namespace std {
      template<class T> class weak_ptr {
      public:
        typedef T element_type;
    
        // 20.9.2.3.1, constructors
        constexpr weak_ptr() noexcept;
        template<class Y> weak_ptr(shared_ptr<Y> const& r) noexcept;
        weak_ptr(weak_ptr const& r) noexcept;
        template<class Y> weak_ptr(weak_ptr<Y> const& r) noexcept;
        weak_ptr(weak_ptr&& r) noexcept;
        template<class Y> weak_ptr(weak_ptr<Y>&& r) noexcept;
    
        […]
    
        // 20.9.2.3.3, assignment
        weak_ptr& operator=(weak_ptr const& r) noexcept;
        template<class Y> weak_ptr& operator=(weak_ptr<Y> const& r) noexcept;
        template<class Y> weak_ptr& operator=(shared_ptr<Y> const& r) noexcept;
        weak_ptr& operator=(weak_ptr&& r) noexcept;
        template<class Y> weak_ptr& operator=(weak_ptr<Y>&& r) noexcept;
      };
    }
    
  2. Add the following new paragraphs at the end of sub-clause 20.8.2.3.1 [util.smartptr.weak.const]:

    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 implicitly convertible to 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.

  3. Edit 20.8.2.3.3 [util.smartptr.weak.assign] as indicated:

    weak_ptr& operator=(const weak_ptr& r) noexcept;
    template<class Y> weak_ptr& operator=(const weak_ptr<Y>& r) noexcept;
    template<class Y> weak_ptr& operator=(const shared_ptr<Y>& r) noexcept;
    

    -1- Effects: […]

    -2- Remarks: […]

    -?- Returns: *this.

    weak_ptr& operator=(weak_ptr&& r) noexcept;
    template<class Y> weak_ptr& operator=(weak_ptr<Y>&& r) noexcept;
    

    -?- Effects: Equivalent to weak_ptr(std::move(r)).swap(*this).

    -?- Returns: *this.


2316. weak_ptr::lock() should be atomic

Section: 20.8.2.3.5 [util.smartptr.weak.obs] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-12

View all other issues in [util.smartptr.weak.obs].

View all issues with Immediate status.

Discussion:

20.8.2.2 [util.smartptr.shared]/4 says: "For purposes of determining the presence of a data race, member functions shall access and modify only the shared_ptr and weak_ptr objects themselves and not objects they refer to. Changes in use_count() do not reflect modifications that can introduce data races." This requires shared_ptr/weak_ptr implementations to protect their strong and weak refcounts with atomic operations, without the Standardese having to say this elsewhere. However, 20.8.2.3.5 [util.smartptr.weak.obs]/5 describes weak_ptr::lock() with "Returns: expired() ? shared_ptr<T>() : shared_ptr<T>(*this)." Even after considering the blanket wording about data races, this specification is insufficient. If this conditional expression were literally implemented, the use_count() could change from nonzero to zero after testing expired(), causing shared_ptr<T>(*this) to throw bad_weak_ptr when the intention is for weak_ptr::lock() to return empty or nonempty without throwing — indeed, weak_ptr::lock() is marked as noexcept.

We all know what weak_ptr::lock() should do, the Standardese just doesn't say it. shared_ptr(const weak_ptr<Y>&)'s specification is not really affected because 20.8.2.2.1 [util.smartptr.shared.const]/23-27 describes the behavior with English instead of code.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit 20.8.2.3.5 [util.smartptr.weak.obs]/5 as indicated:

    shared_ptr<T> lock() const noexcept;
    

    -5- Returns: expired() ? shared_ptr<T>() : shared_ptr<T>(*this), executed atomically.


2317. The type property queries should be UnaryTypeTraits returning size_t

Section: 20.10.5 [meta.unary.prop.query] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-12

View all issues with Immediate status.

Discussion:

The sibling sections 20.10.4 [meta.unary], 20.10.6 [meta.rel], and 20.10.7 [meta.trans] respectively specify UnaryTypeTraits, BinaryTypeTraits, and TransformationTraits, as stated by each /2 paragraph. However, 20.10.5 [meta.unary.prop.query] is underspecified. alignment_of, rank, and extent are said to produce "Values", but the type of that Value is not specified, and the struct templates are not required to derive from integral_constant. Such derivation is more than stylistic — it allows the structs to be passed to functions taking integral_constant.

alignment_of returns alignof(T) which is size_t (5.3.6 [expr.alignof]/2). extent returns an array bound, which is clearly size_t. rank returns "the number of dimensions" of an array, so any type could be chosen, with size_t being a reasonable choice. (Another choice would be unsigned int, to match extent's template parameter I.)

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Following 20.10.5 [meta.unary.prop.query]/1 add a new paragraph as indicated:

    Each of these templates shall be a UnaryTypeTrait (20.10.1 [meta.rqmts]) with a BaseCharacteristic of integral_constant<size_t, Value>.


2320. select_on_container_copy_construction() takes allocators, not containers

Section: 23.2.1 [container.requirements.general] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-13

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

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

View all issues with Immediate status.

Discussion:

23.2.1 [container.requirements.general]/7 says "Copy constructors for these container types obtain an allocator by calling allocator_traits<allocator_type>::select_on_container_copy_construction on their first parameters." However, 20.7.8.2 [allocator.traits.members]/8 says that this takes const Alloc&, not a container. 23.2.1 [container.requirements.general]/7 goes on to say "Move constructors obtain an allocator by move construction from the allocator belonging to the container being moved." so we can follow that wording.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. In 23.2.1 [container.requirements.general]/7 change as indicated:

    -7- Unless otherwise specified, all containers defined in this clause obtain memory using an allocator (see 17.6.3.5). Copy constructors for these container types obtain an allocator by calling allocator_traits<allocator_type>::select_on_container_copy_construction on their first parametersthe allocator belonging to the container being copied. Move constructors obtain an allocator by move construction from the allocator belonging to the container being moved. […]


2322. Associative(initializer_list, stuff) constructors are underspecified

Section: 23.2.4 [associative.reqmts], 23.2.5 [unord.req] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-12

View other active issues in [associative.reqmts].

View all other issues in [associative.reqmts].

View all issues with Immediate status.

Discussion:

23.2.4 [associative.reqmts] specifies both X(i,j) and X(i,j,c), but only X(il). 23.4.4.1 [map.overview] declares "map(initializer_list<value_type>, const Compare& = Compare(), const Allocator& = Allocator());" but 23.4.4.2 [map.cons] intentionally doesn't explain it, relying on the big table's requirements. As a result, map(il, c)'s behavior is not actually specified by the Standard. (All of the other ordered associative containers also provide such constructors.)

The unordered associative containers are similarly affected, although they have more arguments. (Again, the actual containers are correctly depicted with the desired constructors, their behavior just isn't specified.)

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit 23.2.4 [associative.reqmts], Table 102 — "Associative container requirements", as indicated:

    Table 102 — Associative container requirements (in addition to container) (continued)
    Expression Return type Assertion/note pre-/post-condition Complexity
    X(il); Same as X(il.begin(), il.end()). sSame as X(il.begin(), il.end()).
    X(il, c);   Same as X(il.begin(), il.end(), c). Same as X(il.begin(), il.end(), c).
  2. Edit 23.2.5 [unord.req], Table 103 "Unordered associative container requirements", as indicated:

    Table 103 — Unordered associative container requirements (in addition to container)
    Expression Return type Assertion/note pre-/post-condition Complexity
    X(il) X Same as X(il.begin(), il.end()). Same as X(il.begin(), il.end()).
    X(il, n) X Same as X(il.begin(), il.end(), n). Same as X(il.begin(), il.end(), n).
    X(il, n, hf) X Same as X(il.begin(), il.end(), n, hf). Same as X(il.begin(), il.end(), n, hf).
    X(il, n, hf, eq) X Same as X(il.begin(), il.end(), n, hf, eq). Same as X(il.begin(), il.end(), n, hf, eq).

2323. vector::resize(n, t)'s specification should be simplified

Section: 23.3.6.3 [vector.capacity], 23.3.3.3 [deque.capacity] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-12

View other active issues in [vector.capacity].

View all other issues in [vector.capacity].

View all issues with Immediate status.

Discussion:

First, 23.3.3.3 [deque.capacity]/4 and 23.3.6.3 [vector.capacity]/16 say that resize(size_type sz, const T& c) "Requires: T shall be MoveInsertable into *this and CopyInsertable into *this." The CopyInsertable requirement is correct (because sz might be size() + 2 or more), but the MoveInsertable requirement is redundant due to 23.2.1 [container.requirements.general]/13: "T is CopyInsertable into X means that, in addition to T being MoveInsertable into X, the [...]". (LWG 2033's resolution said that this was "not redundant, because CopyInsertable is not necessarily a refinement of MoveInsertable" which was true at the time, but then LWG 2177's resolution made it a refinement.)

Second, 23.3.6.3 [vector.capacity]/17 says "Remarks: If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects." This is confusing because T is required to be CopyInsertable. (/14 says the same thing for resize(size_type sz), where it is correct because that overload requires only MoveInsertable and DefaultInsertable.)

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit 23.3.3.3 [deque.capacity]/4 as indicated:

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

    […]

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

  2. Edit 23.3.6.3 [vector.capacity]/16+17 as indicated:

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

    […]

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

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


2324. Insert iterator constructors should use addressof()

Section: 24.5.2.2.1 [back.insert.iter.cons], 24.5.2.4.1 [front.insert.iter.cons], 24.5.2.6.1 [insert.iter.cons] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-12

View all issues with Immediate status.

Discussion:

24.5.2.2.1 [back.insert.iter.cons]/1, 24.5.2.4.1 [front.insert.iter.cons]/1, and 24.5.2.6.1 [insert.iter.cons]/1 say "Initializes container with &x", which doesn't defend against containers overloading operator&(). Containers are now required to have such defenses for their elements, so we may as well be consistent here.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit 24.5.2.2.1 [back.insert.iter.cons]/1 as indicated:

    explicit back_insert_iterator(Container& x);
    

    -1- Effects: Initializes container with &xstd::addressof(x).

  2. Edit 24.5.2.4.1 [front.insert.iter.cons]/1 as indicated:

    explicit front_insert_iterator(Container& x);
    

    -1- Effects: Initializes container with &xstd::addressof(x).

  3. Edit 24.5.2.6.1 [insert.iter.cons]/1 as indicated:

    insert_iterator(Container& x, typename Container::iterator i);
    

    -1- Effects: Initializes container with &xstd::addressof(x) and iter with i.


2329. regex_match()/regex_search() with match_results should forbid temporary strings

Section: 28.4 [re.syn] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-13

View all other issues in [re.syn].

View all issues with Immediate status.

Discussion:

Consider the following code:

const regex r(R"(meow(\d+)\.txt)");
smatch m;
if (regex_match(dir_iter->path().filename().string(), m, r)) {
  DoSomethingWith(m[1]);
}

This occasionally crashes. The problem is that dir_iter->path().filename().string() returns a temporary string, so the match_results contains invalidated iterators into a destroyed temporary string.

It's fine for regex_match/regex_search(str, reg) to accept temporary strings, because they just return bool. However, the overloads taking match_results should forbid temporary strings.

[2014-02-13 Issaquah: Move as Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Edit 28.4 [re.syn], header <regex> synopsis, as indicated:

    #include <initializer_list>
    
    namespace std {
    
      […]
      
      // 28.11.2, function template regex_match:
      […]
      template <class ST, class SA, class Allocator, class charT, class traits> 
      bool regex_match(const basic_string<charT, ST, SA>&&, 
                       match_results<
                         typename basic_string<charT, ST, SA>::const_iterator, 
                         Allocator>&, 
                       const basic_regex<charT, traits>&, 
                       regex_constants::match_flag_type = 
                         regex_constants::match_default) = delete;
    
      // 28.11.3, function template regex_search:
      […]
      template <class ST, class SA, class Allocator, class charT, class traits> 
      bool regex_search(const basic_string<charT, ST, SA>&&, 
                        match_results<
                          typename basic_string<charT, ST, SA>::const_iterator, 
                          Allocator>&, 
                        const basic_regex<charT, traits>&, 
                        regex_constants::match_flag_type = 
                          regex_constants::match_default) = delete;
      […]
    }
    

2330. regex("meow", regex::icase) is technically forbidden but should be permitted

Section: 28.5.1 [re.synopt] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-13

View other active issues in [re.synopt].

View all other issues in [re.synopt].

View all issues with Immediate status.

Discussion:

28.5.1 [re.synopt]/1 says "A valid value of type syntax_option_type shall have exactly one of the elements ECMAScript, basic, extended, awk, grep, egrep, set."

This "exactly one" wording technically forbids passing icase by itself! Users should not be required to pass regex::ECMAScript | regex::icase. (Note that the cost of an additional check for no grammar being explicitly requested is completely irrelevant, as regex construction is so much more expensive.)

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

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

    -1- The type syntax_option_type is an implementation-defined bitmask type (17.5.2.1.3). Setting its elements has the effects listed in table 138. A valid value of type syntax_option_type shall have exactlyat most one of the grammar elements ECMAScript, basic, extended, awk, grep, egrep, set. If no grammar element is set, the default grammar is ECMAScript.


2332. regex_iterator/regex_token_iterator should forbid temporary regexes

Section: 28.12 [re.iter] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-09-21 Last modified: 2014-02-14

View all issues with Immediate status.

Discussion:

Users can write "for(sregex_iterator i(s.begin(), s.end(), regex("meow")), end; i != end; ++i)", binding a temporary regex to const regex& and storing a pointer to it. This will compile silently, triggering undefined behavior at runtime. We now have the technology to prevent this from compiling, like how reference_wrapper refuses to bind to temporaries.

[2014-02-14 Issaquah meeting: Move to Immediate]

Proposed resolution:

This wording is relative to N3691.

  1. Change 28.12.1 [re.regiter]/1, class template regex_iterator synopsis, as indicated:

    regex_iterator();
    regex_iterator(BidirectionalIterator a, BidirectionalIterator b,
      const regex_type& re,
      regex_constants::match_flag_type m =
        regex_constants::match_default);
    regex_iterator(BidirectionalIterator a, BidirectionalIterator b,
      const regex_type&& re,
      regex_constants::match_flag_type m =
        regex_constants::match_default) = delete;
    
  2. Change 28.12.2 [re.tokiter]/6, class template regex_token_iterator synopsis, as indicated:

    regex_token_iterator();
    regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b,
                         const regex_type& re,
                         int submatch = 0,
                         regex_constants::match_flag_type m =
                           regex_constants::match_default);
    regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b,
                         const regex_type& re,
                         const std::vector<int>& submatches,
                         regex_constants::match_flag_type m =
                           regex_constants::match_default);
    regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b,
                         const regex_type& re,
                         initializer_list<int> submatches,
                         regex_constants::match_flag_type m =
                           regex_constants::match_default);
    template <std::size_t N>
    regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b,
                         const regex_type& re,
                         const int (&submatches)[N],
                         regex_constants::match_flag_type m =
                           regex_constants::match_default);
    regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b,
                         const regex_type&& re,
                         int submatch = 0,
                         regex_constants::match_flag_type m =
                           regex_constants::match_default) = delete;
    regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b,
                         const regex_type&& re,
                         const std::vector<int>& submatches,
                         regex_constants::match_flag_type m =
                           regex_constants::match_default) = delete;
    regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b,
                         const regex_type&& re,
                         initializer_list<int> submatches,
                         regex_constants::match_flag_type m =
                           regex_constants::match_default) = delete;
    template <std::size_t N>
    regex_token_iterator(BidirectionalIterator a, BidirectionalIterator b,
                         const regex_type&& re,
                         const int (&submatches)[N],
                         regex_constants::match_flag_type m =
                           regex_constants::match_default) = delete;
    

2339. Wording issue in nth_element

Section: 25.4.2 [alg.nth.element] Status: Immediate Submitter: Christopher Jefferson Opened: 2013-10-19 Last modified: 2014-02-12

View all other issues in [alg.nth.element].

View all issues with Immediate status.

Discussion:

The wording of nth_element says:

template<class RandomAccessIterator>
  void nth_element(RandomAccessIterator first, RandomAccessIterator nth,
                   RandomAccessIterator last);

After nth_element the element in the position pointed to by nth is the element that would be in that position if the whole range were sorted. Also for every iterator i in the range [first,nth) and every iterator j in the range [nth,last) it holds that: !(*j < *i) or comp(*j, *i) == false.

That wording, to me, implies that there must be an element at 'nth'. However, gcc at least accepts nth == last, and returns without effect (which seems like the sensible option).

Is it intended to accept nth == last? If so, then I would suggest adding this to the wording explicitly, say:

After nth_element the element in the position pointed to by nth, if any, is the element that would be in that position if the whole range were sorted. Also for every iterator i in the range [first,nth) and every iterator j in the range [nth,last) it holds that: !(*j < *i) or comp(*j, *i) == false.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3797.

  1. Modify 25.4.2 [alg.nth.element]/1 as indicated:

    template<class RandomAccessIterator>
      void nth_element(RandomAccessIterator first, RandomAccessIterator nth,
                       RandomAccessIterator last);
    template<class RandomAccessIterator, class Compare>
      void nth_element(RandomAccessIterator first, RandomAccessIterator nth,
                       RandomAccessIterator last, Compare comp);
    

    -1- After nth_element the element in the position pointed to by nth is the element that would be in that position if the whole range were sorted, unless nth == last. Also for every iterator i in the range [first,nth) and every iterator j in the range [nth,last) it holds that: !(*j < *i) or comp(*j, *i) == false.


2341. Inconsistency between basic_ostream::seekp(pos) and basic_ostream::seekp(off, dir)

Section: 27.7.3.5 [ostream.seeks] Status: Immediate Submitter: Marshall Clow Opened: 2013-10-21 Last modified: 2014-02-12

View all issues with Immediate status.

Discussion:

In 27.7.3.5 [ostream.seeks], we have:

basic_ostream<charT,traits>& seekp(pos_type pos);

-3- Effects: If fail() != true, executes rdbuf()->pubseekpos(pos, ios_base::out). In case of failure, the function calls setstate(failbit) (which may throw ios_base::failure).

-4- Returns: *this.

basic_ostream<charT,traits>& seekp(off_type off, ios_base::seekdir dir);

-5- Effects: If fail() != true, executes rdbuf()->pubseekoff(off, dir, ios_base::out).

-6- Returns: *this.

The first call is required to set the failbit on failure, but the second is not

So (given two ostreams, os1 and os2) the following code (confusingly) works:

os1.seekp(-1);
assert(os1.fail());

os2.seekp(-1, std::ios_base::beg);
assert(os2.good());

Note that the description of basic_istream<charT,traits>& seekg(off_type off, ios_base::seekdir dir) in 27.7.2.3 [istream.unformatted] p43 does require setting failbit.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3797.

  1. Modify 27.7.3.5 [ostream.seeks]p5 as indicated:

    basic_ostream<charT,traits>& seekp(off_type off, ios_base::seekdir dir);
    

    -5- Effects: If fail() != true, executes rdbuf()->pubseekoff(off, dir, ios_base::out). In case of failure, the function calls setstate(failbit) (which may throw ios_base::failure).

    -6- Returns: *this.


2344. quoted()'s interaction with padding is unclear

Section: 27.7.6 [quoted.manip] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-11-01 Last modified: 2014-02-14

View other active issues in [quoted.manip].

View all other issues in [quoted.manip].

View all issues with Immediate status.

Discussion:

Given this code:

cout << "[" << left << setfill('x') << setw(20) << R"("AB \"CD\" EF")" << "]" << endl;
cout << "[" << left << setfill('y') << setw(20) << quoted(R"(GH "IJ" KL)") << "]" << endl;

The first line prints ["AB \"CD\" EF"xxxxxx]. The second line should probably print ["GH \"IJ\" KL"yyyyyy], but 27.7.6 [quoted.manip]/2 doesn't say whether or how quoted() should interact with padding. All it says is that

"out << quoted(s, delim, escape) behaves as if it inserts the following characters into out using character inserter function templates (27.7.3.6.4)".

27.7.3.6.4 [ostream.inserters.character] specifies both single-character and null-terminated inserters, both referring to 27.7.3.6.1 [ostream.formatted.reqmts]/3 for padding. Literally implementing quoted() with single-character inserters would result in padding being emitted after the first character, with undesirable effects for ios_base::left.

It appears that 21.4.8.9 [string.io]/5 has the appropriate incantations to follow here. It says that os << str

"Behaves as a formatted output function (27.7.3.6.1) of os. Forms a character sequence seq, initially consisting of the elements defined by the range [str.begin(), str.end()). Determines padding for seq as described in 27.7.3.6.1. Then inserts seq as if by calling os.rdbuf()->sputn(seq, n), where n is the larger of os.width() and str.size(); then calls os.width(0)."

Additionally, saying that it's a "formatted output function" activates 27.7.3.6.1 [ostream.formatted.reqmts]/1's wording for sentry objects.

[2014-02-14 Issaquah meeting: Move to Immediate]

Proposed resolution:

This wording is relative to N3797.

  1. Edit 27.7.6 [quoted.manip] as follows:

    template <class charT>
      unspecified quoted(const charT* s, charT delim=charT('"'), charT escape=charT('\\'));
    template <class charT, class traits, class Allocator>
      unspecified quoted(const basic_string<charT, traits, Allocator>& s,
                         charT delim=charT('"'), charT escape=charT('\\'));
    

    -2- Returns: An object of unspecified type such that if out is an instance of basic_ostream with member type char_type the same as charT, then the expression out << quoted(s, delim, escape) behaves as if it inserts the following characters into out using character inserter function templates (27.7.3.6.4), which may throw ios_base::failure (27.5.3.1.1)a formatted output function (27.7.3.6.1 [ostream.formatted.reqmts]) of out. This forms a character sequence seq, initially consisting of the following elements:

    • delim.

    • Each character in s. If the character to be output is equal to escape or delim, as determined by operator==, first output escape.

    • delim.

    Let x be the number of elements initially in seq. Then padding is determined for seq as described in 27.7.3.6.1 [ostream.formatted.reqmts], seq is inserted as if by calling out.rdbuf()->sputn(seq, n), where n is the larger of out.width() and x, and out.width(0) is called. The expression out << quoted(s, delim, escape) shall have type basic_ostream<charT, traits>& and value out.


2346. integral_constant's member functions should be marked noexcept

Section: 20.10.3 [meta.help] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2013-11-05 Last modified: 2014-02-12

View all other issues in [meta.help].

View all issues with Immediate status.

Discussion:

Obvious.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3797.

  1. Edit 20.10.3 [meta.help] as indicated:

    namespace std {
      template<class T, T v>
      struct integral_constant {
        static constexpr T value = v;
        typedef T value_type;
        typedef integral_constant<T,v> type;
        constexpr operator value_type() const noexcept { return value; }
        constexpr value_type operator()() const noexcept { return value; }
      };
      […]
    }
    

2350. min, max, and minmax should be constexpr

Section: 25.4.7 [alg.min.max] Status: Immediate Submitter: Ville Voutilainen Opened: 2013-12-15 Last modified: 2014-02-12

View other active issues in [alg.min.max].

View all other issues in [alg.min.max].

View all issues with Immediate status.

Discussion:

Having min, max, and minmax constexpr was a large part of the motivation to allow reference-to-const arguments for constexpr functions as per N3039. Furthermore, initializer_lists are immutable and not-movable-from for large part in order to allow using them in constexpr contexts and other hoisting-optimizations. In N3797 version of the draft none of these functions are constexpr, and they should be made constexpr.

Proposed resolution:

This wording is relative to N3797.

  1. In 25.1 [algorithms.general], header <algorithm> synopsis, and 25.4.7 [alg.min.max], change as indicated (add constexpr to every signature before min_element):

    template<class T> constexpr const T& min(const T& a, const T& b);
    template<class T, class Compare>
    constexpr const T& min(const T& a, const T& b, Compare comp);
    […]
    template<class T>
    constexpr T min(initializer_list<T> t);
    template<class T, class Compare>
    constexpr T min(initializer_list<T> t, Compare comp);
    […]
    template<class T> constexpr const T& max(const T& a, const T& b);
    template<class T, class Compare>
    constexpr const T& max(const T& a, const T& b, Compare comp);
    […]
    template<class T>
    constexpr T max(initializer_list<T> t);
    template<class T, class Compare>
    constexpr T max(initializer_list<T> t, Compare comp);
    […]
    template<class T> constexpr pair<const T&, const T&> minmax(const T& a, const T& b);
    template<class T, class Compare>
    constexpr pair<const T&, const T&> minmax(const T& a, const T& b, Compare comp);
    […]
    template<class T>
    constexpr pair<T, T> minmax(initializer_list<T> t);
    template<class T, class Compare>
    constexpr pair<T, T> minmax(initializer_list<T> t, Compare comp);
    

2356. Stability of erasure in unordered associative containers

Section: 23.2.5 [unord.req] Status: Immediate Submitter: Joaquín M López Muñoz Opened: 2014-01-21 Last modified: 2014-02-13

View other active issues in [unord.req].

View all other issues in [unord.req].

View all issues with Immediate status.

Discussion:

Issue 518 resolution for unordered associative containers, modelled after that of issue 371, which is related to associative containers, states that insertion, erasure and rehashing preserve the relative ordering of equivalent elements. Unfortunately, this is not sufficient to guarantee the validity of code such as this:

std::unordered_multimap<int, int> m;
auto i = m.begin();
while (i != m.end()) {
  if (pred(i))
    m.erase(i++);
  else
    ++i;
}

(which is a direct translation from multimap to unordered_multimap of the motivating example in 371), or even this:

std::unordered_multimap<int, int> m;
auto p = m.equal_range(k);
while (p.first != p.second) {
  if (pred(p.first))
    m.erase((p.first)++);
  else
    ++(p.first);
}

because the relative ordering of non-equivalent elements elements could potentially change after erasure (not that any actual implementation does that, anyway). Such an underspecification does not happen for regular associative containers, where the relative ordering of non-equivalent elements is kept by design.

[2014-02-13 Issaquah: Move to Immediate]

Proposed resolution:

This wording is relative to N3797.

  1. Modify 23.2.5 [unord.req], p14 as indicated:

    -14- The insert and emplace members shall not affect the validity of references to container elements, but may invalidate all iterators to the container. The erase members shall invalidate only iterators and references to the erased elements, and preserve the relative order of the elements that are not erased.


2357. Remaining "Assignable" requirement

Section: 25.3.13 [alg.partitions] Status: Immediate Submitter: Daniel Krügler Opened: 2014-02-01 Last modified: 2014-02-12

View all other issues in [alg.partitions].

View all issues with Immediate status.

Discussion:

The Requires element of partition_copy says (emphasis mine):

Requires: InputIterator's value type shall be Assignable, and …

The C++03 term Assignable was replaced by CopyAssignable, remaining cleanups happened via LWG issue 972, but algorithm partition_copy was not affected at that time (during that time the requirements of partition_copy didn't mention writable nor assignable, but I cannot track down at the moment where these requirements had been added). Presumably this requirement should be corrected similarly to the approach used in 972.

Another question is whether a CopyAssignable is needed here, given the fact that we already require "writable to" an OutputIterator which is defined in 24.2.1 [iterator.requirements.general] and does already impose the necessary statement

*out = *in;

Given the fact that partition_copy never touches any input value twice, there is no reason why anything more than writable to should be necessary.

The below suggested primary resolution does not respond to the second part of this question.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3797.

  1. Modify 25.3.13 [alg.partitions], p12 as indicated:

    -12- Requires: InputIterator's value type shall be CopyAssignable, and shall be writable to the out_true and out_false OutputIterators, and shall be convertible to Predicate's argument type. The input range shall not overlap with either of the output ranges.


2359. How does regex_constants::nosubs affect basic_regex::mark_count()?

Section: 28.5.1 [re.synopt] Status: Immediate Submitter: Jonathan Wakely Opened: 2014-02-01 Last modified: 2014-02-12

View other active issues in [re.synopt].

View all other issues in [re.synopt].

View all issues with Immediate status.

Discussion:

As discussed in c++std-lib-35399 and its replies, I can see two possible interpretations of the effects of regex_constants::nosubs:

  1. The effect of nosubs only applies during matching. Parentheses are still recognized as marking a sub-expression by the basic_regex compiler, and basic_regex::mark_count() still returns the number of marked sub-expressions, but anything they match is not stored in the results. This means it is not always true that results.size() == r.mark_count() + 1 for a successful match.

  2. nosubs affects how a regular expression is compiled, altering the state of the std::basic_regex object so that mark_count() returns zero. This also affects any subsequent matching.

The definition of nosubs should make this clear.

The wording in 28.5.1 [re.synopt]/1 seems to imply that nosubs only has effects during matching, which is (1), but all known implementations do (2). John Maddock confirmed that (2) was intended.

[Issaquah 20014-10-11: Move to Immediate]

Proposed resolution:

This wording is relative to N3797.

  1. Apply the following edit to the table in 28.5.1 [re.synopt]/1

    Specifies that no sub-expressions shall be considered to be marked, so that when a regular expression is matched against a character container sequence, no sub-expression matches shall be stored in the supplied match_results structure.


2360. reverse_iterator::operator*() is unimplementable

Section: 24.5.1.1 [reverse.iterator] Status: Immediate Submitter: Stephan T. Lavavej Opened: 2014-02-07 Last modified: 2014-02-14

View all other issues in [reverse.iterator].

View all issues with Immediate status.

Discussion:

Previously, C++03 24.4.1.3.3 [lib.reverse.iter.op.star] required:

reference operator*() const;

Effects:

Iterator tmp = current;
return *--tmp;

Now, N3797 24.5.1.1 [reverse.iterator] depicts:

private:
  Iterator deref_tmp; // exposition only
};

And 24.5.1.3.4 [reverse.iter.op.star] requires:

reference operator*() const;

Effects:

deref_tmp = current;
--deref_tmp;
return *deref_tmp;

[Note: This operation must use an auxiliary member variable rather than a temporary variable to avoid returning a reference that persists beyond the lifetime of its associated iterator. (See 24.2.) — end note]

As written, this won't compile, because operator*() is const yet it's modifying (via assignment and decrement) the deref_tmp data member. So what happens if you say "mutable Iterator deref_tmp;"?

DANGER: WARP CORE BREACH IMMINENT.

The Standard requires const member functions to be callable from multiple threads simultaneously. This is 17.6.5.9 [res.on.data.races]/3: "A C++ standard library function shall not directly or indirectly modify objects (1.10) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function's non-const arguments, including this."

Multiple threads simultaneously modifying deref_tmp will trigger data races, so both mutable and some form of synchronization (e.g. mutex or atomic) are actually necessary!

Here's what implementations currently do: Dinkumware/VC follows C++03 and doesn't use deref_tmp (attempting to implement it is what led me to file this issue). According to Jonathan Wakely, libstdc++ also follows C++03 (see PR51823 which is suspended until LWG 2204 is resolved). According to Marshall Clow, libc++ uses deref_tmp with mutable but without synchronization, so it can trigger data races.

This deref_tmp Standardese was added by LWG 198 "Validity of pointers and references unspecified after iterator destruction" and is present in Working Papers going back to N1638 on April 11, 2004, long before C++ recognized the existence of multithreading and developed the "const means simultaneously readable" convention.

A related issue is LWG 1052 "reverse_iterator::operator-> should also support smart pointers" which mentioned the need to depict mutable in the Standardese, but it was resolved NAD Future and no change was made.

Finally, LWG 2204 "reverse_iterator should not require a second copy of the base iterator" talked about removing deref_tmp, but without considering multithreading.

I argue that deref_tmp must be removed. Its existence has highly undesirable consequences: either no synchronization is used, violating the Standard's usual multithreading guarantees, or synchronization is used, adding further costs for all users that benefit almost no iterators.

deref_tmp is attempting to handle iterators that return references to things "inside themselves", which I usually call "stashing iterators" (as they have a secret stash). Note that these are very unusual, and are different from proxy iterators like vector<bool>::iterator. While vector<bool>::iterator's operator*() does not return a true reference, it refers to a bit that is unrelated to the iterator's lifetime.

[2014-02-14 Issaquah meeting: Move to Immediate]

Strike superfluous note to avoid potential confusion, and move to Immediate.

Proposed resolution:

This wording is relative to N3797.

  1. Change class template reverse_iterator synopsis, 24.5.1.1 [reverse.iterator], as indicated:

    […]
    protected:
      Iterator current;
    private:
      Iterator deref_tmp; // exposition only
    };
    
  2. Change 24.5.1.3.4 [reverse.iter.op.star] as indicated:

    reference operator*() const;
    

    -1- Effects:

    deref_tmp = current;
    --deref_tmp;
    return *deref_tmp;
    Iterator tmp = current;
    return *--tmp;
    

    -2- [Note: This operation must use an auxiliary member variable rather than a temporary variable to avoid returning a reference that persists beyond the lifetime of its associated iterator. (See 24.2.) — end note]