Revised 2017-03-20 at 14:03:10 UTC

Tentative Issues


760. The emplace issue

Section: 23.2 [container.requirements] Status: Tentatively NAD Submitter: Paolo Carlini Opened: 2007-11-11 Last modified: 2016-10-10

Priority: 2

View all other issues in [container.requirements].

View all issues with Tentatively NAD status.

Discussion:

In an emplace member function the function parameter pack may be bound to a priori unlimited number of objects: some or all of them can be elements of the container itself. Apparently, in order to conform to the blanket statement 23.2 [container.requirements]/11, the implementation must check all of them for that possibility. A possible solution can involve extending the exception in 23.2 [container.requirements]/12 also to the emplace member. As a side note, the push_back and push_front member functions are luckily not affected by this problem, can be efficiently implemented anyway.

[ Related to 767 and to 2164 ]

[ Bellevue: ]

The proposed addition (13) is partially redundant with the existing paragraph 12. Why was the qualifier "rvalues" added to paragraph 12? Why does it not cover subelements and pointers?

Resolution: Alan Talbot to rework language, then set state to Review.

[ 2009-07 Frankfurt ]

The problem is broader than emplace. The LWG doesn't feel that it knows how to write wording that prohibits all of the problematic use cases at this time.

NAD Future.

[2015-02 Cologne]

LWG believes that 2164 addresses this issue and therefore considers 760 as NAD.

Proposed resolution:

Add after 23.2 [container.requirements]/12:

-12- Objects passed to member functions of a container as rvalue references shall not be elements of that container. No diagnostic required.

-13- Objects bound to the function parameter pack of the emplace member function shall not be elements or sub-objects of elements of the container. No diagnostic required.


2337. shared_ptr operator*() should not be noexcept

Section: 20.11.2.2.5 [util.smartptr.shared.obs] Status: Tentatively NAD Submitter: Stephan T. Lavavej Opened: 2013-10-05 Last modified: 2016-10-10

Priority: 2

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

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

View all issues with Tentatively NAD status.

Discussion:

20.11.1.2.4 [unique.ptr.single.observers]/3: "pointer operator->() const noexcept; Requires: get() != nullptr."

20.11.2.2.5 [util.smartptr.shared.obs]/2: "T& operator*() const noexcept; Requires: get() != 0."

20.11.2.2.5 [util.smartptr.shared.obs]/5: "T* operator->() const noexcept; Requires: get() != 0."

Narrow-contract functions should not be noexcept.

[2014-02-15 Issaquah]

Issue is contentious, raise to P2.

[2015-02 Cologne]

AM: This ship has sailed. JM: What's the issue? AM: operator-> has narrow contract and should never have had noexcept. DK: Not quite. We explicitly called out that for shared_ptr this is fine. You said so in your "narrow contract" paper. GR: This would be a fairly major regression in the design of {unique,shared}_ptr over raw pointers; raw pointer dereferencing is noexcept. It's not a performance regression but a usability regression. AM: Do we expect users to query noexpect on dereference expressions? Room: Yes. VV: We don't just expect it, we have seen it. JM: Yes, users may be querying something like noexcept(x->y) and expect to be checking y, but silently end up checking x->.

Close as NAD, with explanation from GR.

Previous resolution [SUPERSEDED]:

This wording is relative to N3691.

  1. In 20.11.1.2 [unique.ptr.single]/1, class template unique_ptr synopsis for single objects, change as indicated:

    pointer operator->() const noexcept;
    
  2. In 20.11.1.2.4 [unique.ptr.single.observers] change as indicated:

    pointer operator->() const noexcept;
    

    -3- Requires: get() != nullptr.

    -4- Returns: get().

    -?- Throws: Nothing.

    -5- Note: use typically requires that T be a complete type.

  3. In 20.11.2.2 [util.smartptr.shared]/1, class template shared_ptr synopsis, change as indicated:

    T& operator*() const noexcept;
    T* operator->() const noexcept;
    
  4. In 20.11.2.2.5 [util.smartptr.shared.obs] change as indicated:

    T& operator*() const noexcept;
    

    -2- Requires: get() != 0.

    -3- Returns: *get().

    -?- Throws: Nothing.

    -4- Remarks: When T is void, it is unspecified whether this member function is declared. If it is declared, it is unspecified what its return type is, except that the declaration (although not necessarily the definition) of the function shall be well formed.

    T* operator->() const noexcept;
    

    -5- Requires: get() != 0.

    -6- Returns: get().

    -?- Throws: Nothing.

