Implement LWG 2905 changes to constrain unique_ptr constructors
LWG DR 2905 says that is_constructible_v<unique_ptr<P, D>, P, D const &> should be false when D is not copy constructible. This commit implements the changes from the DR and simplifies the signatures as per https://github.com/cplusplus/draft/issues/1530 * 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. From-SVN: r264206
This commit is contained in:
parent
9356a18eb4
commit
86fc6ec9f3
|
@ -1,3 +1,22 @@
|
||||||
|
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>
|
2018-09-05 Jonathan Wakely <jwakely@redhat.com>
|
||||||
|
|
||||||
* libsupc++/cxxabi.h (__cxa_demangle): Clarify doxygen comment.
|
* libsupc++/cxxabi.h (__cxa_demangle): Clarify doxygen comment.
|
||||||
|
|
|
@ -139,6 +139,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
|
|
||||||
using pointer = typename _Ptr<_Tp, _Dp>::type;
|
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() = default;
|
||||||
__uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }
|
__uniq_ptr_impl(pointer __p) : _M_t() { _M_ptr() = __p; }
|
||||||
|
|
||||||
|
@ -159,9 +165,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
template <typename _Tp, typename _Dp = default_delete<_Tp>>
|
template <typename _Tp, typename _Dp = default_delete<_Tp>>
|
||||||
class unique_ptr
|
class unique_ptr
|
||||||
{
|
{
|
||||||
template <class _Up>
|
template <typename _Up>
|
||||||
using _DeleterConstraint =
|
using _DeleterConstraint =
|
||||||
typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;
|
typename __uniq_ptr_impl<_Tp, _Up>::_DeleterConstraint::type;
|
||||||
|
|
||||||
__uniq_ptr_impl<_Tp, _Dp> _M_t;
|
__uniq_ptr_impl<_Tp, _Dp> _M_t;
|
||||||
|
|
||||||
|
@ -170,6 +176,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
using element_type = _Tp;
|
using element_type = _Tp;
|
||||||
using deleter_type = _Dp;
|
using deleter_type = _Dp;
|
||||||
|
|
||||||
|
private:
|
||||||
// helper template for detecting a safe conversion from another
|
// helper template for detecting a safe conversion from another
|
||||||
// unique_ptr
|
// unique_ptr
|
||||||
template<typename _Up, typename _Ep>
|
template<typename _Up, typename _Ep>
|
||||||
|
@ -183,11 +190,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
public:
|
||||||
// Constructors.
|
// Constructors.
|
||||||
|
|
||||||
/// Default constructor, creates a unique_ptr that owns nothing.
|
/// Default constructor, creates a unique_ptr that owns nothing.
|
||||||
template <typename _Up = _Dp,
|
template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
|
||||||
typename = _DeleterConstraint<_Up>>
|
|
||||||
constexpr unique_ptr() noexcept
|
constexpr unique_ptr() noexcept
|
||||||
: _M_t()
|
: _M_t()
|
||||||
{ }
|
{ }
|
||||||
|
@ -198,8 +205,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
*
|
*
|
||||||
* The deleter will be value-initialized.
|
* The deleter will be value-initialized.
|
||||||
*/
|
*/
|
||||||
template <typename _Up = _Dp,
|
template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
|
||||||
typename = _DeleterConstraint<_Up>>
|
|
||||||
explicit
|
explicit
|
||||||
unique_ptr(pointer __p) noexcept
|
unique_ptr(pointer __p) noexcept
|
||||||
: _M_t(__p)
|
: _M_t(__p)
|
||||||
|
@ -212,27 +218,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
*
|
*
|
||||||
* The deleter will be initialized with @p __d
|
* The deleter will be initialized with @p __d
|
||||||
*/
|
*/
|
||||||
unique_ptr(pointer __p,
|
template<typename _Del = deleter_type,
|
||||||
typename conditional<is_reference<deleter_type>::value,
|
typename = _Require<is_copy_constructible<_Del>>>
|
||||||
deleter_type, const deleter_type&>::type __d) noexcept
|
unique_ptr(pointer __p, const deleter_type& __d) noexcept
|
||||||
: _M_t(__p, __d) { }
|
: _M_t(__p, __d) { }
|
||||||
|
|
||||||
/** Takes ownership of a pointer.
|
/** Takes ownership of a pointer.
|
||||||
*
|
*
|
||||||
* @param __p A pointer to an object of @c element_type
|
* @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)
|
* The deleter will be initialized with @p std::move(__d)
|
||||||
*/
|
*/
|
||||||
unique_ptr(pointer __p,
|
template<typename _Del = deleter_type,
|
||||||
typename remove_reference<deleter_type>::type&& __d) noexcept
|
typename = _Require<is_move_constructible<_Del>>>
|
||||||
: _M_t(std::move(__p), std::move(__d))
|
unique_ptr(pointer __p,
|
||||||
{ static_assert(!std::is_reference<deleter_type>::value,
|
__enable_if_t<!is_lvalue_reference<_Del>::value,
|
||||||
"rvalue deleter bound to reference"); }
|
_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.
|
/// Creates a unique_ptr that owns nothing.
|
||||||
template <typename _Up = _Dp,
|
template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
|
||||||
typename = _DeleterConstraint<_Up>>
|
|
||||||
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
|
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
|
||||||
|
|
||||||
// Move constructors.
|
// Move constructors.
|
||||||
|
@ -454,8 +467,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
// Constructors.
|
// Constructors.
|
||||||
|
|
||||||
/// Default constructor, creates a unique_ptr that owns nothing.
|
/// Default constructor, creates a unique_ptr that owns nothing.
|
||||||
template <typename _Up = _Dp,
|
template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
|
||||||
typename = _DeleterConstraint<_Up>>
|
|
||||||
constexpr unique_ptr() noexcept
|
constexpr unique_ptr() noexcept
|
||||||
: _M_t()
|
: _M_t()
|
||||||
{ }
|
{ }
|
||||||
|
@ -485,12 +497,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
*
|
*
|
||||||
* The deleter will be initialized with @p __d
|
* The deleter will be initialized with @p __d
|
||||||
*/
|
*/
|
||||||
template<typename _Up,
|
template<typename _Up, typename _Del = deleter_type,
|
||||||
typename = typename enable_if<
|
typename = _Require<__safe_conversion_raw<_Up>,
|
||||||
__safe_conversion_raw<_Up>::value, bool>::type>
|
is_copy_constructible<_Del>>>
|
||||||
unique_ptr(_Up __p,
|
unique_ptr(_Up __p, const deleter_type& __d) noexcept
|
||||||
typename conditional<is_reference<deleter_type>::value,
|
|
||||||
deleter_type, const deleter_type&>::type __d) noexcept
|
|
||||||
: _M_t(__p, __d) { }
|
: _M_t(__p, __d) { }
|
||||||
|
|
||||||
/** Takes ownership of a pointer.
|
/** Takes ownership of a pointer.
|
||||||
|
@ -501,22 +511,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||||
*
|
*
|
||||||
* The deleter will be initialized with @p std::move(__d)
|
* The deleter will be initialized with @p std::move(__d)
|
||||||
*/
|
*/
|
||||||
template<typename _Up,
|
template<typename _Up, typename _Del = deleter_type,
|
||||||
typename = typename enable_if<
|
typename = _Require<__safe_conversion_raw<_Up>,
|
||||||
__safe_conversion_raw<_Up>::value, bool>::type>
|
is_move_constructible<_Del>>>
|
||||||
unique_ptr(_Up __p, typename
|
unique_ptr(_Up __p,
|
||||||
remove_reference<deleter_type>::type&& __d) noexcept
|
__enable_if_t<!is_lvalue_reference<_Del>::value,
|
||||||
: _M_t(std::move(__p), std::move(__d))
|
_Del&&> __d) noexcept
|
||||||
{ static_assert(!is_reference<deleter_type>::value,
|
: _M_t(std::move(__p), std::move(__d))
|
||||||
"rvalue deleter bound to reference"); }
|
{ }
|
||||||
|
|
||||||
|
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.
|
/// Move constructor.
|
||||||
unique_ptr(unique_ptr&& __u) noexcept
|
unique_ptr(unique_ptr&& __u) noexcept
|
||||||
: _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }
|
: _M_t(__u.release(), std::forward<deleter_type>(__u.get_deleter())) { }
|
||||||
|
|
||||||
/// Creates a unique_ptr that owns nothing.
|
/// Creates a unique_ptr that owns nothing.
|
||||||
template <typename _Up = _Dp,
|
template<typename _Del = _Dp, typename = _DeleterConstraint<_Del>>
|
||||||
typename = _DeleterConstraint<_Up>>
|
|
||||||
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
|
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr() { }
|
||||||
|
|
||||||
template<typename _Up, typename _Ep,
|
template<typename _Up, typename _Ep,
|
||||||
|
|
|
@ -42,10 +42,10 @@ void f()
|
||||||
std::unique_ptr<int, D&> ud(nullptr, d);
|
std::unique_ptr<int, D&> ud(nullptr, d);
|
||||||
ub = std::move(ud); // { dg-error "no match" }
|
ub = std::move(ud); // { dg-error "no match" }
|
||||||
ub2 = 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[], B&> uba(nullptr, b);
|
||||||
std::unique_ptr<int[], D&> uda(nullptr, d);
|
std::unique_ptr<int[], D&> uda(nullptr, d);
|
||||||
uba = std::move(uda); // { dg-error "no match" }
|
uba = std::move(uda); // { dg-error "no match" }
|
||||||
// { dg-error "no type" "" { target *-*-* } 566 }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// { dg-prune-output "no type" }
|
||||||
|
|
|
@ -39,7 +39,7 @@ test07()
|
||||||
std::unique_ptr<const A[]> cA3(p); // { dg-error "no matching function" }
|
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<volatile A[]> vA3(p); // { dg-error "no matching function" }
|
||||||
std::unique_ptr<const volatile A[]> cvA3(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>
|
template<typename T>
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
// 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>(), "" );
|
|
@ -21,13 +21,18 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
// Not swappable, and unique_ptr not swappable via the generic std::swap.
|
// 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;
|
void swap(C&, C&) = delete;
|
||||||
|
|
||||||
static_assert( !std::is_swappable_v<std::unique_ptr<int, C>> );
|
static_assert( !std::is_swappable_v<std::unique_ptr<int, C>> );
|
||||||
|
|
||||||
// Not swappable, and unique_ptr not swappable via the generic std::swap.
|
// 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>> );
|
static_assert( !std::is_swappable_v<std::unique_ptr<int, D>> );
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue