On Tue, 25 Aug 2026 at 14:54, Patrick Palka <[email protected]> wrote:
> This implements the [util.smartptr.shared.const] constraints on the
> constructors that take a deleter (which before C++20 were requirements
> rather than constraints).  In passing proactively implement the proposed
> resolution of LWG 4110 which relaxes these constraints on the nullptr_t
> overloads.
>
>         PR libstdc++/126120
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/shared_ptr.h
>         (shared_ptr::shared_ptr(nullptr_t, ...)): Add _Constructible
>         constraint to verify __shared_ptr base initialization is
>         well-formed.  Add _ValidDeleter constraint.
>         * include/bits/shared_ptr_base.h (_ValidDeleter_type): New.
>         (_ValidDeleter): New __enable_if_t constraint.
>         (__shared_ptr::__shared_ptr(Yp*, _Deleter, ...)): Remove
>         static_assert checking deleter invocability.  Add _ValidDeleter
>         constraint.
>         * testsuite/20_util/shared_ptr/cons/126120.cc: New test.
> ---
>  libstdc++-v3/include/bits/shared_ptr.h        | 15 +++++--
>  libstdc++-v3/include/bits/shared_ptr_base.h   | 23 +++++-----
>  .../20_util/shared_ptr/cons/126120.cc         | 45 +++++++++++++++++++
>  3 files changed, 67 insertions(+), 16 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
>
> diff --git a/libstdc++-v3/include/bits/shared_ptr.h 
> b/libstdc++-v3/include/bits/shared_ptr.h
> index 8be9aace1711..6b9acdcffa94 100644
> --- a/libstdc++-v3/include/bits/shared_ptr.h
> +++ b/libstdc++-v3/include/bits/shared_ptr.h
> @@ -212,6 +212,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         shared_ptr(_Yp* __p, _Deleter __d)
>          : __shared_ptr<_Tp>(__p, std::move(__d)) { }
>
> +      // _GLIBCXX_RESOLVE_LIB_DEFECTS
> +      // 4110. shared_ptr(nullptr_t, Deleter) is overconstrained, breaking 
> some
> +      // sensible deleters
> +
>        /**
>         *  @brief  Construct a %shared_ptr that owns a null pointer
>         *          and the deleter @a __d.
> @@ -225,8 +229,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         *
>         *  The last owner will call __d(__p)
>         */
> -      template<typename _Deleter>
> -       shared_ptr(nullptr_t __p, _Deleter __d)
> +      template<typename _Deleter, typename = _Constructible<nullptr_t, 
> _Deleter>>
> +       shared_ptr(nullptr_t __p, _Deleter __d,
> +                  _ValidDeleter<_Deleter, nullptr_t> = {})
>          : __shared_ptr<_Tp>(__p, std::move(__d)) { }
>
>        /**
> @@ -264,8 +269,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         *
>         *  The last owner will call __d(__p)
>         */
> -      template<typename _Deleter, typename _Alloc>
> -       shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
> +      template<typename _Deleter, typename _Alloc,
> +              typename = _Constructible<nullptr_t, _Deleter, _Alloc>>
> +       shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a,
> +                  _ValidDeleter<_Deleter, nullptr_t> = {})
>         : __shared_ptr<_Tp>(__p, std::move(__d), std::move(__a)) { }
>
>        // Aliasing constructor
> diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h 
> b/libstdc++-v3/include/bits/shared_ptr_base.h
> index c8342b7904a5..e4dc40157c52 100644
> --- a/libstdc++-v3/include/bits/shared_ptr_base.h
> +++ b/libstdc++-v3/include/bits/shared_ptr_base.h
> @@ -1469,6 +1469,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        { return static_cast<const __shared_ptr<_Tp, _Lp>*>(this)->get(); }
>      };
>
> +  // Constraint for well-formedness of deleter expression d(p):
> +  struct _ValidDeleter_type { };
> +  template<typename _Deleter, typename _Up>
> +    using _ValidDeleter = __enable_if_t<__is_invocable<_Deleter&, 
> _Up&>::value,
> +                                       _ValidDeleter_type>;

This could be local to __shared_ptr, and private, so it isn't at
namespace scope.