[2015-03-03, Geoffrey provides rationale]

Rationale:

It is by design that these members are noexcept, and changing that now would be a substantial regression in functionality. These classes were designed to substitute for plain pointers as transparently as possible, so since those operations are effectively noexcept on plain pointers, they should be noexcept on unique_ptr and shared_ptr as well. This matters in practice because we expect these members to be used fairly often inside the noexcept operator, and such code could be broken by this change. These design considerations override our general policy against noexcept for narrow-contract functions.

It is notable that N3279, which proposed this policy, did not propose striking noexcept from these operations. It's not clear if the omission of operator* and operator-> was an oversight, or an intentional reflection of the above considerations. N3279 was based on N3248 by the same authors, which states that:

"Most applications of noexcept for unique_ptr and shared_ptr are on functions with wide contracts. However, there are preconditions on the atomic access functions, so these should lose the specification."

Proposed resolution:


2444. Inconsistent complexity for std::sort_heap

Section: 25.7.7.4 [sort.heap] Status: Tentatively Ready Submitter: François Dumont Opened: 2014-10-07 Last modified: 2017-03-20

Priority: 3

View all issues with Tentatively Ready status.

Discussion:

While creating complexity tests for the GNU libstdc++ implementation I stumbled across a surprising requirement for the std::sort_heap algorithm.

In 25.7.7.4 [sort.heap] p3 the Standard states:

Complexity: At most N log(N) comparisons (where N == last - first).

As stated on the libstdc++ mailing list by Marc Glisse sort_heap can be implemented by N calls to pop_heap. As max number of comparisons of pop_heap is 2 * log(N) then sort_heap max limit should be 2 * log(1) + 2 * log(2) + .... + 2 * log(N) that is to say 2 * log(N!). In terms of log(N) we can also consider that this limit is also cap by 2 * N * log(N) which is surely what the Standard wanted to set as a limit.

This is why I would like to propose to replace paragraph 3 by:

Complexity: At most 2N log(N) comparisons (where N == last - first).

[2015-02 Cologne]

Marshall will research the maths and report back in Lenexa.

[2015-05-06 Lenexa]

STL: I dislike exact complexity requirements, they prevent one or two extra checks in debug mode. Would it be better to say O(N log(N)) not at most?

[2017-03-04, Kona]

Move to Tentatively Ready. STL may write a paper (with Thomas & Robert) offering guidance about Big-O notation vs. exact requirements.

Proposed resolution:

This wording is relative to N3936.

  1. In 25.7.7.4 [sort.heap] p3 the Standard states:

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

    […]

    -3- Complexity: At most 2N log(N) comparisons (where N == last - first).


2597. std::log misspecified for complex numbers

Section: 26.5.8 [complex.transcendentals] Status: Tentatively Ready Submitter: Thomas Koeppe Opened: 2016-03-01 Last modified: 2017-03-20

Priority: 3

View all other issues in [complex.transcendentals].

View all issues with Tentatively Ready status.

Discussion:

The current specification of std::log is inconsistent for complex numbers, specifically, the Returns clause (26.5.8 [complex.transcendentals]). On the one hand, it states that the imaginary part of the return value lies in the closed interval [-i π, +i π]. On the other hand, it says that "the branch cuts are along the negative real axis" and "the imaginary part of log(x) is when x is a negative real number".

The inconsistency lies in the difference between the mathematical concept of a branch cut and the nature of floating point numbers in C++. The corresponding specification in the C standard makes it clearer that if x is a real number, then log(x + 0i) = +π, but log(x - 0i) = -π, i.e. they consider positive and negative zero to represent the two different limits of approaching the branch cut from opposite directions. In other words, the term "negative real number" is misleading, and in fact there are two distinct real numbers, x + 0i and x - 0i, that compare equal but whose logarithms differ by 2 π i.

The resolution should consist of two parts:

  1. Double-check that our usage and definition of "branch cut" is sufficiently unambiguous. The C standard contains a lot more wording around this that we don't have in C++.

  2. Change the Returns clause of log appropriately. For example: "When x is a negative real number, imag(log(x + 0i)) is π, and imag(log(x - 0i)) is ."

Current implementations seem to behave as described in (2). (Try-it-at-home link)

[2016-11-12, Issaquah]

Move to Open - Thomas to provide wording

