This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of WP status.

3195. What is the stored pointer value of an empty weak_ptr?

Section: 20.3.2.3.2 [util.smartptr.weak.const] Status: WP Submitter: Casey Carter Opened: 2019-03-15 Last modified: 2020-11-09

Priority: 2

View all issues with WP status.

Discussion:

20.3.2.3.2 [util.smartptr.weak.const] specifies weak_ptr's default constructor:

constexpr weak_ptr() noexcept;

1 Effects: Constructs an empty weak_ptr object.

2 Ensures: use_count() == 0.

and shared_ptr converting constructor template:

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

3 Remarks: The second and third constructors shall not participate in overload resolution unless Y* is compatible with T*.

4 Effects: If r is empty, constructs an empty weak_ptr object; otherwise, constructs a weak_ptr object that shares ownership with r and stores a copy of the pointer stored in r.

5 Ensures: use_count() == r.use_count().

Note that neither specifies the value of the stored pointer when the resulting weak_ptr is empty. This didn't matter — the stored pointer value was unobservable for an empty weak_ptr — until we added atomic<weak_ptr>. 33.5.8.7.3 [util.smartptr.atomic.weak]/15 says:

Remarks: Two weak_ptr objects are equivalent if they store the same pointer value and either share ownership, or both are empty. The weak form may fail spuriously. See 33.5.8.2 [atomics.types.operations].

Two empty weak_ptr objects that store different pointer values are not equivalent. We could correct this by changing 33.5.8.7.3 [util.smartptr.atomic.weak]/15 to "Two weak_ptr objects are equivalent if they are both empty, or if they share ownership and store the same pointer value." In practice, an implementation of atomic<weak_ptr> will CAS on both the ownership (control block pointer) and stored pointer value, so it seems cleaner to pin down the stored pointer value of an empty weak_ptr.

[2019-06-09 Priority set to 2 after reflector discussion]

Previous resolution [SUPERSEDED]

This wording is relative to N4810.

  1. Modify 20.3.2.3.2 [util.smartptr.weak.const] as indicated (note the drive-by edit to cleanup the occurrences of "constructs an object of class foo"):

    constexpr weak_ptr() noexcept;
    

    -1- Effects: Constructs an empty weak_ptr object that stores a null pointer value.

    -2- Ensures: use_count() == 0.

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

    -3- Remarks: The second and third constructors shall not participate in overload resolution unless Y* is compatible with T*.

    -4- Effects: If r is empty, constructs an empty weak_ptr object that stores a null pointer value; otherwise, constructs a weak_ptr object that shares ownership with r and stores a copy of the pointer stored in r.

    -5- Ensures: use_count() == r.use_count().

[2020-02-14 Casey updates P/R per LWG instruction]

While reviewing the P/R in Prague, Tim Song noticed that the stored pointer value of a moved-from weak_ptr must also be specified.

[2020-02-16; Prague]

Reviewed revised wording and moved to Ready for Varna.

[2020-11-09 Approved In November virtual meeting. Status changed: Ready → WP.]

Proposed resolution:

This wording is relative to N4849.

  1. Modify 20.3.2.3.2 [util.smartptr.weak.const] as indicated:

    constexpr weak_ptr() noexcept;
    

    -1- Effects: Constructs an empty weak_ptr object that stores a null pointer value.

    -2- Postconditions: use_count() == 0.

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

    -3- Remarks: The second and third constructors shall not participate in overload resolution unless Y* is compatible with T*.

    -4- Effects: If r is empty, constructs an empty weak_ptr object that stores a null pointer value; otherwise, constructs a weak_ptr object that shares ownership with r and stores a copy of the pointer stored in r.

    -5- Postconditions: use_count() == r.use_count().

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

    -6- Remarks: The second constructor shall not participate in overload resolution unless Y* is compatible with T*.

    -7- Effects: Move constructs a weak_ptr instance from r.

    -8- Postconditions: *this shall containcontains the old value of r. r shall beis empty., stores a null pointer value, and r.use_count() == 0.