+2018-09-11 Jonathan Wakely <jwakely@redhat.com>
+
+ Implement LWG 2905 changes to constrain unique_ptr constructors
+ * include/bits/unique_ptr.h (__uniq_ptr_impl): Add assertions to
+ check deleter type.
+ (unique_ptr::unique_ptr(pointer, const deleter_type&)): Add copy
+ constructible constraint.
+ (unique_ptr::unique_ptr(pointer, deleter_type&&)): Disable for
+ deleters of reference type and add move constructible constraint.
+ (unique_ptr::unique_ptr(pointer, remove_reference_t<deleter_type>&&)):
+ Disable for deleters of non-reference type. Define as deleted.
+ (unique_ptr<T[], D>): Likewise.
+ * testsuite/20_util/unique_ptr/assign/48635_neg.cc: Replace dg-error
+ directives with unstable line numbers with dg-prune-output.
+ * testsuite/20_util/unique_ptr/cons/cv_qual_neg.cc: Likewise.
+ * testsuite/20_util/unique_ptr/cons/lwg2905.cc: New test.
+ * testsuite/20_util/unique_ptr/specialized_algorithms/swap_cxx17.cc:
+ Make deleter types invocable.
+
2018-09-05 Jonathan Wakely <jwakely@redhat.com>
* libsupc++/cxxabi.h (__cxa_demangle): Clarify doxygen comment.
using pointer = typename _Ptr<_Tp, _Dp>::type;
+ static_assert( !is_rvalue_reference<_Dp>::value,
+ "unique_ptr's deleter type must be a function object type"
+ " or an lvalue reference type" );
+ static_assert( __is_invocable<_Dp&, pointer&>::value,
+ "unique_ptr's deleter must be invocable with a pointer" );
+
__uniq_ptr_impl() = default;
__uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }
template <typename _Tp, typename _Dp = default_delete<_Tp>>
class unique_ptr
{
- template <class _Up>
- using _DeleterConstraint =
- typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;
+ template <typename _Up>
+ using _DeleterConstraint =
+ typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;
__uniq_ptr_impl<_Tp, _Dp> _M_t;
using element_type = _Tp;
using deleter_type = _Dp;
+ private:
// helper template for detecting a safe conversion from another
// unique_ptr
template<typename _Up, typename _Ep>
>
>;
+ public:
// Constructors.
/// Default constructor, creates a unique_ptr that owns nothing.
- template <typename _Up = _Dp,
- typename = _DeleterConstraint<_Up>>
+ template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
constexpr unique_ptr() noexcept
: _M_t()
{ }
*
* The deleter will be value-initialized.
*/
- template <typename _Up = _Dp,
- typename = _DeleterConstraint<_Up>>
+ template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
explicit
unique_ptr(pointer __p) noexcept
: _M_t(__p)
*
* The deleter will be initialized with @p __d
*/
- unique_ptr(pointer __p,
- typename conditional<is_reference<deleter_type>::value,
- deleter_type, const deleter_type&>::type __d) noexcept
- : _M_t(__p, __d) { }
+ template<typename _Del = deleter_type,
+ typename = _Require<is_copy_constructible<_Del>>>
+ unique_ptr(pointer __p, const deleter_type& __d) noexcept
+ : _M_t(__p, __d) { }
/** Takes ownership of a pointer.
*
* @param __p A pointer to an object of @c element_type
- * @param __d An rvalue reference to a deleter.
+ * @param __d An rvalue reference to a (non-reference) deleter.
*
* The deleter will be initialized with @p std::move(__d)
*/
- unique_ptr(pointer __p,
- typename remove_reference<deleter_type>::type&& __d) noexcept
- : _M_t(std::move(__p), std::move(__d))
- { static_assert(!std::is_reference<deleter_type>::value,
- "rvalue deleter bound to reference"); }
+ template<typename _Del = deleter_type,
+ typename = _Require<is_move_constructible<_Del>>>
+ unique_ptr(pointer __p,
+ __enable_if_t<!is_lvalue_reference<_Del>::value,
+ _Del&&> __d) noexcept
+ : _M_t(__p, std::move(__d))
+ { }
+
+ template<typename _Del = deleter_type,
+ typename _DelUnref = typename remove_reference<_Del>::type>
+ unique_ptr(pointer,
+ __enable_if_t<is_lvalue_reference<_Del>::value,
+ _DelUnref&&>) = delete;
/// Creates a unique_ptr that owns nothing.
- template <typename _Up = _Dp,
- typename = _DeleterConstraint<_Up>>
+ template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
// Move constructors.
// Constructors.
/// Default constructor, creates a unique_ptr that owns nothing.
- template <typename _Up = _Dp,
- typename = _DeleterConstraint<_Up>>
+ template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
constexpr unique_ptr() noexcept
: _M_t()
{ }
*
* The deleter will be initialized with @p __d
*/
- template<typename _Up,
- typename = typename enable_if<
- __safe_conversion_raw<_Up>::value, bool>::type>
- unique_ptr(_Up __p,
- typename conditional<is_reference<deleter_type>::value,
- deleter_type, const deleter_type&>::type __d) noexcept
+ template<typename _Up, typename _Del = deleter_type,
+ typename = _Require<__safe_conversion_raw<_Up>,
+ is_copy_constructible<_Del>>>
+ unique_ptr(_Up __p, const deleter_type& __d) noexcept
: _M_t(__p, __d) { }
/** Takes ownership of a pointer.
*
* The deleter will be initialized with @p std::move(__d)
*/
- template<typename _Up,
- typename = typename enable_if<
- __safe_conversion_raw<_Up>::value, bool>::type>
- unique_ptr(_Up __p, typename
- remove_reference<deleter_type>::type&& __d) noexcept
- : _M_t(std::move(__p), std::move(__d))
- { static_assert(!is_reference<deleter_type>::value,
- "rvalue deleter bound to reference"); }
+ template<typename _Up, typename _Del = deleter_type,
+ typename = _Require<__safe_conversion_raw<_Up>,
+ is_move_constructible<_Del>>>
+ unique_ptr(_Up __p,
+ __enable_if_t<!is_lvalue_reference<_Del>::value,
+ _Del&&> __d) noexcept
+ : _M_t(std::move(__p), std::move(__d))
+ { }
+
+ template<typename _Up, typename _Del = deleter_type,
+ typename _DelUnref = typename remove_reference<_Del>::type,
+ typename = _Require<__safe_conversion_raw<_Up>>>
+ unique_ptr(_Up,
+ __enable_if_t<is_lvalue_reference<_Del>::value,
+ _DelUnref&&>) = delete;
/// Move constructor.
unique_ptr(unique_ptr&& __u) noexcept
: _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }
/// Creates a unique_ptr that owns nothing.
- template <typename _Up = _Dp,
- typename = _DeleterConstraint<_Up>>
+ template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
template<typename _Up, typename _Ep,
std::unique_ptr<int, D&> ud(nullptr, d);
ub = std::move(ud); // { dg-error "no match" }
ub2 = ud; // { dg-error "no match" }
-// { dg-error "no type" "" { target *-*-* } 307 }
std::unique_ptr<int[], B&> uba(nullptr, b);
std::unique_ptr<int[], D&> uda(nullptr, d);
uba = std::move(uda); // { dg-error "no match" }
-// { dg-error "no type" "" { target *-*-* } 566 }
}
+
+// { dg-prune-output "no type" }
std::unique_ptr<const A[]> cA3(p); // { dg-error "no matching function" }
std::unique_ptr<volatile A[]> vA3(p); // { dg-error "no matching function" }
std::unique_ptr<const volatile A[]> cvA3(p); // { dg-error "no matching function" }
- // { dg-error "no type" "" { target *-*-* } 473 }
+ // { dg-prune-output "no type" }
}
template<typename T>
--- /dev/null
+// 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>
+
+template<typename T, typename D, typename P, typename E>
+constexpr bool check()
+{ return std::is_constructible<std::unique_ptr<T, D>, P, E>::value; }
+
+struct Del { void operator()(void*) const { } };
+
+static_assert( ! check<int, Del&, int*, Del>(), "" );
+static_assert( check<int, Del&, int*, Del&>(), "" );
+static_assert( check<int, const Del&, int*, Del&>(), "" );
+static_assert( ! check<int, Del&, int*, const Del&>(), "" );
+static_assert( check<int, Del, int*, const Del&>(), "" );
+static_assert( check<int, Del, int*, Del>(), "" );
+
+static_assert( ! check<int[], Del&, int*, Del>(), "" );
+static_assert( check<int[], Del&, int*, Del&>(), "" );
+static_assert( check<int[], const Del&, int*, Del&>(), "" );
+static_assert( ! check<int[], Del&, int*, const Del&>(), "" );
+static_assert( check<int[], Del, int*, const Del&>(), "" );
+static_assert( check<int[], Del, int*, Del>(), "" );
+
+struct DelNoCopy {
+ DelNoCopy() = default;
+ DelNoCopy(const DelNoCopy&) = delete;
+ DelNoCopy(DelNoCopy&&) = default;
+ void operator()(void*) const { }
+};
+
+static_assert( ! check<int, DelNoCopy&, int*, DelNoCopy>(), "" );
+static_assert( check<int, DelNoCopy&, int*, DelNoCopy&>(), "" );
+static_assert( check<int, const DelNoCopy&, int*, DelNoCopy&>(), "" );
+static_assert( ! check<int, DelNoCopy&, int*, const DelNoCopy&>(), "" );
+static_assert( ! check<int, DelNoCopy, int*, const DelNoCopy&>(), "" );
+static_assert( check<int, DelNoCopy, int*, DelNoCopy>(), "" );
+
+static_assert( ! check<int[], DelNoCopy&, int*, DelNoCopy>(), "" );
+static_assert( check<int[], DelNoCopy&, int*, DelNoCopy&>(), "" );
+static_assert( check<int[], const DelNoCopy&, int*, DelNoCopy&>(), "" );
+static_assert( ! check<int[], DelNoCopy&, int*, const DelNoCopy&>(), "" );
+static_assert( ! check<int[], DelNoCopy, int*, const DelNoCopy&>(), "" );
+static_assert( check<int[], DelNoCopy, int*, DelNoCopy>(), "" );
+
+struct Base { virtual ~Base() { } };
+struct Derived : Base { };
+
+static_assert( ! check<Base[], Del&, Base*, Del>(), "" );
+static_assert( check<Base[], Del&, Base*, Del&>(), "" );
+static_assert( check<Base[], const Del&, Base*, Del&>(), "" );
+static_assert( ! check<Base[], Del&, Base*, const Del&>(), "" );
+static_assert( check<Base[], Del, Base*, const Del&>(), "" );
+static_assert( check<Base[], Del, Base*, Del>(), "" );
+
+static_assert( ! check<Base[], Del&, Derived*, Del>(), "" );
+static_assert( ! check<Base[], Del&, Derived*, Del&>(), "" );
+static_assert( ! check<Base[], const Del&, Derived*, Del&>(), "" );
+static_assert( ! check<Base[], Del&, Derived*, const Del&>(), "" );
+static_assert( ! check<Base[], Del, Derived*, const Del&>(), "" );
+static_assert( ! check<Base[], Del, Derived*, Del>(), "" );
#include <memory>
// Not swappable, and unique_ptr not swappable via the generic std::swap.
-struct C { };
+struct C {
+ void operator()(void*) const { }
+};
void swap(C&, C&) = delete;
static_assert( !std::is_swappable_v<std::unique_ptr<int, C>> );
// Not swappable, and unique_ptr not swappable via the generic std::swap.
-struct D { D(D&&) = delete; };
+struct D {
+ D(D&&) = delete;
+ void operator()(void*) const { }
+};
static_assert( !std::is_swappable_v<std::unique_ptr<int, D>> );