> +
>    template<typename _Tp, _Lock_policy _Lp>
>      class __shared_ptr
>      : public __shared_ptr_access<_Tp, _Lp>
> @@ -1524,24 +1530,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           _M_enable_shared_from_this_with(__p);
>         }
>
> -      template<typename _Yp, typename _Deleter, typename = _SafeConv<_Yp>>
> +      template<typename _Yp, typename _Deleter, typename = _SafeConv<_Yp>,
> +              typename = _ValidDeleter<_Deleter, _Yp*>>
>         __shared_ptr(_Yp* __p, _Deleter __d)
>         : _M_ptr(__p), _M_refcount(__p, std::move(__d))
> -       {
> -         static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
> -             "deleter expression d(p) is well-formed");
> -         _M_enable_shared_from_this_with(__p);
> -       }
> +       { _M_enable_shared_from_this_with(__p); }
>
>        template<typename _Yp, typename _Deleter, typename _Alloc,
> -              typename = _SafeConv<_Yp>>
> +              typename = _SafeConv<_Yp>, typename = _ValidDeleter<_Deleter, 
> _Yp*>>
>         __shared_ptr(_Yp* __p, _Deleter __d, _Alloc __a)
>         : _M_ptr(__p), _M_refcount(__p, std::move(__d), std::move(__a))
> -       {
> -         static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
> -             "deleter expression d(p) is well-formed");

I wonder if we should move this static_assert into the reset(Y*, D)
member. Previously passing and invalid deleter to reset would trigger
this assertion, with a nice message. Now it will fail overload
resolution for the constructor.

We could put the static_assert in reset(Y*, D) to restore the nice message.

> -         _M_enable_shared_from_this_with(__p);
> -       }
> +       { _M_enable_shared_from_this_with(__p); }
>
>        template<typename _Deleter>
>         __shared_ptr(nullptr_t __p, _Deleter __d)
> diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc 
> b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
> new file mode 100644
> index 000000000000..7a82358200e3
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
> @@ -0,0 +1,45 @@
> +// PR libstdc++/126120 - is_constructible_v with shared_ptr from an invalid 
> deleter
> +// { dg-do compile { target c++11 } }
> +
> +#include <memory>
> +#include <type_traits>
> +
> +struct NonDeleter { };
> +
> +struct NonMoveableDeleter
> +{
> +  NonMoveableDeleter() = default;
> +  NonMoveableDeleter(const NonMoveableDeleter&) = delete;
> +  void operator()(int* p) const { delete p; }
> +};
> +
> +using shared_ptr = std::shared_ptr<int>;
> +using Alloc = std::allocator<int>;
> +
> +// 1. Pointer + Deleter
> +static_assert( ! std::is_constructible<shared_ptr, int*, NonDeleter>::value, 
> "");
> +static_assert( ! std::is_constructible<shared_ptr, int*, 
> NonMoveableDeleter>::value, "");
> +
> +// 2. Pointer + Deleter + Allocator
> +static_assert( ! std::is_constructible<shared_ptr, int*, NonDeleter, 
> Alloc>::value, "");
> +static_assert( ! std::is_constructible<shared_ptr, int*, NonMoveableDeleter, 
> Alloc>::value, "");
> +
> +// 3. nullptr_t + Deleter
> +static_assert( ! std::is_constructible<shared_ptr, std::nullptr_t, 
> NonDeleter>::value, "");
> +static_assert( ! std::is_constructible<shared_ptr, std::nullptr_t, 
> NonMoveableDeleter>::value, "");
> +
> +// 4. nullptr_t + Deleter + Allocator
> +static_assert( ! std::is_constructible<shared_ptr, std::nullptr_t, 
> NonDeleter, Alloc>::value, "");
> +static_assert( ! std::is_constructible<shared_ptr, std::nullptr_t, 
> NonMoveableDeleter, Alloc>::value, "");
> +
> +#pragma GCC diagnostic push
> +#pragma GCC diagnostic ignored "-Wc++20-extensions" // lambda template
> +void
> +test_lwg4110()
> +{
> +  // LWG 4110 - shared_ptr(nullptr_t, Deleter) is overconstrained, breaking 
> some sensible deleters
> +  auto deleter = []<typename T>(T pointer) { delete pointer; };
> +  shared_ptr p(new int, deleter);
> +  shared_ptr q(new int, deleter, Alloc{});
> +}
> +#pragma GCC diagnostic pop
> --
> 2.55.0.708.g2c3adbb2c4
>
> >
> >
> >       Subject: [PATCH] libstdc++: Fix SFINAE with shared_ptr and invalid 
> > deleter
> >        [PR126120]
> >
> >       Tested on x86_64-pc-linux-gnu, does this look OK for trunk/16?
> >
> >       Changes in v2:
> >         - Implement LWG 4110 proposed resolution, encoding the _ValidDeleter
> >           constraint as a function parameter instead of a template 
> > parameter.
> >         - Remove redundant is_move_constructible constraint in _ValidDeleter
> >           since the derived class's _Constructible constraint already checks
> >           it.
> >
> >       -- >8 --
> >
> >       This implements the [util.smartptr.shared.const] constraints on the
> >       constructors that take a deleter (which before C++20 were requirements
> >       rather than constraints).  In passing proactively implement the 
> > proposed
> >       resolution of LWG 4110.
> >
> >               PR libstdc++/126120
> >
> >       libstdc++-v3/ChangeLog:
> >
> >               * include/bits/shared_ptr.h
> >               (shared_ptr::shared_ptr(nullptr_t, ...)): Add _Constructible
> >               constraint to verify __shared_ptr base initialization is
> >               well-formed.  Add _ValidDeleter constraint.
> >               * include/bits/shared_ptr_base.h (_ValidDeleter): New 
> > __enable_if_t
> >               constraint.
> >               (__shared_ptr::__shared_ptr(Yp*, _Deleter, ...)): Remove
> >               static_assert checking deleter invocability.  Add 
> > _ValidDeleter
> >               constraint.
> >               * testsuite/20_util/shared_ptr/cons/126120.cc: New test.
> >       ---
> >        libstdc++-v3/include/bits/shared_ptr.h        | 15 +++++--
> >        libstdc++-v3/include/bits/shared_ptr_base.h   | 21 ++++-----
> >        .../20_util/shared_ptr/cons/126120.cc         | 45 
> > +++++++++++++++++++
> >        3 files changed, 65 insertions(+), 16 deletions(-)
> >        create mode 100644 
> > libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
> >
> >       diff --git a/libstdc++-v3/include/bits/shared_ptr.h 
> > b/libstdc++-v3/include/bits/shared_ptr.h
> >       index 8be9aace1711..6b9acdcffa94 100644
> >       --- a/libstdc++-v3/include/bits/shared_ptr.h
> >       +++ b/libstdc++-v3/include/bits/shared_ptr.h
> >       @@ -212,6 +212,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >               shared_ptr(_Yp* __p, _Deleter __d)
> >                : __shared_ptr<_Tp>(__p, std::move(__d)) { }
> >
> >       +      // _GLIBCXX_RESOLVE_LIB_DEFECTS
> >       +      // 4110. shared_ptr(nullptr_t, Deleter) is overconstrained, 
> > breaking some
> >       +      // sensible deleters
> >       +
> >              /**
> >               *  @brief  Construct a %shared_ptr that owns a null pointer
> >               *          and the deleter @a __d.
> >       @@ -225,8 +229,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >               *
> >               *  The last owner will call __d(__p)
> >               */
> >       -      template<typename _Deleter>
> >       -       shared_ptr(nullptr_t __p, _Deleter __d)
> >       +      template<typename _Deleter, typename = 
> > _Constructible<nullptr_t, _Deleter>>
> >       +       shared_ptr(nullptr_t __p, _Deleter __d,
> >       +                  _ValidDeleter<_Deleter, nullptr_t> = {})
> >                : __shared_ptr<_Tp>(__p, std::move(__d)) { }
> >
> >              /**
> >       @@ -264,8 +269,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >               *
> >               *  The last owner will call __d(__p)
> >               */
> >       -      template<typename _Deleter, typename _Alloc>
> >       -       shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
> >       +      template<typename _Deleter, typename _Alloc,
> >       +              typename = _Constructible<nullptr_t, _Deleter, _Alloc>>
> >       +       shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a,
> >       +                  _ValidDeleter<_Deleter, nullptr_t> = {})
> >               : __shared_ptr<_Tp>(__p, std::move(__d), std::move(__a)) { }
> >
> >              // Aliasing constructor
> >       diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h 
> > b/libstdc++-v3/include/bits/shared_ptr_base.h
> >       index c8342b7904a5..7c1f965f0bb1 100644
> >       --- a/libstdc++-v3/include/bits/shared_ptr_base.h
> >       +++ b/libstdc++-v3/include/bits/shared_ptr_base.h
> >       @@ -1469,6 +1469,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >              { return static_cast<const __shared_ptr<_Tp, 
> > _Lp>*>(this)->get(); }
> >            };
> >
> >       +  // Constraint for well-formedness of deleter expression d(p):
> >       +  template<typename _Deleter, typename _Up>
> >       +    using _ValidDeleter = __enable_if_t<__is_invocable<_Deleter&, 
> > _Up&>::value, int>;
> >       +
> >          template<typename _Tp, _Lock_policy _Lp>
> >            class __shared_ptr
> >            : public __shared_ptr_access<_Tp, _Lp>
> >       @@ -1524,24 +1528,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >                 _M_enable_shared_from_this_with(__p);
> >               }
> >
> >       -      template<typename _Yp, typename _Deleter, typename = 
> > _SafeConv<_Yp>>
> >       +      template<typename _Yp, typename _Deleter, typename = 
> > _SafeConv<_Yp>,
> >       +              typename = _ValidDeleter<_Deleter, _Yp*>>
> >               __shared_ptr(_Yp* __p, _Deleter __d)
> >               : _M_ptr(__p), _M_refcount(__p, std::move(__d))
> >       -       {
> >       -         static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
> >       -             "deleter expression d(p) is well-formed");
> >       -         _M_enable_shared_from_this_with(__p);
> >       -       }
> >       +       { _M_enable_shared_from_this_with(__p); }
> >
> >              template<typename _Yp, typename _Deleter, typename _Alloc,
> >       -              typename = _SafeConv<_Yp>>
> >       +              typename = _SafeConv<_Yp>, typename = 
> > _ValidDeleter<_Deleter, _Yp*>>
> >               __shared_ptr(_Yp* __p, _Deleter __d, _Alloc __a)
> >               : _M_ptr(__p), _M_refcount(__p, std::move(__d), 
> > std::move(__a))
> >       -       {
> >       -         static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
> >       -             "deleter expression d(p) is well-formed");
> >       -         _M_enable_shared_from_this_with(__p);
> >       -       }
> >       +       { _M_enable_shared_from_this_with(__p); }
> >
> >              template<typename _Deleter>
> >               __shared_ptr(nullptr_t __p, _Deleter __d)
> >       diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc 
> > b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
> >       new file mode 100644
> >       index 000000000000..0d3f84e2e779
> >       --- /dev/null
> >       +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
> >       @@ -0,0 +1,45 @@
> >       +// PR libstdc++/126120 - is_constructible_v with shared_ptr from an 
> > invalid deleter
> >       +// { dg-do compile { target c++11 } }
> >       +
> >       +#include <memory>
> >       +#include <type_traits>
> >       +
> >       +struct NonDeleter { };
> >       +
> >       +struct NonMoveableDeleter
> >       +{
> >       +  NonMoveableDeleter() = default;
> >       +  NonMoveableDeleter(const NonMoveableDeleter&) = delete;
> >       +  void operator()(int*) const;
> >       +};
> >       +
> >       +using shared_ptr = std::shared_ptr<int>;
> >       +using Alloc = std::allocator<int>;
> >       +
> >       +// 1. Pointer + Deleter
> >       +static_assert( ! std::is_constructible<shared_ptr, int*, 
> > NonDeleter>::value, "");
> >       +static_assert( ! std::is_constructible<shared_ptr, int*, 
> > NonMoveableDeleter>::value, "");
> >       +
> >       +// 2. Pointer + Deleter + Allocator
> >       +static_assert( ! std::is_constructible<shared_ptr, int*, NonDeleter, 
> > Alloc>::value, "");
> >       +static_assert( ! std::is_constructible<shared_ptr, int*, 
> > NonMoveableDeleter, Alloc>::value, "");
> >       +
> >       +// 3. nullptr_t + Deleter
> >       +static_assert( ! std::is_constructible<shared_ptr, std::nullptr_t, 
> > NonDeleter>::value, "");
> >       +static_assert( ! std::is_constructible<shared_ptr, std::nullptr_t, 
> > NonMoveableDeleter>::value, "");
> >       +
> >       +// 4. nullptr_t + Deleter + Allocator
> >       +static_assert( ! std::is_constructible<shared_ptr, std::nullptr_t, 
> > NonDeleter, Alloc>::value, "");
> >       +static_assert( ! std::is_constructible<shared_ptr, std::nullptr_t, 
> > NonMoveableDeleter, Alloc>::value, "");
> >       +
> >       +struct GenericDeleter {
> >       +  template<typename T> void operator()(T*) const;
> >
> >       +};
> >       +
> >       +void
> >       +test01()
> >       +{
> >       +  // LWG 4110 - shared_ptr(nullptr_t, Deleter) is overconstrained, 
> > breaking some sensible deleters
> >       +  shared_ptr p(new int, GenericDeleter{});
> >       +  shared_ptr q(new int, GenericDeleter{}, Alloc{});
> >
> > I do not think that above example reproduces problem from the issue, 
> > checking is invocable
> > <GenericDeleter, nullptr> is simply true. The lambda with auto was 
> > important, as checking is
> > invocable triggered return type deduction, which instantiated delete with 
> > nullptr, that was ill-formed.
> >       +}
> >       --
> >       2.55.0.708.g2c3adbb2c4
> >
> >
> >
> >       >
> >       >
> >       >
> >       >       @@ -264,7 +264,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       >               *
> >       >               *  The last owner will call __d(__p)
> >       >               */
> >       >       -      template<typename _Deleter, typename _Alloc>
> >       >       +      template<typename _Deleter, typename _Alloc,
> >       >       +              typename = _Constructible<nullptr_t, _Deleter, 
> > _Alloc>>
> >       >               shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
> >       >              : __shared_ptr<_Tp>(__p, std::move(__d), 
> > std::move(__a)) { }
> >       >
> >       >
> >       >
> >       >       diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h 
> > b/libstdc++-v3/include/bits/shared_ptr_base.h
> >       >       index c8342b7904a5..2eb3436183ee 100644
> >       >       --- a/libstdc++-v3/include/bits/shared_ptr_base.h
> >       >       +++ b/libstdc++-v3/include/bits/shared_ptr_base.h
> >       >       @@ -1469,6 +1469,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       >              { return static_cast<const __shared_ptr<_Tp, 
> > _Lp>*>(this)->get(); }
> >       >            };
> >       >
> >       >       +  // Constraint for well-formedness of deleter expression 
> > d(p):
> >       >       +  template<typename _Deleter, typename _Up>
> >       >       +    using _ValidDeleter = __enable_if_t<__and_<
> >       >       +      is_move_constructible<_Deleter>,
> >       >       +      __is_invocable<_Deleter&, _Up&>
> >       >       +      >::value>;
> >       >       +
> >       >          template<typename _Tp, _Lock_policy _Lp>
> >       >            class __shared_ptr
> >       >            : public __shared_ptr_access<_Tp, _Lp>
> >       >       @@ -1524,31 +1531,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >       >                 _M_enable_shared_from_this_with(__p);
> >       >               }
> >       >
> >       >       -      template<typename _Yp, typename _Deleter, typename = 
> > _SafeConv<_Yp>>
> >       >       +      template<typename _Yp, typename _Deleter, typename = 
> > _SafeConv<_Yp>,
> >       >       +              typename = _ValidDeleter<_Deleter, _Yp*>>
> >       >               __shared_ptr(_Yp* __p, _Deleter __d)
> >       >               : _M_ptr(__p), _M_refcount(__p, std::move(__d))
> >       >       -       {
> >       >       -         static_assert(__is_invocable<_Deleter&, 
> > _Yp*&>::value,
> >       >       -             "deleter expression d(p) is well-formed");
> >       >       -         _M_enable_shared_from_this_with(__p);
> >       >       -       }
> >       >       +       { _M_enable_shared_from_this_with(__p); }
> >       >
> >       >              template<typename _Yp, typename _Deleter, typename 
> > _Alloc,
> >       >       -              typename = _SafeConv<_Yp>>
> >       >       +              typename = _SafeConv<_Yp>, typename = 
> > _ValidDeleter<_Deleter, _Yp*>>
> >       >               __shared_ptr(_Yp* __p, _Deleter __d, _Alloc __a)
> >       >               : _M_ptr(__p), _M_refcount(__p, std::move(__d), 
> > std::move(__a))
> >       >       -       {
> >       >       -         static_assert(__is_invocable<_Deleter&, 
> > _Yp*&>::value,
> >       >       -             "deleter expression d(p) is well-formed");
> >       >       -         _M_enable_shared_from_this_with(__p);
> >       >       -       }
> >       >       +       { _M_enable_shared_from_this_with(__p); }
> >       >
> >       >       -      template<typename _Deleter>
> >       >       +      template<typename _Deleter, typename = 
> > _ValidDeleter<_Deleter, nullptr_t>>
> >       >               __shared_ptr(nullptr_t __p, _Deleter __d)
> >       >               : _M_ptr(0), _M_refcount(__p, std::move(__d))
> >       >               { }
> >       >
> >       >       -      template<typename _Deleter, typename _Alloc>
> >       >       +      template<typename _Deleter, typename _Alloc,
> >       >       +              typename = _ValidDeleter<_Deleter, nullptr_t>>
> >       >                __shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
> >       >               : _M_ptr(0), _M_refcount(__p, std::move(__d), 
> > std::move(__a))
> >       >               { }
> >       >       diff --git 
> > a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc 
> > b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
> >       >       new file mode 100644
> >       >       index 000000000000..a5a29597e078
> >       >       --- /dev/null
> >       >       +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/126120.cc
> >       >       @@ -0,0 +1,31 @@
> >       >       +// PR libstdc++/126120 - is_constructible_v with shared_ptr 
> > from an invalid deleter
> >       >       +// { dg-do compile { target c++11 } }
> >       >       +
> >       >       +#include <memory>
> >       >       +#include <type_traits>
> >       >       +
> >       >       +struct NonDeleter { };
> >       >       +
> >       >       +struct NonMoveableDeleter {
> >       >       +  NonMoveableDeleter() = default;
> >       >       +  NonMoveableDeleter(const NonMoveableDeleter&) = delete;
> >       >       +  void operator()(int* p) const { delete p; }
> >       >       +};
> >       >       +
> >       >       +using Alloc = std::allocator<int>;
> >       >       +
> >       >       +// 1. Pointer + Deleter
> >       >       +static_assert( ! std::is_constructible<std::shared_ptr<int>, 
> > int*, NonDeleter>::value, "");
> >       >       +static_assert( ! std::is_constructible<std::shared_ptr<int>, 
> > int*, NonMoveableDeleter>::value, "");
> >       >       +
> >       >       +// 2. Pointer + Deleter + Allocator
> >       >       +static_assert( ! std::is_constructible<std::shared_ptr<int>, 
> > int*, NonDeleter, Alloc>::value, "");
> >       >       +static_assert( ! std::is_constructible<std::shared_ptr<int>, 
> > int*, NonMoveableDeleter, Alloc>::value, "");
> >       >       +
> >       >       +// 3. nullptr_t + Deleter
> >       >       +static_assert( ! std::is_constructible<std::shared_ptr<int>, 
> > std::nullptr_t, NonDeleter>::value, "");
> >       >       +static_assert( ! std::is_constructible<std::shared_ptr<int>, 
> > std::nullptr_t, NonMoveableDeleter>::value, "");
> >       >       +
> >       >       +// 4. nullptr_t + Deleter + Allocator
> >       >       +static_assert( ! std::is_constructible<std::shared_ptr<int>, 
> > std::nullptr_t, NonDeleter, Alloc>::value, "");
> >       >       +static_assert( ! std::is_constructible<std::shared_ptr<int>, 
> > std::nullptr_t, NonMoveableDeleter, Alloc>::value, "");
> >       >       --
> >       >       2.55.0.669.g593c42fe07
> >       >
> >       >
> >       >
> >
> >
> >

Reply via email to