diff mbox

Support move-only deleters in std::shared_ptr (LWG 2802)

Message ID 20170609132305.GA22451@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely June 9, 2017, 1:23 p.m. UTC
As required for C++17, this relaxes the requirements to allow
move-only deleters.

We can't pass them everywhere by rvalue reference, because the
constructor from std::unique_ptr can lead to construction from an
lvalue that won't bind to an rvalue reference. We could add another
_Sp_shared_deleter constructor, but I haven't done so.

	* doc/xml/manual/intro.xml: Document LWG 2802, 2873 and 2942 changes.
	* include/bits/shared_ptr.h (shared_ptr): Use rvalues for deleters
	(LWG 2802).
	* include/bits/shared_ptr_base.h (_Sp_ebo_helper, _Sp_counted_deleter
	(_Sp_counted_deleter::_Impl, __shared_count, __shared_ptr): Likewise.
	* testsuite/20_util/shared_ptr/cons/lwg2802.cc: New.

Tested powerp64le-linux, committed to trunk.
commit bf8b16a9aa960265af37dde30798e2bbfd599056
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Jun 9 12:15:10 2017 +0100

    Support move-only deleters in std::shared_ptr (LWG 2802)
    
    	* doc/xml/manual/intro.xml: Document LWG 2802, 2873 and 2942 changes.
    	* include/bits/shared_ptr.h (shared_ptr): Use rvalues for deleters
    	(LWG 2802).
    	* include/bits/shared_ptr_base.h (_Sp_ebo_helper, _Sp_counted_deleter
    	(_Sp_counted_deleter::_Impl, __shared_count, __shared_ptr): Likewise.
    	* testsuite/20_util/shared_ptr/cons/lwg2802.cc: New.
diff mbox

Patch

diff --git a/libstdc++-v3/doc/xml/manual/intro.xml b/libstdc++-v3/doc/xml/manual/intro.xml
index b750f0a..4ec7494 100644
--- a/libstdc++-v3/doc/xml/manual/intro.xml
+++ b/libstdc++-v3/doc/xml/manual/intro.xml
@@ -1119,6 +1119,29 @@  requirements of the license of GCC.
       arguments and store them directly as the target object.
     </para></listitem></varlistentry>
 
+    <varlistentry><term><link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="&DR;#2802">2802</link>:
+       <emphasis>Add noexcept to several <code>shared_ptr</code> related
+         functions
+       </emphasis>
+    </term>
+    <listitem><para>Add noexcept.
+    </para></listitem></varlistentry>
+
+    <varlistentry><term><link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="&DR;#2873">2873</link>:
+       <emphasis><code>shared_ptr</code> constructor requirements for a deleter
+       </emphasis>
+    </term>
+    <listitem><para>Use rvalues for deleters.
+    </para></listitem></varlistentry>
+
+    <varlistentry><term><link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="&DR;#2942">2942</link>:
+       <emphasis>LWG 2873's resolution missed
+         <code>weak_ptr::owner_before</code>
+       </emphasis>
+    </term>
+    <listitem><para>Add noexcept.
+    </para></listitem></varlistentry>
+
   </variablelist>
 
  </section>
diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h
index 2ddb221..264e35c 100644
--- a/libstdc++-v3/include/bits/shared_ptr.h
+++ b/libstdc++-v3/include/bits/shared_ptr.h
@@ -144,7 +144,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       template<typename _Yp, typename _Deleter,
 	       typename = _Constructible<_Yp*, _Deleter>>
 	shared_ptr(_Yp* __p, _Deleter __d)
-        : __shared_ptr<_Tp>(__p, __d) { }
+        : __shared_ptr<_Tp>(__p, std::move(__d)) { }
 
       /**
        *  @brief  Construct a %shared_ptr that owns a null pointer
@@ -161,7 +161,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
        */
       template<typename _Deleter>
 	shared_ptr(nullptr_t __p, _Deleter __d)
-        : __shared_ptr<_Tp>(__p, __d) { }
+        : __shared_ptr<_Tp>(__p, std::move(__d)) { }
 
       /**
        *  @brief  Construct a %shared_ptr that owns the pointer @a __p
@@ -181,7 +181,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       template<typename _Yp, typename _Deleter, typename _Alloc,
 	       typename = _Constructible<_Yp*, _Deleter, _Alloc>>
 	shared_ptr(_Yp* __p, _Deleter __d, _Alloc __a)
-	: __shared_ptr<_Tp>(__p, __d, std::move(__a)) { }
+	: __shared_ptr<_Tp>(__p, std::move(__d), std::move(__a)) { }
 
       /**
        *  @brief  Construct a %shared_ptr that owns a null pointer
@@ -200,7 +200,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
        */
       template<typename _Deleter, typename _Alloc>
 	shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
-	: __shared_ptr<_Tp>(__p, __d, std::move(__a)) { }
+	: __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 f0916d0..a07058c 100644
--- a/libstdc++-v3/include/bits/shared_ptr_base.h
+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
@@ -418,6 +418,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct _Sp_ebo_helper<_Nm, _Tp, true> : private _Tp
     {
       explicit _Sp_ebo_helper(const _Tp& __tp) : _Tp(__tp) { }
+      explicit _Sp_ebo_helper(_Tp&& __tp) : _Tp(std::move(__tp)) { }
 
       static _Tp&
       _S_get(_Sp_ebo_helper& __eboh) { return static_cast<_Tp&>(__eboh); }
@@ -428,6 +429,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct _Sp_ebo_helper<_Nm, _Tp, false>
     {
       explicit _Sp_ebo_helper(const _Tp& __tp) : _M_tp(__tp) { }
+      explicit _Sp_ebo_helper(_Tp&& __tp) : _M_tp(std::move(__tp)) { }
 
       static _Tp&
       _S_get(_Sp_ebo_helper& __eboh)
@@ -448,7 +450,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       public:
 	_Impl(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept
-	: _M_ptr(__p), _Del_base(__d), _Alloc_base(__a)
+	: _M_ptr(__p), _Del_base(std::move(__d)), _Alloc_base(__a)
 	{ }
 
 	_Deleter& _M_del() noexcept { return _Del_base::_S_get(*this); }
@@ -462,11 +464,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       // __d(__p) must not throw.
       _Sp_counted_deleter(_Ptr __p, _Deleter __d) noexcept
-      : _M_impl(__p, __d, _Alloc()) { }
+      : _M_impl(__p, std::move(__d), _Alloc()) { }
 
       // __d(__p) must not throw.
       _Sp_counted_deleter(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept
-      : _M_impl(__p, __d, __a) { }
+      : _M_impl(__p, std::move(__d), __a) { }
 
       ~_Sp_counted_deleter() noexcept { }
 
@@ -1111,7 +1113,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Yp, typename _Deleter, typename = _SafeConv<_Yp>>
 	__shared_ptr(_Yp* __p, _Deleter __d)
-	: _M_ptr(__p), _M_refcount(__p, __d)
+	: _M_ptr(__p), _M_refcount(__p, std::move(__d))
 	{
 	  static_assert(__is_invocable<_Deleter&, _Yp*&>::value,
 	      "deleter expression d(p) is well-formed");
@@ -1121,7 +1123,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       template<typename _Yp, typename _Deleter, typename _Alloc,
 	       typename = _SafeConv<_Yp>>
 	__shared_ptr(_Yp* __p, _Deleter __d, _Alloc __a)
-	: _M_ptr(__p), _M_refcount(__p, __d, std::move(__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");
@@ -1130,12 +1132,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       template<typename _Deleter>
 	__shared_ptr(nullptr_t __p, _Deleter __d)
-	: _M_ptr(0), _M_refcount(__p, __d)
+	: _M_ptr(0), _M_refcount(__p, std::move(__d))
 	{ }
 
       template<typename _Deleter, typename _Alloc>
         __shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
-	: _M_ptr(0), _M_refcount(__p, __d, std::move(__a))
+	: _M_ptr(0), _M_refcount(__p, std::move(__d), std::move(__a))
 	{ }
 
       template<typename _Yp>
@@ -1278,12 +1280,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       template<typename _Yp, typename _Deleter>
 	_SafeConv<_Yp>
 	reset(_Yp* __p, _Deleter __d)
-	{ __shared_ptr(__p, __d).swap(*this); }
+	{ __shared_ptr(__p, std::move(__d)).swap(*this); }
 
       template<typename _Yp, typename _Deleter, typename _Alloc>
 	_SafeConv<_Yp>
 	reset(_Yp* __p, _Deleter __d, _Alloc __a)
-        { __shared_ptr(__p, __d, std::move(__a)).swap(*this); }
+        { __shared_ptr(__p, std::move(__d), std::move(__a)).swap(*this); }
 
       element_type*
       get() const noexcept
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/lwg2802.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/lwg2802.cc
new file mode 100644
index 0000000..c69b378
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/lwg2802.cc
@@ -0,0 +1,51 @@ 
+// Copyright (C) 2017 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do compile { target c++11 } }
+
+#include <memory>
+
+// LWG 2802. shared_ptr constructor requirements for a deleter
+
+struct D
+{
+  D() { }
+  D(D&&) { }
+  void operator()(int* p) const { delete p; }
+};
+
+std::allocator<int> a;
+
+std::shared_ptr<int> s1((int*)nullptr, D());
+std::shared_ptr<int> s2((int*)nullptr, D(), a);
+std::shared_ptr<int> s3(nullptr, D());
+std::shared_ptr<int> s4(nullptr, D(), a);
+
+void test01()
+{
+  s1.reset((int*)nullptr, D());
+  s1.reset((int*)nullptr, D(), a);
+}
+
+struct D2 final
+{
+  D2() { }
+  D2(D2&&) { }
+  void operator()(int* p) const { delete p; }
+};
+
+std::shared_ptr<int> s5(nullptr, D2());