The standard subtly requires us to constrain the assignment operators, due to "Effects: Equivalent to [a constrained expression]".
Constraining reset() is not required, but a private discussion started by Alisdair Meredith has convinced me that it's useful (and apparently libc++ already does this). * 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. Tested powerpc64le-linux, committed to trunk.
commit 1c8d8d7ddb83f6205137e4e62266f7c361158ef3 Author: Jonathan Wakely <jwak...@redhat.com> Date: Wed Aug 31 14:15:38 2016 +0100 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. diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h index 747b09a..b2523b8 100644 --- a/libstdc++-v3/include/bits/shared_ptr.h +++ b/libstdc++-v3/include/bits/shared_ptr.h @@ -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)); diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h index 60b825c..4ae2668 100644 --- a/libstdc++-v3/include/bits/shared_ptr_base.h +++ b/libstdc++-v3/include/bits/shared_ptr_base.h @@ -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); } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/sfinae.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/sfinae.cc new file mode 100644 index 0000000..d79af04 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/sfinae.cc @@ -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*"); diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/shared_ptr_neg.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/shared_ptr_neg.cc index 751495a..96f07b5 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/shared_ptr_neg.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/shared_ptr_neg.cc @@ -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 } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc index 530256a..c58c842 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc @@ -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 } } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/void_neg.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/void_neg.cc index f77ddec..0cadf25 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/void_neg.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/void_neg.cc @@ -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 } } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc new file mode 100644 index 0000000..f75530f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/modifiers/reset_sfinae.cc @@ -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*");