Constrain std::shared_ptr assignment and resetting

* include/bits/shared_ptr.h (_Assignable): New alias template.
	(shared_ptr::operator=(const shared_ptr<_Tp1>&))
	(shared_ptr::operator=(shared_ptr<_Tp1>&&))
	(shared_ptr::operator=(unique_ptr<_Tp1>&&)): Constrain with
	_Assignable.
	* include/bits/shared_ptr_base.h (_Assignable): New alias template.
	(__shared_ptr::operator=(const __shared_ptr<_Tp1>&))
	(__shared_ptr::operator=(__shared_ptr<_Tp1>&&))
	(__shared_ptr::operator=(unique_ptr<_Tp1>&&)): Constrain with
	_Assignable.
	(__shared_ptr::reset(_Tp1*), __shared_ptr::reset(_Tp1*, _Deleter))
	(__shared_ptr::reset(_Tp1*, _Deleter, _Alloc)): Constrain with
	_Convertible.
	* testsuite/20_util/shared_ptr/cons/43820_neg.cc: Change dg-error to
	match on any line.
	* testsuite/20_util/shared_ptr/cons/void_neg.cc: Likewise.
	* testsuite/20_util/shared_ptr/assign/sfinae.cc: New test.
	* testsuite/20_util/shared_ptr/assign/shared_ptr_neg.cc: Update
	expected errors. Remove unnecessary code.
	* testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc: New test.

From-SVN: r239898
This commit is contained in:
Jonathan Wakely 2016-08-31 17:57:20 +01:00 committed by Jonathan Wakely
parent e46d22a821
commit 7663cae227
8 changed files with 211 additions and 31 deletions

View File

@ -1,5 +1,26 @@
2016-08-31 Jonathan Wakely <jwakely@redhat.com>
* include/bits/shared_ptr.h (_Assignable): New alias template.
(shared_ptr::operator=(const shared_ptr<_Tp1>&))
(shared_ptr::operator=(shared_ptr<_Tp1>&&))
(shared_ptr::operator=(unique_ptr<_Tp1>&&)): Constrain with
_Assignable.
* include/bits/shared_ptr_base.h (_Assignable): New alias template.
(__shared_ptr::operator=(const __shared_ptr<_Tp1>&))
(__shared_ptr::operator=(__shared_ptr<_Tp1>&&))
(__shared_ptr::operator=(unique_ptr<_Tp1>&&)): Constrain with
_Assignable.
(__shared_ptr::reset(_Tp1*), __shared_ptr::reset(_Tp1*, _Deleter))
(__shared_ptr::reset(_Tp1*, _Deleter, _Alloc)): Constrain with
_Convertible.
* testsuite/20_util/shared_ptr/cons/43820_neg.cc: Change dg-error to
match on any line.
* testsuite/20_util/shared_ptr/cons/void_neg.cc: Likewise.
* testsuite/20_util/shared_ptr/assign/sfinae.cc: New test.
* testsuite/20_util/shared_ptr/assign/shared_ptr_neg.cc: Update
expected errors. Remove unnecessary code.
* testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc: New test.
* include/bits/stl_tree.h (_Rb_tree::operator=(_Rb_tree&&)): Move
comparison object.
* testsuite/23_containers/set/move_comparison.cc: New test.

View File

