unique_ptr
"Mandates: This constructor is not selected by class template argument deduction"Section: 20.3.1.3.2 [unique.ptr.single.ctor] Status: C++23 Submitter: Arthur O'Dwyer Opened: 2021-11-03 Last modified: 2023-11-22
Priority: Not Prioritized
View all other issues in [unique.ptr.single.ctor].
View all issues with C++23 status.
Discussion:
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: Ifis_pointer_v<deleter_type>
istrue
oris_default_constructible_v<deleter_type>
isfalse
, 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:
Mandates: This constructor is not selected by class template argument deduction. Preconditions: […] Effects: […] Postconditions: […]is_pointer_v<deleter_type>
isfalse
andis_default_constructible_v<deleter_type>
istrue
.
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:
Preconditions: […] Effects: […] Postconditions: […]is_pointer_v<deleter_type>
isfalse
andis_default_constructible_v<deleter_type>
istrue
.
with no Mandates: or Remarks: elements at all.
[2022-01-29; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
[2022-02-10 Approved at February 2022 virtual plenary. Status changed: Tentatively Ready → WP.]
Proposed resolution:
This wording is relative to N4901.
Modify 20.3.1.3.1 [unique.ptr.single.general], class template unique_ptr
synopsis, as indicated:
[…] // 20.3.1.3.2 [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; […]
Modify 20.3.1.3.2 [unique.ptr.single.ctor] as indicated:
explicit unique_ptr(type_identity_t<pointer> p) noexcept;-5- Constraints:
is_pointer_v<deleter_type>
isfalse
andis_default_constructible_v<deleter_type>
istrue
.-6- Mandates: This constructor is not selected by class template argument deduction (12.2.2.9 [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)>
istrue
.-11- Mandates: These constructors are not selected by class template argument deduction (12.2.2.9 [over.match.class.deduct]).-12- Preconditions: […] -13- Effects: […] -14- Postconditions: […] -15- Remarks: IfD
is a reference type, the second constructor is defined as deleted. -16- [Example 1: […] — end example]