Title
unique_ptr "Mandates: This constructor is not selected by class template argument deduction"
Status
new
Section
[unique.ptr.single.ctor]
Submitter
Arthur O'Dwyer

Created on 2021-11-03.00:00:00 last changed 3 weeks ago

Messages

Date: 2021-11-06.15:42:05

Proposed resolution:

This wording is relative to N4901.

  1. Modify [unique.ptr.single.general], class template unique_ptr synopsis, as indicated:

    […]
    // [unique.ptr.single.ctor], constructors
    constexpr unique_ptr() noexcept;
    explicit unique_ptr(type_identity_t<pointer> p) noexcept;
    unique_ptr(type_identity_t<pointer> p, see below d1) noexcept;
    unique_ptr(type_identity_t<pointer> p, see below d2) noexcept;
    […]
    
  2. Modify [unique.ptr.single.ctor] as indicated:

    explicit unique_ptr(type_identity_t<pointer> p) noexcept;
    

    -5- Constraints: is_pointer_v<deleter_type> is false and is_default_constructible_v<deleter_type> is true.

    -6- Mandates: This constructor is not selected by class template argument deduction ([over.match.class.deduct]).

    -7- Preconditions: […]

    -8- Effects: […]

    -9- Postconditions: […]

    unique_ptr(type_identity_t<pointer> p, const D& d) noexcept;
    unique_ptr(type_identity_t<pointer> p, remove_reference_t<D>&& d) noexcept;
    

    -10- Constraints: is_constructible_v<D, decltype(d)> is true.

    -11- Mandates: These constructors are not selected by class template argument deduction ([over.match.class.deduct]).

    -12- Preconditions: […]

    -13- Effects: […]

    -14- Postconditions: […]

    -15- Remarks: If D is a reference type, the second constructor is defined as deleted.

    -16- [Example 1: […] — end example]

Date: 2021-11-03.00:00:00

P1460R1 changed the wording for unique_ptr's constructor unique_ptr(pointer). In C++17, it said in [unique.ptr.single.ctor] p5:

explicit unique_ptr(pointer p) noexcept;

Preconditions: […]

Effects: […]

Postconditions: […]

Remarks: If is_pointer_v<deleter_type> is true or is_default_constructible_v<deleter_type> is false, this constructor shall not participate in overload resolution. If class template argument deduction would select the function template corresponding to this constructor, then the program is ill-formed.

In C++20, it says in [unique.ptr.single.ctor] p5:

explicit unique_ptr(pointer p) noexcept;

Constraints: is_pointer_v<deleter_type> is false and is_default_constructible_v<deleter_type> is true.

Mandates: This constructor is not selected by class template argument deduction.

Preconditions: […]

Effects: […]

Postconditions: […]

Normally, we use "Mandates:" for static_assert-like stuff, not just to indicate that some constructor doesn't contribute to CTAD. Both libstdc++ and Microsoft (and soon libc++, see LLVM issue) seem to agree about the intent of this wording: It's basically asking for the constructor to be implemented with a CTAD firewall, as

explicit unique_ptr(type_identity_t<pointer> p) noexcept;

and there is no actual static_assert corresponding to this "Mandates:" element. In particular, the following program is well-formed on all vendors:

// godbolt link
template<class T> auto f(T p) -> decltype(std::unique_ptr(p));
template<class T> constexpr bool f(T p) { return true; }  
static_assert(f((int*)nullptr));

I claim that this is a confusing and/or wrong use of "Mandates:". My proposed resolution is simply to respecify the constructor as

explicit unique_ptr(type_identity_t<pointer> p) noexcept;

Constraints: is_pointer_v<deleter_type> is false and is_default_constructible_v<deleter_type> is true.

Preconditions: […]

Effects: […]

Postconditions: […]

with no Mandates: or Remarks: elements at all.

History
Date User Action Args
2021-11-06 15:42:05adminsetmessages: + msg12209
2021-11-03 00:00:00admincreate