@ -93,8 +93,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
class shared_ptr : public __shared_ptr<_Tp>
{
template<typename _Ptr>
using _Convertible
= typename enable_if<is_convertible<_Ptr, _Tp*>::value>::type;
using _Convertible = typename
enable_if<is_convertible<_Ptr, _Tp*>::value>::type;
template<typename _Ptr>
using _Assignable = typename
enable_if<is_convertible<_Ptr, _Tp*>::value, shared_ptr&>::type;
public:
@ -276,7 +280,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
shared_ptr& operator=(const shared_ptr&) noexcept = default;
template<typename _Tp1>
shared_ptr&
_Assignable<_Tp1*>
operator=(const shared_ptr<_Tp1>& __r) noexcept
{
this->__shared_ptr<_Tp>::operator=(__r);
@ -301,7 +305,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<class _Tp1>
shared_ptr&
_Assignable<_Tp1*>
operator=(shared_ptr<_Tp1>&& __r) noexcept
{
this->__shared_ptr<_Tp>::operator=(std::move(__r));
@ -309,7 +313,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename _Tp1, typename _Del>
shared_ptr&
_Assignable<typename unique_ptr<_Tp1, _Del>::pointer>
operator=(std::unique_ptr<_Tp1, _Del>&& __r)
{
this->__shared_ptr<_Tp>::operator=(std::move(__r));

View File

@ -873,6 +873,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
using _Convertible
= typename enable_if<is_convertible<_Ptr, _Tp*>::value>::type;
template<typename _Ptr>
using _Assignable = typename
enable_if<is_convertible<_Ptr, _Tp*>::value, __shared_ptr&>::type;
public:
typedef _Tp element_type;
@ -983,7 +987,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
constexpr __shared_ptr(nullptr_t) noexcept : __shared_ptr() { }
template<typename _Tp1>
__shared_ptr&
_Assignable<_Tp1*>
operator=(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
{
_M_ptr = __r._M_ptr;
@ -1009,7 +1013,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<class _Tp1>
__shared_ptr&
_Assignable<_Tp1*>
operator=(__shared_ptr<_Tp1, _Lp>&& __r) noexcept
{
__shared_ptr(std::move(__r)).swap(*this);
@ -1017,7 +1021,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename _Tp1, typename _Del>
__shared_ptr&
_Assignable<typename unique_ptr<_Tp1, _Del>::pointer>
operator=(std::unique_ptr<_Tp1, _Del>&& __r)
{
__shared_ptr(std::move(__r)).swap(*this);
@ -1029,7 +1033,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ __shared_ptr().swap(*this); }
template<typename _Tp1>
void
_Convertible<_Tp1*>
reset(_Tp1* __p) // _Tp1 must be complete.
{
// Catch self-reset errors.
@ -1038,12 +1042,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename _Tp1, typename _Deleter>
void
_Convertible<_Tp1*>
reset(_Tp1* __p, _Deleter __d)
{ __shared_ptr(__p, __d).swap(*this); }
template<typename _Tp1, typename _Deleter, typename _Alloc>
void
_Convertible<_Tp1*>
reset(_Tp1* __p, _Deleter __d, _Alloc __a)
{ __shared_ptr(__p, __d, std::move(__a)).swap(*this); }

View File

@ -0,0 +1,75 @@
// Copyright (C) 2016 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 From>
constexpr bool can_assign()
{ return std::is_assignable<std::shared_ptr<T>, From>::value; }
struct Base { };
struct Derived : Base { };
// Positive cases:
static_assert( can_assign<const void, const std::shared_ptr<void>&>(),
"void* convertible to const void*");
static_assert( can_assign<const void, std::shared_ptr<void>&&>(),
"void* convertible to const void*");
static_assert( can_assign<const int, std::shared_ptr<int>>(),
"int* convertible to const int*");
static_assert( can_assign<Base, std::shared_ptr<Derived>>(),
"Derived* convertible to Base*");
static_assert( can_assign<const Base, std::shared_ptr<Derived>>(),
"Derived* convertible to const Base*");
// Negative cases:
static_assert( !can_assign<int, const std::shared_ptr<void>&>(),
"void* not convertible to int*");
static_assert( !can_assign<int, std::shared_ptr<void>&&>(),
"void* not convertible to int*");
static_assert( !can_assign<int, const std::shared_ptr<const int>&>(),
"const int* not convertible to int*");
static_assert( !can_assign<int, std::shared_ptr<const int>&&>(),
"const int* not convertible to int*");
static_assert( !can_assign<int, const std::shared_ptr<long>&>(),
"long* not convertible to int*");
static_assert( !can_assign<int, std::shared_ptr<long>&&>(),
"long* not convertible to int*");
static_assert( !can_assign<int, std::unique_ptr<long>&&>(),
"unique_ptr<long>::pointer not convertible to int*");
static_assert( !can_assign<Derived, const std::shared_ptr<Base>&>(),
"Base* not convertible to Derived*");
static_assert( !can_assign<int, std::shared_ptr<long>&&>(),
"Base* not convertible to Derived*");
static_assert( !can_assign<Derived, std::unique_ptr<Base>&&>(),
"unique_ptr<Base>::pointer not convertible to Derived*");
struct Deleter {
using pointer = void*;
void operator()(pointer) const { }
};
static_assert( !can_assign<Derived, std::unique_ptr<Derived, Deleter>&&>(),
"unique_ptr<Derived, Deleter>::pointer not convertible to Derived*");

View File

@ -28,24 +28,10 @@ struct B { };
// 20.6.6.2.3 shared_ptr assignment [util.smartptr.shared.assign]
// Assignment from incompatible shared_ptr<Y>
int
void
test01()
{
bool test __attribute__((unused)) = true;
std::shared_ptr<A> a;
std::shared_ptr<B> b;
a = b; // { dg-error "here" }
return 0;
a = b; // { dg-error "no match" }
}
int
main()
{
test01();
return 0;
}
// { dg-error "In instantiation" "" { target *-*-* } 0 }
// { dg-error "cannot convert" "" { target *-*-* } 0 }
// { dg-error "required from" "" { target *-*-* } 0 }

View File

@ -32,8 +32,6 @@ void test01()
{
X* px = 0;
std::shared_ptr<X> p1(px); // { dg-error "here" }
// { dg-error "incomplete" "" { target *-*-* } 893 }
std::shared_ptr<X> p9(ap()); // { dg-error "here" }
// { dg-error "incomplete" "" { target *-*-* } 307 }
// { dg-error "incomplete" "" { target *-*-* } 0 }
}

View File

@ -24,5 +24,5 @@
void test01()
{
std::shared_ptr<void> p((void*)nullptr); // { dg-error "here" }
// { dg-error "incomplete" "" { target *-*-* } 892 }
// { dg-error "incomplete" "" { target *-*-* } 0 }
}

View File

@ -0,0 +1,92 @@
// Copyright (C) 2016 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 Args, typename = void>
struct resettable
: std::false_type
{ };
template<typename... T> struct type_list { };
template<typename T, typename... Args>
using reset_result
= decltype(std::shared_ptr<T>{}.reset(std::declval<Args>()...));
template<typename T, typename... Args>
struct resettable<T, type_list<Args...>, reset_result<T, Args...>>
: std::true_type
{ };
template<typename T, typename... Args>
constexpr bool can_reset()
{ return resettable<T, type_list<Args...>>::value; }
template<typename T>
struct Deleter {
void operator()(T*) const;
};
template<typename T>
using Alloc = std::allocator<T>;
struct Base { };
struct Derived : Base { };
// Positive cases:
static_assert( can_reset<const void, void*>(),
"void* convertible to const void*");
static_assert( can_reset<const int, int*>(),
"int* convertible to const int*");
static_assert( can_reset<Base, Derived*>(),
"Derived* convertible to Base*");
static_assert( can_reset<const Base, Derived*>(),
"Derived* convertible to const Base*");
// Negative cases:
static_assert( !can_reset<int, void*>(),
"void* not convertible to int*");
static_assert( !can_reset<int, void*, Deleter<int>>(),
"void* not convertible to int*");
static_assert( !can_reset<int, void*, Deleter<int>, Alloc<int>>(),
"void* not convertible to int*");
static_assert( !can_reset<int, const int*>(),
"const int* not convertible to int*");
static_assert( !can_reset<int, const int*, Deleter<int>>(),
"const int* not convertible to int*");
static_assert( !can_reset<int, const int*, Deleter<int>, Alloc<int>>(),
"const int* not convertible to int*");
static_assert( !can_reset<int, long*>(),
"long* not convertible to int*");
static_assert( !can_reset<int, long*, Deleter<int>>(),
"long* not convertible to int*");
static_assert( !can_reset<int, long*, Deleter<int>, Alloc<int>>(),
"long* not convertible to int*");
static_assert( !can_reset<Derived, Base*>(),
"Base* not convertible to Derived*");
static_assert( !can_reset<Derived, Base*, Deleter<int>>(),
"Base* not convertible to Derived*");
static_assert( !can_reset<Derived, Base*, Deleter<int>, Alloc<int>>(),
"Base* not convertible to Derived*");