C++ Standard Library Issues to be moved in [INSERT CURRENT MEETING HERE]

Doc. no. R0165???
Date:

Revised 2017-11-29 at 03:52:06 UTC

Project: Programming Language C++
Reply to: Marshall Clow <lwgchair@gmail.com>

Ready Issues


2843(i). Unclear behavior of std::pmr::memory_resource::do_allocate()

Section: 23.12.2.2 [mem.res.private] Status: Ready Submitter: Jens Maurer Opened: 2016-12-13 Last modified: 2017-11-12

Priority: 3

View all other issues in [mem.res.private].

View all issues with Ready status.

Discussion:

The specification of do_allocate() (23.12.2.2 [mem.res.private] p2+p3) says:

Returns: A derived class shall implement this function to return a pointer to allocated storage (3.7.4.2) with a size of at least bytes. The returned storage is aligned to the specified alignment, if such alignment is supported (3.11); otherwise it is aligned to max_align.

Throws: A derived class implementation shall throw an appropriate exception if it is unable to allocate memory with the requested size and alignment.

It is unclear whether a request for an unsupported alignment (e.g. larger than max_align) yields an exception or the returned storage is silently aligned to max_align.

This is editorial issue #966.

[2017-01-27 Telecon]

Priority 3; Marshall to ping Pablo for intent and provide wording.

[2017-02-12 Pablo responds and provides wording]

The original intent was:

However, the description of do_allocate might have gone stale as the aligned-allocation proposal made its way into the standard.

The understanding I take from the definition of extended alignment in (the current text of) 3.11/3 [basic.align] and "assembling an argument list" in 5.3.4/14 [expr.new] is that it is intended that, when allocating space for an object with extended alignment in a well-formed program, the alignment will be honored and will not be truncated to max_align. I think this is a change from earlier drafts of the extended-alignment proposal, where silent truncation to max_align was permitted (I could be wrong). Anyway, it seems wrong to ever ignore the alignment parameter in do_allocate().

[2017-11 Albuquerque Wednesday issue processing]

Move to Ready.

Proposed resolution:

Change the specification of do_allocate() (23.12.2.2 [mem.res.private] p2+p3) as follows:

Returns: A derived class shall implement this function to return a pointer to allocated storage (3.7.4.2) with a size of at least bytes, aligned to the specified alignment. The returned storage is aligned to the specified alignment, if such alignment is supported; otherwise it is aligned to max_align.

Throws: A derived class implementation shall throw an appropriate exception if it is unable to allocate memory with the requested size and alignment.


2969(i). polymorphic_allocator::construct() shouldn't pass resource()

Section: 23.12.3.2 [mem.poly.allocator.mem] Status: Ready Submitter: Pablo Halpern Opened: 2017-05-30 Last modified: 2017-11-12

Priority: 2

View other active issues in [mem.poly.allocator.mem].

View all other issues in [mem.poly.allocator.mem].

View all issues with Ready status.

Discussion:

Section 23.12.3.2 [mem.poly.allocator.mem] defines the effect of polymorphic_allocator<T>::construct as:

Effects: Construct a T object in the storage whose address is represented by p by uses-allocator construction with allocator resource() and constructor arguments std::forward<Args>(args)....

The use of resource() is a hold-over from the LFTS, which contains a modified definition of uses-allocator construction. This revised definition was not carried over into the C++17 WP when allocator_resource and polymorphic_allocator were moved over.

Previous resolution [SUPERSEDED]:

This wording is relative to N4659.

  1. Edit 23.12.3.2 [mem.poly.allocator.mem] as indicated:

    template <class T, class... Args>
      void construct(T* p, Args&&... args);
    

    -5- Requires: Uses-allocator construction of T with allocator resource()*this (see 23.10.8.2 [allocator.uses.construction]) and constructor arguments std::forward<Args>(args)... is well-formed. [Note: Uses-allocator construction is always well formed for types that do not use allocators. — end note]

    -6- Effects: Construct a T object in the storage whose address is represented by p by uses-allocator construction with allocator resource()*this and constructor arguments std::forward<Args>(args)....

    -7- Throws: Nothing unless the constructor for T throws.

    template <class T1, class T2, class... Args1, class... Args2>
      void construct(pair<T1,T2>* p, piecewise_construct_t,
                     tuple<Args1...> x, tuple<Args2...> y);
    

    -8- [Note: This method and the construct methods that follow are overloads for piecewise construction of pairs (23.4.2 [pairs.pair]). — end note]

    -9- Effects: Let xprime be a tuple constructed from x according to the appropriate rule from the following list. [Note: The following description can be summarized as constructing a pair<T1, T2> object in the storage whose address is represented by p, as if by separate uses-allocator construction with allocator resource()*this (23.10.8.2 [allocator.uses.construction]) of p->first using the elements of x and p->second using the elements of y. — end note]

    […]

[2017-06-12, Pablo comments]

The current description is correct and does not depend on changes to uses-allocator construction. It relies on the fact that memory_resource* is convertible to polymorphic_allocator.

[2017-06-13, Tim Song reopens]

While it is true that memory_resource* is convertible to polymorphic_allocator, uses-allocator construction still requires allocators, and a memory_resource* isn't an allocator.

To take a concrete example from the current WP, a pmr::vector<std::promise<int>>, as specified, will be attempting to uses-allocator construct a promise<int> with a memory_resource*, but std::promise's allocator-taking constructor expects something that satisfies the allocator requirements, rather than a memory_resource*.

[2017-06-13, Daniel and Tim restore and improve the previously proposed wording]

[2017-07 Toronto Monday issue prioritization]

Priority 2; Dietmar to check the P/R before Albuquerque.

[2017-11 Albuquerque Wednesday issue processing]

Move to Ready.

Proposed resolution:

