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

4110. shared_ptr(nullptr_t, Deleter) is overconstrained, breaking some sensible deleters

Section: 20.3.2.2.2 [util.smartptr.shared.const] Status: New Submitter: Louis Dionne Opened: 2024-06-11 Last modified: 2024-06-11

Priority: Not Prioritized

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

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

View all issues with New status.

Discussion:

The following code doesn't compile on conforming implementations:


    #include <memory>

    void f() {
        std::shared_ptr<int>(new int, [](auto pointer) { delete pointer; });
    }
(Godbolt)

This is caused by the constraint on shared_ptr(nullptr_t p, D d); being that d(p) is valid (20.3.2.2.2 [util.smartptr.shared.const] p9), which leads to a hard error inside the lambda since it is called with a nullptr_t. This seems unintended.

See LLVM issue 93071 comment for additional context.

Proposed resolution:

This wording is relative to N4981.

  1. In 20.3.2.2.2 [util.smartptr.shared.const]/9, specify that shared_ptr(nullptr_t p, D d); checks whether d(static_cast<T*>(nullptr)) is well-formed. This requires expressing the the constraints for the Y* constructors and the nullptr_t constructors separately, which is mostly editorial:
    
    template<class Y, class D> shared_ptr(Y* p, D d);
    template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
    template<class D> shared_ptr(nullptr_t p, D d);
    template<class D, class A> shared_ptr(nullptr_t p, D d, A a);
    

    -9- Constraints: is_move_constructible_v<D> is true, and d(p) is a well-formed expression. For the first two overloads:

    1. (9.1) If T is an array type, then either T is U[N] and Y(*)[N] is convertible to T*, or T is U[] and Y(*)[] is convertible to T*.
    2. (9.2) If T is not an array type, then Y* is convertible to T*.

    
    template<class D> shared_ptr(nullptr_t p, D d);
    template<class D, class A> shared_ptr(nullptr_t p, D d, A a);
    

    -?- Constraints: is_move_constructible_v<D> is true, and d(static_cast<T*>(p)) is a well-formed expression.