[2016-11-15, Thomas comments and provides wording]

Following LWG discussion in Issaquah, I now propose to resolve this issue by removing the normative requirement on the function limits, and instead adding a note that the intention is to match the behaviour of C. This allows implementations to use the behaviour of C without having to specify what floating point numbers really are.

The change applies to both std::log and std::sqrt.

Updated try-at-home link, see here.

[2017-03-04, Kona]

Minor wording update and status to Tentatively Ready.

Previous resolution [SUPERSEDED]:

This wording is relative to N4606.

  1. Change the "returns" element for std::log (26.5.8 [complex.transcendentals] p17):

    template<class T> complex<T> log(const complex<T>& x);
    

    -16- Remarks: The branch cuts are along the negative real axis.

    -17- Returns: The complex natural (base-ℯ) logarithm of x. For all x, imag(log(x)) lies in the interval [-π, π], and when x is a negative real number, imag(log(x)) is π. [Note: The semantics of std::log are intended to be the same in C++ as they are for clog in C. — end note]

  2. Change the "returns" element for std::sqrt (26.5.8 [complex.transcendentals] p25):

    template<class T> complex<T> sqrt(const complex<T>& x);
    

    -24- Remarks: The branch cuts are along the negative real axis.

    -25- Returns: The complex square root of x, in the range of the right half-plane. If the argument is a negative real number, the value returned lies on the positive imaginary axis.[Note: The semantics of std::sqrt are intended to be the same in C++ as they are for csqrt in C. — end note]

Proposed resolution:

This wording is relative to N4606.

  1. Change the "returns" element for std::log (26.5.8 [complex.transcendentals] p17):

    template<class T> complex<T> log(const complex<T>& x);
    

    -16- Remarks: The branch cuts are along the negative real axis.

    -17- Returns: The complex natural (base-ℯ) logarithm of x. For all x, imag(log(x)) lies in the interval [-π, π], and when x is a negative real number, imag(log(x)) is π. [Note: the semantics of this function are intended to be the same in C++ as they are for clog in C. — end note]

  2. Change the "returns" element for std::sqrt (26.5.8 [complex.transcendentals] p25):

    template<class T> complex<T> sqrt(const complex<T>& x);
    

    -24- Remarks: The branch cuts are along the negative real axis.

    -25- Returns: The complex square root of x, in the range of the right half-plane. If the argument is a negative real number, the value returned lies on the positive imaginary axis.[Note: The semantics of this function are intended to be the same in C++ as they are for csqrt in C. — end note]


2692. Overspecification of lvalueness of bitmask elements

Section: 17.4.2.1.4 [bitmask.types] Status: Tentatively NAD Submitter: Hubert Tong Opened: 2016-04-14 Last modified: 2017-02-06

Priority: 3

View all other issues in [bitmask.types].

View all issues with Tentatively NAD status.

Discussion:

The usual pattern now used for identifying where bitmask elements are declared, namely, as variables, preclude declaring them as enumerators.

Compare: ctype_base::space in C++03 subclause 22.2.1 [lib.category.ctype] versus the same in N4582 subclause 22.4.1 [category.ctype].

It is unclear whether this is intentional. Further it is unclear if odr-use of bitmask elements is intended to be allowed.

[2016-05 Issues Telecon]

Jonathan believes that this was intentional, and was done by N3110. Jonathan will provide more precise references.

Proposed resolution:


2717. scoped_allocator_adaptor uses forward to do move's job

Section: 20.13.4 [allocator.adaptor.members] Status: Tentatively NAD Submitter: Billy Robert O'Neal III Opened: 2016-05-24 Last modified: 2016-10-10

Priority: Not Prioritized

View other active issues in [allocator.adaptor.members].

View all other issues in [allocator.adaptor.members].

View all issues with Tentatively NAD status.

Discussion:

scoped_allocator_adaptor is specified to use forward when what it is really doing is moving elements. It should use move.

Previous resolution [SUPERSEDED]:

This wording is relative to N4582.

  1. Edit 20.13.4 [allocator.adaptor.members] p15 as indicated:

    template <class T1, class T2, class U, class V>
      void construct(pair<T1, T2>* p, pair<U, V>&& x);
    

    Effects: Equivalent to this->construct(p, piecewise_construct, forward_as_tuple(std::forwardmove<U>(x.first)), forward_as_tuple(std::forwardmove<V>(x.second))).

Proposed resolution:

Withdrawn by the submitter, since the prerequisites were incorrect.


2783. stack::emplace() and queue::emplace() should return decltype(auto)

Section: 23.6.4.1 [queue.defn], 23.6.6.1 [stack.defn] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2016-10-14 Last modified: 2017-03-20

Priority: 2

View all issues with Tentatively Ready status.

Discussion:

The stack and queue adaptors are now defined as:

template <class... Args>
reference emplace(Args&&... args) { return c.emplace_back(std::forward<Args>(args)...); }

This breaks any code using queue<UserDefinedSequence> or stack<UserDefinedSequence> until the user-defined containers are updated to meet the new C++17 requirements.

If we defined them as returning decltype(auto) then we don't break any code. When used with std::vector or std::deque they will return reference, as required, but when used with C++14-conforming containers they will return void, as before.

[2016-11-12, Issaquah]

Sat AM: P2

[2017-03-04, Kona]

Status to Tentatively Ready.

Proposed resolution:

This wording is relative to N4606.

  1. Change return type of emplace in class definition in 23.6.4.1 [queue.defn]:

    template <class... Args>
      referencedecltype(auto) emplace(Args&&... args) { return c.emplace_back(std::forward<Args>(args)...); }
    
  2. Change return type of emplace in class definition in 23.6.6.1 [stack.defn]:

    template <class... Args>
      referencedecltype(auto) emplace(Args&&... args) { return c.emplace_back(std::forward<Args>(args)...); }
    

2937. Is equivalent("existing_thing", "not_existing_thing") an error?

Section: 27.10.15.12 [fs.op.equivalent] Status: Tentatively Ready Submitter: Billy Robert O'Neal III Opened: 2017-02-27 Last modified: 2017-03-20

Priority: 0

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

View all issues with Tentatively Ready status.

Discussion:

See discussion on the LWG mailing list with subject "Is equivalent("existing_thing", "not_existing_thing") an error?", abreviated below:

Billy O'Neal:

The existing "an error is reported" effects say that an error is reported for !exists(p1) && !exists(p2), but I'm not sure that treating equivalent("existing_thing", "not_existing_thing") as "false, no error" makes any more sense than for equivalent("not_existing_thing", "not_existing_thing").

It's also unfortunate that the current spec requires reporting an error for is_other(p1) && is_other(p2) — there's no reason that you can't give a sane answer for paths to NT pipes. (Do POSIX FIFOs give garbage answers here?)

Davis Herring:

I'm fine with an error if either path does not exist. See also Late 29: I would much prefer

file_identity identity(const path&, bool resolve = true);

which would of course produce an error if the path did not exist (or, with the default resolve, was a broken symlink).

See Late 30 and 32 (31 has been resolved). FIFOs pose no trouble: you can even fstat(2) on the naked file descriptors produced by pipe(2). (That said, I observe the strange inconsistency that Linux but not macOS gives both ends of a pipe the same st_ino.)

POSIX has no reason that I know of to treat any file type specially for equivalent().

Billy O'Neal:

I think such a file_identity feature would be useful but we can always add it in addition to equivalent post-C++17.

Beman Dawes:

Looks good to me. Maybe submit this as an issue right away in the hopes it can go in C++17?

[2017-03-04, Kona]

Set priority to 0; Tentatively Ready

Proposed resolution:

This wording is relative to N4640.

  1. Make the following edits to 27.10.15.12 [fs.op.equivalent]:

    bool equivalent(const path& p1, const path& p2);
    bool equivalent(const path& p1, const path& p2, error_code& ec) noexcept;
    

    -1- Let s1 and s2 be file_statuss determined as if by status(p1) and status(p2), respectively.

    -2- Effects: Determines s1 and s2. If (!exists(s1) && !exists(s2)) || (is_other(s1) && is_other(s2)) an error is reported (27.10.7).

    -3- Returns: true, if s1 == s2 and p1 and p2 resolve to the same file system entity, else false. The signature with argument ec returns false if an error occurs.

    -4- Two paths are considered to resolve to the same file system entity if two candidate entities reside on the same device at the same location. [Note: On POSIX platforms, tThis is determined as if by the values of the POSIX stat structure, obtained as if by stat() for the two paths, having equal st_dev values and equal st_ino values. end note]

    -?- Remarks: !exists(p1) || !exists(p2) is an error.

    -5- Throws: As specified in 27.10.7.