This wording is relative to N4659.

  1. Edit 23.12.3.2 [mem.poly.allocator.mem] as indicated:

    template <class T, class... Args>
      void construct(T* p, Args&&... args);
    

    -5- Requires: Uses-allocator construction of T with allocator resource()*this (see 23.10.8.2 [allocator.uses.construction]) and constructor arguments std::forward<Args>(args)... is well-formed. [Note: Uses-allocator construction is always well formed for types that do not use allocators. — end note]

    -6- Effects: Construct a T object in the storage whose address is represented by p by uses-allocator construction with allocator resource()*this and constructor arguments std::forward<Args>(args)....

    -7- Throws: Nothing unless the constructor for T throws.

    template <class T1, class T2, class... Args1, class... Args2>
      void construct(pair<T1,T2>* p, piecewise_construct_t,
                     tuple<Args1...> x, tuple<Args2...> y);
    

    -8- [Note: This method and the construct methods that follow are overloads for piecewise construction of pairs (23.4.2 [pairs.pair]). — end note]

    -9- Effects: Let xprime be a tuple constructed from x according to the appropriate rule from the following list. [Note: The following description can be summarized as constructing a pair<T1, T2> object in the storage whose address is represented by p, as if by separate uses-allocator construction with allocator resource()*this (23.10.8.2 [allocator.uses.construction]) of p->first using the elements of x and p->second using the elements of y. — end note]

    1. (9.1) — If uses_allocator_v<T1,memory_resource*polymorphic_allocator> is false and is_constructible_v<T1,Args1...> is true, then xprime is x.

    2. (9.2) — Otherwise, if uses_allocator_v<T1,memory_resource*polymorphic_allocator> is true and is_constructible_v<T1,allocator_arg_t,memory_resource*polymorphic_allocator,Args1...> is true, then xprime is tuple_cat(make_tuple(allocator_arg, resource()*this), std::move(x)).

    3. (9.3) — Otherwise, if uses_allocator_v<T1,memory_resource*polymorphic_allocator> is true and is_constructible_v<T1,Args1...,memory_resource*polymorphic_allocator> is true, then xprime is tuple_cat(std::move(x), make_tuple(resource()*this)).

    4. (9.4) — Otherwise the program is ill formed.

    Let yprime be a tuple constructed from y according to the appropriate rule from the following list:

    1. (9.5) — If uses_allocator_v<T2,memory_resource*polymorphic_allocator> is false and is_constructible_v<T2,Args2...> is true, then yprime is y.

    2. (9.6) — Otherwise, if uses_allocator_v<T2,memory_resource*polymorphic_allocator> is true and is_constructible_v<T2,allocator_arg_t,memory_resource*polymorphic_allocator,Args2...> is true, then yprime is tuple_cat(make_tuple(allocator_arg, resource()*this), std::move(y)).

    3. (9.7) — Otherwise, if uses_allocator_v<T2,memory_resource*polymorphic_allocator> is true and is_constructible_v<T2,Args2...,memory_resource*polymorphic_allocator> is true, then yprime is tuple_cat(std::move(y), make_tuple(resource()*this)).

    4. (9.8) — Otherwise the program is ill formed.


2975(i). Missing case for pair construction in scoped and polymorphic allocators

Section: 23.12.3.2 [mem.poly.allocator.mem], 23.13.4 [allocator.adaptor.members] Status: Ready Submitter: Casey Carter Opened: 2017-06-13 Last modified: 2017-11-12

Priority: 3

View other active issues in [mem.poly.allocator.mem].

View all other issues in [mem.poly.allocator.mem].

View all issues with Ready status.

Discussion:

scoped_allocator_adaptor ([allocator.adaptor.syn]) and polymorphic_allocator ([mem.poly.allocator.class]) have identical families of members named construct:

template <class T, class... Args>
  void construct(T* p, Args&&... args);

template <class T1, class T2, class... Args1, class... Args2>
  void construct(pair<T1,T2>* p, piecewise_construct_t,
                 tuple<Args1...> x, tuple<Args2...> y);
template <class T1, class T2>
  void construct(pair<T1,T2>* p);
template <class T1, class T2, class U, class V>
  void construct(pair<T1,T2>* p, U&& x, V&& y);
template <class T1, class T2, class U, class V>
  void construct(pair<T1,T2>* p, const pair<U, V>& pr);
template <class T1, class T2, class U, class V>
  void construct(pair<T1,T2>* p, pair<U, V>&& pr);

Both allocators perform uses_allocator construction, and therefore need special handling for pair constructions since pair doesn't specialize uses_allocator (tuple gets all of that magic and pair is left out in the cold). Presumably, the intent is that the construct overloads whose first argument is a pointer to pair capture all pair constructions. This is not the case: invoking construct with a pair pointer and a non-constant lvalue pair resolves to the first overload when it is viable: it's a better match than the pair-pointer-and-const-lvalue-pair overload. The first overload notably does not properly perform piecewise uses_allocator construction for pairs as intended.

[2017-07 Toronto Monday issue prioritization]

Priority 2; Marshall to work with Casey to reduce the negations in the wording.

Previous resolution [SUPERSEDED]:

  1. Modify 23.12.3.2 [mem.poly.allocator.mem] as indicated:

    template <class T, class... Args>
      void construct(T* p, Args&&... args);
    

    -5- Requires: Uses-allocator construction of T with allocator resource() (see 23.10.8.2 [allocator.uses.construction]) and constructor arguments std::forward<Args>(args)... is well-formed. [Note: Uses-allocator construction is always well formed for types that do not use allocators. — end note]

    -6- Effects: Construct a T object in the storage whose address is represented by p by uses-allocator construction with allocator resource() and constructor arguments std::forward<Args>(args)....

    -7- Throws: Nothing unless the constructor for T throws.

    -?- Remarks: This function shall not participate in overload resolution unless T is not a specialization of pair.

  2. Modify 23.13.4 [allocator.adaptor.members] as indicated:

    template <class T, class... Args>
      void construct(T* p, Args&&... args);
    

    -9- Effects: […]

    -?- Remarks: This function shall not participate in overload resolution unless T is not a specialization of pair.

[2017-11-02 Marshall and Casey provide updated wording]

[2017-11 Albuquerque Wednesday issue processing]

Move to Ready.

Proposed resolution:

This wording is relative to N4659.

  1. Modify 23.12.3.2 [mem.poly.allocator.mem] as indicated:

    template <class T, class... Args>
      void construct(T* p, Args&&... args);
    

    -5- Requires: Uses-allocator construction of T with allocator resource() (see 23.10.8.2 [allocator.uses.construction]) and constructor arguments std::forward<Args>(args)... is well-formed. [Note: Uses-allocator construction is always well formed for types that do not use allocators. — end note]

    -6- Effects: Construct a T object in the storage whose address is represented by p by uses-allocator construction with allocator resource() and constructor arguments std::forward<Args>(args)....

    -7- Throws: Nothing unless the constructor for T throws.

    -?- Remarks: This function shall not participate in overload resolution if T is a specialization of pair.

  2. Modify 23.13.4 [allocator.adaptor.members] as indicated:

    template <class T, class... Args>
      void construct(T* p, Args&&... args);
    

    -9- Effects: […]

    -?- Remarks: This function shall not participate in overload resolution if T is a specialization of pair.


3000(i). monotonic_memory_resource::do_is_equal uses dynamic_cast unnecessarily

Section: 23.12.6.2 [mem.res.monotonic.buffer.mem] Status: Tentatively Ready Submitter: Pablo Halpern Opened: 2017-07-14 Last modified: 2017-11-12

Priority: 0

View all issues with Tentatively Ready status.

Discussion:

Section [mem.res.monotonic.buffer.mem], paragraph 11 says

bool do_is_equal(const memory_resource& other) const noexcept override;

Returns: this == dynamic_cast<const monotonic_buffer_resource*>(&other).

The dynamic_cast adds nothing of value. It is an incorrect cut-and-paste from an example do_is_equal for a more complex resource.

[2017-07-16, Tim Song comments]

The pool resource classes appear to also have this issue.

[2017-09-18, Casey Carter expands PR to cover the pool resources.]

Previous resolution: [SUPERSEDED]
  1. Edit 23.12.6.2 [mem.res.monotonic.buffer.mem] as indicated:

    bool do_is_equal(const memory_resource& other) const noexcept override;
    

    Returns: this == dynamic_cast<const monotonic_buffer_resource*>(&other).

[ 2017-11-01 Moved to Tentatively Ready after 7 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This resolution is relative to N4687.

  1. Edit 23.12.5.4 [mem.res.pool.mem] as indicated:

    bool synchronized_pool_resource::do_is_equal(const memory_resource& other) const noexcept override;
        const memory_resource& other) const noexcept override;
    

    Returns: this == dynamic_cast<const synchronized_pool_resource*>(&other).

  2. Strike 23.12.5.4 [mem.res.pool.mem] paragraph 10, and the immediately preceding declaration of unsynchronized_pool_resource::do_is_equal.

  3. Edit 23.12.6.2 [mem.res.monotonic.buffer.mem] as indicated:

    bool do_is_equal(const memory_resource& other) const noexcept override;
    

    Returns: this == dynamic_cast<const monotonic_buffer_resource*>(&other).


3002(i). [networking.ts] basic_socket_acceptor::is_open() isn't noexcept

Section: 99 [networking.ts::socket.acceptor.ops] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2017-07-14 Last modified: 2017-11-12

Priority: 0

View all issues with Tentatively Ready status.

Discussion:

Addresses: networking.ts

basic_socket::is_open() is noexcept, but the corresponding function on basic_socket_acceptor is not. This is a simple observer with a wide contract and cannot fail.

[ 2017-11-01 Moved to Tentatively Ready after 7 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This resolution is relative to N4656.

  1. Edit [networking.ts::socket.acceptor], class template basic_socket_acceptor synopsis, as indicated:

    template<class AcceptableProtocol> 
    class basic_socket_acceptor {
    public:
      […]
      bool is_open() const noexcept;
      […]
    };
    
  2. Edit 99 [networking.ts::socket.acceptor.ops] as indicated:

    bool is_open() const noexcept;
    

    -10- Returns: A bool indicating whether this acceptor was opened by a previous call to open or assign.


3004(i). §[string.capacity] and §[vector.capacity] should specify time complexity for capacity()

Section: 24.3.2.4 [string.capacity], 26.3.11.3 [vector.capacity] Status: Tentatively Ready Submitter: Andy Giese Opened: 2017-07-24 Last modified: 2017-11-12

Priority: 0

View other active issues in [string.capacity].

View all other issues in [string.capacity].

View all issues with Tentatively Ready status.

Discussion:

basic_string and vector both have a capacity function that returns the size of the internally allocated buffer. This function does not specify a time complexity nor does it have an implied time complexity. However, given the complexities for data() to be 𝒪(1) and size() to be 𝒪(1), we can imagine that it's reasonable to also require capacity() to be 𝒪(1), since the implementation will most likely be caching the size of the allocated buffer so as to check for the need to reallocate on insertion.

[ 2017-11-01 Moved to Tentatively Ready after 10 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This resolution is relative to N4659.

  1. Edit 24.3.2.4 [string.capacity] as indicated:

    size_type capacity() const noexcept;
    

    -9- Returns: The size of the allocated storage in the string.

    -?- Complexity: Constant time.

  2. Edit 26.3.11.3 [vector.capacity] as indicated:

    size_type capacity() const noexcept;
    

    -1- Returns: The total number of elements that the vector can hold without requiring reallocation.

    -?- Complexity: Constant time.


3005(i). Destruction order of arrays by make_shared/allocate_shared only recommended?

Section: 23.11.3.6 [util.smartptr.shared.create] Status: Tentatively Ready Submitter: Richard Smith Opened: 2017-08-01 Last modified: 2017-11-12

Priority: 0

View other active issues in [util.smartptr.shared.create].

View all other issues in [util.smartptr.shared.create].

View all issues with Tentatively Ready status.

Discussion:

In [util.smartptr.shared.create]/7.9 we find this:

"When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements should be destroyed in the reverse order of their construction."

Why is this only a "should be" and not a "shall be" (or, following usual conventions for how we write requirements on the implementation, "are")? Is there some problem that means we can't require an implementation to destroy in reverse construction order in all cases?

Previous resolution: [SUPERSEDED]

This resolution is relative to N4687.

  1. Edit 23.11.3.6 [util.smartptr.shared.create] as indicated:

    template<class T, ...>
    shared_ptr<T> make_shared(args);
    template<class T, class A, ...>
    shared_ptr<T> allocate_shared(const A& a, args);
    

    […]

    -7- Remarks:

    1. […]

    2. (7.9) — When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements areshould be destroyed in decreasing index orderthe reverse order of their construction.

[2017-11-01, Alisdair comments and suggests wording improvement]

I dislike the change of how we specify the order of destruction, which is clear (but non-normative) from the preceding paragraph making the order of construction explicit. We are replacing that with a different specification, so now I need to match up ""decreasing index order" to "ascending order of address" to infer the behavior that is worded more directly today.

P0 to change "should be" to "are" though.

Previous resolution: [SUPERSEDED]

This resolution is relative to N4700.

  1. Edit 23.11.3.6 [util.smartptr.shared.create] as indicated:

    template<class T, ...>
    shared_ptr<T> make_shared(args);
    template<class T, class A, ...>
    shared_ptr<T> allocate_shared(const A& a, args);
    

    […]

    -7- Remarks:

    1. […]

    2. (7.9) — When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements areshould be destroyed in the reverse order of their construction.

[2017-11-01]

The rationale for the "decreasing index order" change in the original P/R suggested by Richard Smith had been presented as that user code may destroy one or more array elements and construct new ones in their place. In those cases shared_ptr has no way of knowing the construction order, so it cannot ensure that the destruction order reverses it.
Richard: Perhaps something like:

the initialized elements are should be destroyed in the reverse order of their original construction.

would work?

[ 2017-11-02 Moved to Tentatively Ready after 10 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This resolution is relative to N4700.

  1. Edit 23.11.3.6 [util.smartptr.shared.create] as indicated:

    template<class T, ...>
    shared_ptr<T> make_shared(args);
    template<class T, class A, ...>
    shared_ptr<T> allocate_shared(const A& a, args);
    

    […]

    -7- Remarks:

    1. […]

    2. (7.9) — When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements areshould be destroyed in the reverse order of their original construction.


3007(i). allocate_shared should rebind allocator to cv-unqualified value_type for construction

Section: 23.11.3.6 [util.smartptr.shared.create] Status: Tentatively Ready Submitter: Glen Joseph Fernandes Opened: 2017-08-06 Last modified: 2017-11-12

Priority: 0

View other active issues in [util.smartptr.shared.create].

View all other issues in [util.smartptr.shared.create].

View all issues with Tentatively Ready status.

Discussion:

The remarks for the allocate_shared family of functions specify that when constructing a (sub)object of type U, it uses a rebound copy of the allocator a passed to allocate_shared such that its value_type is U. However U can be a const or volatile qualified type, and [allocator.requirements] specify that the value_type must be cv-unqualified.

[ 2017-11-01 Moved to Tentatively Ready after 6 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This resolution is relative to N4687.

  1. Edit 23.11.3.6 [util.smartptr.shared.create] as indicated:

    template<class T, ...>
    shared_ptr<T> make_shared(args);
    template<class T, class A, ...>
    shared_ptr<T> allocate_shared(const A& a, args);
    

    […]

    -7- Remarks:

    1. […]

    2. (7.5) — When a (sub)object of a non-array type U is specified to have an initial value of v, or U(l...), where l... is a list of constructor arguments, allocate_shared shall initialize this (sub)object via the expression

      1. (7.5.1) — allocator_traits<A2>::construct(a2, pv, v) or

      2. (7.5.2) — allocator_traits<A2>::construct(a2, pv, l...)

      respectively, where pv points to storage suitable to hold an object of type U and a2 of type A2 is a rebound copy of the allocator a passed to allocate_shared such that its value_type is remove_cv_t<U>.

    3. (7.6) — When a (sub)object of non-array type U is specified to have a default initial value, make_shared shall initialize this (sub)object via the expression ::new(pv) U(), where pv has type void* and points to storage suitable to hold an object of type U.

    4. (7.7) — When a (sub)object of non-array type U is specified to have a default initial value, allocate_shared shall initialize this (sub)object via the expression allocator_traits<A2>::construct(a2, pv), where pv points to storage suitable to hold an object of type U and a2 of type A2 is a rebound copy of the allocator a passed to allocate_shared such that its value_type is remove_cv_t<U>.

    5. […]


3009(i). Including <string_view> doesn't provide std::size/empty/data

Section: 27.8 [iterator.container] Status: Tentatively Ready Submitter: Tim Song Opened: 2017-08-11 Last modified: 2017-11-12

Priority: 0

View all issues with Tentatively Ready status.

Discussion:

basic_string_view has size(), empty(), and data() members, but including <string_view> isn't guaranteed to give you access to the corresponding free function templates. This seems surprising.

[ 2017-11-01 Moved to Tentatively Ready after 7 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This resolution is relative to N4687.

  1. Edit 27.8 [iterator.container] as indicated:

    -1- In addition to being available via inclusion of the <iterator> header, the function templates in 27.8 are available when any of the following headers are included: <array>, <deque>, <forward_list>, <list>, <map>, <regex>, <set>, <string>, <string_view>, <unordered_map>, <unordered_set>, and <vector>.


3010(i). [networking.ts] uses_executor says "if a type T::executor_type exists"

Section: 99 [networking.ts::async.uses.excecutor.trait] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2017-08-17 Last modified: 2017-11-12

Priority: 0

View all issues with Tentatively Ready status.

Discussion:

Addresses: networking.ts

[async.uses.executor.trait] p1 says "if a type T::executor_type exists" but we don't want it to be required to detect private or ambiguous types.

[ 2017-11-01 Moved to Tentatively Ready after 7 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This resolution is relative to N4656.

  1. Edit [networking.ts::async.uses.executor.trait] as indicated:

    -1- Remark: Detects whether T has a nested executor_type that is convertible from Executor. Meets the BinaryTypeTrait requirements (C++Std [meta.rqmts]). The implementation provides a definition that is derived from true_type if a typethe qualified-id T::executor_type existsis valid and denotes a type and is_convertible<Executor, T::executor_type>::value != false, otherwise it is derived from false_type. A program may specialize this template […].


3013(i). (recursive_)directory_iterator construction and traversal should not be noexcept

Section: 30.11.12.1 [fs.dir.itr.members], 30.11.13.1 [fs.rec.dir.itr.members], 30.11.14.3 [fs.op.copy], 30.11.14.19 [fs.op.is_empty] Status: Tentatively Ready Submitter: Tim Song Opened: 2017-08-23 Last modified: 2017-11-12

Priority: 0

View other active issues in [fs.dir.itr.members].

View all other issues in [fs.dir.itr.members].

View all issues with Tentatively Ready status.

Discussion:

Constructing a (recursive_)directory_iterator from a path requires, at a minimum, initializing its underlying directory_entry object with the path formed from the supplied path and the name of the first entry, which requires a potentially throwing memory allocation; every implementation I've looked at also allocates memory to store additional data as well.

Similarly, increment() needs to update the path stored in directory_entry object to refer to the name of the next entry, which may require a memory allocation. While it might conceivably be possible to postpone the update in this case until the iterator is dereferenced (the dereference operation is not noexcept due to its narrow contract), it seems highly unlikely that such an implementation is intended (not to mention that it would require additional synchronization as the dereference operations are const).

This further calls into question whether the error_code overloads of copy and is_empty, whose specification uses directory_iterator, should be noexcept. There might be a case for keeping the noexcept for is_empty, although that would require changes in all implementations I checked (libstdc++, libc++, and Boost). copy appears to be relentlessly hostile to noexcept, since its specification forms a path via operator/ in two places (bullets 4.7.4 and 4.8.2) in addition to the directory_iterator usage. The proposed resolution below removes both.

[ 2017-11-03 Moved to Tentatively Ready after 7 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This wording is relative to N4700.

  1. Edit 30.11.12 [fs.class.directory_iterator], class directory_iterator synopsis, as indicated:

    […]
    explicit directory_iterator(const path& p);
    directory_iterator(const path& p, directory_options options);
    directory_iterator(const path& p, error_code& ec) noexcept;
    directory_iterator(const path& p, directory_options options,
                       error_code& ec) noexcept;
    […]
    
    directory_iterator& operator++();
    directory_iterator& increment(error_code& ec) noexcept;
    
  2. Edit 30.11.12.1 [fs.dir.itr.members] before p2 as indicated:

    explicit directory_iterator(const path& p);
    directory_iterator(const path& p, directory_options options);
    directory_iterator(const path& p, error_code& ec) noexcept;
    directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
    

    -2- Effects: […]

    -3- Throws: As specified in 30.11.6 [fs.err.report].

    -4- [Note: […] — end note]

  3. Edit 30.11.12.1 [fs.dir.itr.members] before p10 as indicated:

    directory_iterator& operator++();
    directory_iterator& increment(error_code& ec) noexcept;
    

    -10- Effects: As specified for the prefix increment operation of Input iterators (24.2.3).

    -11- Returns: *this.

    -12- Throws: As specified in 30.11.6 [fs.err.report].

  4. Edit 30.11.13 [fs.class.rec.dir.itr], class recursive_directory_iterator synopsis, as indicated:

    […]
    explicit recursive_directory_iterator(const path& p);
    recursive_directory_iterator(const path& p, directory_options options);
    recursive_directory_iterator(const path& p, directory_options options,
                                 error_code& ec) noexcept;
    recursive_directory_iterator(const path& p, error_code& ec) noexcept;
    […]
    
    recursive_directory_iterator& operator++();
    recursive_directory_iterator& increment(error_code& ec) noexcept;
    
  5. Edit 30.11.13.1 [fs.rec.dir.itr.members] before p2 as indicated:

    explicit recursive_directory_iterator(const path& p);
    recursive_directory_iterator(const path& p, directory_options options);
    recursive_directory_iterator(const path& p, directory_options options, error_code& ec) noexcept;
    recursive_directory_iterator(const path& p, error_code& ec) noexcept;
    

    -2- Effects: […]

    -3- Postconditions: […]

    -4- Throws: As specified in 30.11.6 [fs.err.report].

    -5- [Note: […] — end note]

    -6- [Note: […] — end note]

  6. Edit 30.11.13.1 [fs.rec.dir.itr.members] before p23 as indicated:

    recursive_directory_iterator& operator++();
    recursive_directory_iterator& increment(error_code& ec) noexcept;
    

    -23- Effects: As specified for the prefix increment operation of Input iterators (24.2.3), except that: […]

    -24- Returns: *this.

    -25- Throws: As specified 30.11.6 [fs.err.report].

  7. Edit 30.11.5 [fs.filesystem.syn], header <filesystem> synopsis, as indicated:

    namespace std::filesystem {
    
      […]
    
      void copy(const path& from, const path& to);
      void copy(const path& from, const path& to, error_code& ec) noexcept;
      void copy(const path& from, const path& to, copy_options options);
      void copy(const path& from, const path& to, copy_options options,
                error_code& ec) noexcept;
    
      […]
    
      bool is_empty(const path& p);
      bool is_empty(const path& p, error_code& ec) noexcept;
      
      […]
    
    }
    
  8. Edit 30.11.14.3 [fs.op.copy] as indicated:

    void copy(const path& from, const path& to, error_code& ec) noexcept;
    

    -2- Effects: Equivalent to copy(from, to, copy_options::none, ec).

    void copy(const path& from, const path& to, copy_options options);
    void copy(const path& from, const path& to, copy_options options,
              error_code& ec) noexcept;
    

    -3- Requires: […]

    -4- Effects: […]

    -5- Throws: […]

    -6- Remarks: […]

    -7- [Example: […] — end example]

  9. Edit 30.11.14.19 [fs.op.is_empty] as indicated:

    bool is_empty(const path& p);
    bool is_empty(const path& p, error_code& ec) noexcept;
    

    -1- Effects: […]

    -2- Throws: […]


3014(i). More noexcept issues with filesystem operations

Section: 30.11.14.4 [fs.op.copy_file], 30.11.14.6 [fs.op.create_directories], 30.11.14.31 [fs.op.remove_all] Status: Ready Submitter: Tim Song Opened: 2017-08-23 Last modified: 2017-11-12

Priority: Not Prioritized

View other active issues in [fs.op.copy_file].

View all other issues in [fs.op.copy_file].

View all issues with Ready status.

Discussion:

create_directories may need to create temporary paths, and remove_all may need to create temporary paths and/or directory_iterators. These operations may require a potentially throwing memory allocation.

Implementations of copy_file may wish to dynamically allocate the buffer used for copying when the underlying OS doesn't supply a copy API directly. This can happen indirectly, e.g., by using <fstream> facilities to perform the copying without supplying a custom buffer. Unless LWG wishes to prohibit using a dynamically allocated buffer in this manner, the noexcept should be removed.

[2017-11 Albuquerque Wednesday night issues processing]

Moved to Ready

Proposed resolution:

This wording is relative to N4687.

  1. Edit 30.11.5 [fs.filesystem.syn], header <filesystem> synopsis, as indicated:

    namespace std::filesystem {
    
      […]
      
      bool copy_file(const path& from, const path& to);
      bool copy_file(const path& from, const path& to, error_code& ec) noexcept;
      bool copy_file(const path& from, const path& to, copy_options option);
      bool copy_file(const path& from, const path& to, copy_options option,
                     error_code& ec) noexcept;
    
      […]
    
      bool create_directories(const path& p);
      bool create_directories(const path& p, error_code& ec) noexcept;
      
      […]
      
      uintmax_t remove_all(const path& p);
      uintmax_t remove_all(const path& p, error_code& ec) noexcept;
      
      […]
    }
    
  2. Edit 30.11.14.4 [fs.op.copy_file] as indicated:

    bool copy_file(const path& from, const path& to);
    bool copy_file(const path& from, const path& to, error_code& ec) noexcept;
    

    -1- Returns: […]

    -2- Throws: […]

    bool copy_file(const path& from, const path& to, copy_options options);
    bool copy_file(const path& from, const path& to, copy_options options,
                   error_code& ec) noexcept;
    

    -3- Requires: […]

    -4- Effects: […]

    -5- Returns: […]

    -6- Throws: […]

    -7- Complexity: […]

  3. Edit 30.11.14.6 [fs.op.create_directories] as indicated:

    bool create_directories(const path& p);
    bool create_directories(const path& p, error_code& ec) noexcept;
    

    -1- Effects: […]

    -2- Postconditions: […]

    -3- Returns: […]

    -4- Throws: […]

    -5- Complexity: […]

  4. Edit 30.11.14.31 [fs.op.remove_all] as indicated:

    uintmax_t remove_all(const path& p);
    uintmax_t remove_all(const path& p, error_code& ec) noexcept;
    

    -1- Effects: […]

    -2- Postconditions: […]

    -3- Returns: […]

    -4- Throws: […]


3017(i). list splice functions should use addressof

Section: 26.3.9.6 [forwardlist.ops], 26.3.10.5 [list.ops] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2017-09-05 Last modified: 2017-11-12

Priority: 0

View all other issues in [forwardlist.ops].

View all issues with Tentatively Ready status.

Discussion:

26.3.9.6 [forwardlist.ops] p1 and 26.3.10.5 [list.ops] p3 say &x != this, but should use addressof. We really need front matter saying that when the library says &x it means std::addressof(x).

[ 2017-11-03 Moved to Tentatively Ready after 9 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This wording is relative to N4687.

  1. Edit 26.3.9.6 [forwardlist.ops] p1 as indicated:

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

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

  2. Edit 26.3.10.5 [list.ops] p3 as indicated:

    void splice(const_iterator position, list& x);
    void splice(const_iterator position, list&& x);
    

    -3- Requires: &addressof(x) != this.

  3. Edit 26.3.10.5 [list.ops] p3 as indicated:

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

    -22- Requires: comp shall define a strict weak ordering (28.7 [alg.sorting]), and both the list and the argument list shall be sorted according to this ordering.

    -23- Effects: If (&addressof(x) == this) does nothing; otherwise, merges the two sorted ranges [begin(), end()) and [x.begin(), x.end()). The result is a range in which the elements will be sorted in non-decreasing order according to the ordering defined by comp; that is, for every iterator i, in the range other than the first, the condition comp(*i, *(i - 1)) will be false. Pointers and references to the moved elements of x now refer to those same elements but as members of *this. Iterators referring to the moved elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.

    -24- Remarks: Stable (20.5.5.7 [algorithm.stable]). If (&addressof(x) != this) the range [x.begin(), x.end()) is empty after the merge. No elements are copied by this operation. The behavior is undefined if get_allocator() != x.get_allocator().

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


3020(i). [networking.ts] Remove spurious nested value_type buffer sequence requirement

Section: 99 [networking.ts::buffer.reqmts] Status: Tentatively Ready Submitter: Vinnie Falco Opened: 2017-09-20 Last modified: 2017-11-12

Priority: Not Prioritized

View all issues with Tentatively Ready status.

Discussion:

Addresses: networking.ts

The post-condition requirements for ConstBufferSequence and MutableBufferSequence refer to X::value_type, but no such nested type is required. The lambda expression passed to equal can use auto const& parameter types instead.

Previous resolution: [SUPERSEDED]

This wording is relative to N4588.

  1. Modify 99 [networking.ts::buffer.reqmts.mutablebuffersequence] Table 12 "MutableBufferSequence requirements" as indicated:

    Table 12 — MutableBufferSequence requirements
    expression return type assertion/note pre/post-condition
    […]
    X u(x); post:
    equal(
      net::buffer_sequence_begin(x),
      net::buffer_sequence_end(x),
      net::buffer_sequence_begin(u),
      net::buffer_sequence_end(u),
      [](const typename X::value_typeauto& v1,
         const typename X::value_typeauto& v2)
        {
          mutable_buffer b1(v1);
          mutable_buffer b2(v2);
          return b1.data() == b2.data()
              && b1.size() == b2.size();
        })
    
  2. Modify 99 [networking.ts::buffer.reqmts.constbuffersequence] Table 13 "ConstBufferSequence requirements" as indicated:

    Table 13 — ConstBufferSequence requirements
    expression return type assertion/note pre/post-condition
    […]
    X u(x); post:
    equal(
      net::buffer_sequence_begin(x),
      net::buffer_sequence_end(x),
      net::buffer_sequence_begin(u),
      net::buffer_sequence_end(u),
      [](const typename X::value_typeauto& v1,
         const typename X::value_typeauto& v2)
        {
          const_buffer b1(v1);
          const_buffer b2(v2);
          return b1.data() == b2.data()
              && b1.size() == b2.size();
        })
    

[2017-10-19, Peter Dimov provides improved wording]

The alternative wording prevents the need for auto parameters because it takes advantage of the "convertible to" requirement.

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

Proposed resolution:

This wording is relative to N4588.

  1. Modify 99 [networking.ts::buffer.reqmts.mutablebuffersequence] Table 12 "MutableBufferSequence requirements" as indicated:

    Table 12 — MutableBufferSequence requirements
    expression return type assertion/note pre/post-condition
    […]
    X u(x); post:
    equal(
      net::buffer_sequence_begin(x),
      net::buffer_sequence_end(x),
      net::buffer_sequence_begin(u),
      net::buffer_sequence_end(u),
      [](const typename X::value_type& v1mutable_buffer& b1,
         const typename X::value_type& v2mutable_buffer& b2)
        {
          mutable_buffer b1(v1);
          mutable_buffer b2(v2);
          return b1.data() == b2.data()
              && b1.size() == b2.size();
        })
    
  2. Modify 99 [networking.ts::buffer.reqmts.constbuffersequence] Table 13 "ConstBufferSequence requirements" as indicated:

    Table 13 — ConstBufferSequence requirements
    expression return type assertion/note pre/post-condition
    […]
    X u(x); post:
    equal(
      net::buffer_sequence_begin(x),
      net::buffer_sequence_end(x),
      net::buffer_sequence_begin(u),
      net::buffer_sequence_end(u),
      [](const typename X::value_type& v1const_buffer& b1,
         const typename X::value_type& v2const_buffer& v2)
        {
          const_buffer b1(v1);
          const_buffer b2(v2);
          return b1.data() == b2.data()
              && b1.size() == b2.size();
        })
    

3026(i). filesystem::weakly_canonical still defined in terms of canonical(p, base)

Section: 30.11.14.39 [fs.op.weakly_canonical] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2017-10-14 Last modified: 2017-10-17

Priority: Not Prioritized

View all issues with Tentatively Ready status.

Discussion:

LWG 2956 fixed canonical to no longer use a base path, but weakly_canonical should have been changed too:

Effects: Using status(p) or status(p, ec), respectively, to determine existence, return a path composed by operator/= from the result of calling canonical() without a base argument and with a […]

Since canonical doesn't accept a base argument, it doesn't make sense to talk about calling it without one.

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

Proposed resolution:

This wording is relative to N4687.

  1. Change 30.11.14.39 [fs.op.weakly_canonical] as indicated:

    path weakly_canonical(const path& p);
    path weakly_canonical(const path& p, error_code& ec);
    

    -1- Returns: […]

    -2- Effects: Using status(p) or status(p, ec), respectively, to determine existence, return a path composed by operator/= from the result of calling canonical() without a base argument and with a path argument composed of the leading elements of p that exist, if any, followed by the elements of p that do not exist, if any. For the first form, canonical() is called without an error_code argument. For the second form, canonical() is called with ec as an error_code argument, and path() is returned at the first error occurrence, if any.

    […]


3030(i). Who shall meet the requirements of try_lock?

Section: 33.4.5 [thread.lock.algorithm] Status: Ready Submitter: Jonathan Wakely Opened: 2017-11-07 Last modified: 2017-11-12

Priority: 0

View all other issues in [thread.lock.algorithm].

View all issues with Ready status.

Discussion:

33.4.5 [thread.lock.algorithm] says:

"If a call to try_lock() fails, unlock() shall be called for all prior arguments and there shall be no further calls to try_lock()."

We try to use "shall" for requirements on the user (e.g. as in the previous paragraph) which is absolutely not what is meant here.

[2017-11 Albuquerque Wednesday night issues processing]

Moved to Ready

Proposed resolution:

This wording is relative to N4700.

  1. Change 33.4.5 [thread.lock.algorithm] as indicated:

    template <class L1, class L2, class... L3> int try_lock(L1&, L2&, L3&...);
    

    -1- Requires: […]

    -2- Effects: Calls try_lock() for each argument in order beginning with the first until all arguments have been processed or a call to try_lock() fails, either by returning false or by throwing an exception. If a call to try_lock() fails, unlock() shall beis called for all prior arguments and there shall bewith no further calls to try_lock().

    […]

    template <class L1, class L2, class... L3> void lock(L1&, L2&, L3&...);
    

    -4- Requires: […]

    -5- Effects: All arguments are locked via a sequence of calls to lock(), try_lock(), or unlock() on each argument. The sequence of calls shalldoes not result in deadlock, but is otherwise unspecified. [Note: A deadlock avoidance algorithm such as try-and-back-off must be used, but the specific algorithm is not specified to avoid over-constraining implementations. — end note] If a call to lock() or try_lock() throws an exception, unlock() shall beis called for any argument that had been locked by a call to lock() or try_lock().


3034(i). P0767R1 breaks previously-standard-layout types

Section: 23.15.7.6 [meta.trans.other] Status: Tentatively Ready Submitter: Casey Carter Opened: 2017-11-12 Last modified: 2017-11-22

Priority: 0

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

View all issues with Tentatively Ready status.

Discussion:

P0767R1 "Expunge POD" changed the requirement for several library types from "POD" to "trivial." Since these types no longer provide/require the standard-layout portion of "POD," the change breaks:

It appears this breakage was not intentional and not discussed in LWG.

The fix is straight-forward: apply an additional standard-layout requirement to the affected types:

(Albeit the potential for breakage with max_align_t is admittedly small.)

[ 2017-11-14 Moved to Tentatively Ready after 8 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This wording is relative to N4700 + P0767R1.

  1. Change in 21.2.4 [support.types.layout] paragraph 5:

    The type max_align_t is a trivial standard-layout type whose alignment requirement is at least as great as that of every scalar type, and whose alignment requirement is supported in every context (6.6.5 [basic.align]).

  2. Change the table in 23.15.7.6 [meta.trans.other] as indicated:

    aligned_storage
    The member typedef type shall be a trivial standard-layout type suitable for use as uninitialized storage for any object whose size is at most Len and whose alignment is a divisor of Align.

    aligned_union
    The member typedef type shall be a trivial standard-layout type suitable for use as uninitialized storage for any object whose type is listed in Types; its size shall be at least Len.

  3. Change 24.1 [strings.general] paragraph 1 as indicated:

    This Clause describes components for manipulating sequences of any non-array trivial standard-layout (6.7 [basic.types]) type. Such types are called char-like types, and objects of char-like types are called char-like objects or simply characters.


3035(i). std::allocator's constructors should be constexpr

Section: 23.10.10 [default.allocator] Status: Tentatively Ready Submitter: Geoffrey Romer Opened: 2017-11-11 Last modified: 2017-11-28

Priority: Not Prioritized

View all other issues in [default.allocator].

View all issues with Tentatively Ready status.

Discussion:

std::allocator's constructors should be constexpr. It's expected to be an empty class as far as I know, so this should impose no implementation burden, and it would be useful to permit guaranteed static initialization of objects that need to hold a std::allocator, but don't have to actually use it until after construction.

[ 2017-11-25 Moved to Tentatively Ready after 7 positive votes for P0 on c++std-lib. ]

Proposed resolution:

This wording is relative to N4700.

  1. Change in 23.10.10 [default.allocator] as indicated:

    namespace std {
      template <class T> class allocator {
      public:
        using value_type = T;
        using propagate_on_container_move_assignment = true_type;
        using is_always_equal = true_type;
        constexpr allocator() noexcept;
        constexpr allocator(const allocator&) noexcept;
        constexpr template <class U> allocator(const allocator<U>&) noexcept;
        ~allocator();
        T* allocate(size_t n);
        void deallocate(T* p, size_t n);
      };
    }
    

3039(i). Unnecessary decay in thread and packaged_task

Section: 33.3.2.2 [thread.thread.constr], 33.6.10.1 [futures.task.members] Status: Tentatively Ready Submitter: Stephan T. Lavavej Opened: 2017-11-17 Last modified: 2017-11-28

Priority: Not Prioritized

View all other issues in [thread.thread.constr].

View all issues with Tentatively Ready status.

Discussion:

Following P0777R1 "Treating Unnecessary decay", more occurrences can be fixed. When constraints are checking for the same type as thread or a specialization of packaged_task, decaying functions to function pointers and arrays to object pointers can't affect the result.

[28-Nov-2017 Moved to Tentatively Ready after five positive votes on the ML.]

Proposed resolution:

Wording relative to N4700.

  1. Edit 33.3.2.2 [thread.thread.constr] as indicated:

    template <class F, class... Args> explicit thread(F&& f, Args&&... args);
    

    -3- Requires: […]

    -4- Remarks: This constructor shall not participate in overload resolution if decay_tremove_cvref_t<F> is the same type as std::thread.

  2. Edit 33.6.10.1 [futures.task.members] as indicated:

    template <class F>
      packaged_task(F&& f);
    

    -2- Requires: […]

    -3- Remarks: This constructor shall not participate in overload resolution if decay_tremove_cvref_t<F> is the same type as packaged_task<R(ArgTypes...)>.