libstdc++: Avoid forming T* in unique_ptr(auto_ptr<U>&&) constraints [PR116529]

PR 116529 shows that std::unique_ptr<X&, D> is currently unusable
because the constructor taking std::auto_ptr (which is a non-standard
extension since C++17) tries to form the invalid type X&* during
overload resolution. We can use the `pointer` type in the constructor
constraints, instead of trying to form an invalid type. The
std::auto_ptr constructor can never actually match for the case where
element_type is a reference, so we just need it to produce a
substitution failure instead of being ill-formed.

LWG 4144 might make std::unique_ptr<X&, D> ill-formed, which would
invalidate this new test. We would have to remove this test in that
case. Using `pointer` in the constructor from std::auto_ptr would not be
needed to support the std::unique_ptr<X&, D> case, but would not cause
any harm either.

libstdc++-v3/ChangeLog:

	PR libstdc++/116529
	* include/bits/unique_ptr.h (unique_ptr(auto_ptr<U>&&)):
	Use pointer instead of T*.
	* testsuite/20_util/unique_ptr/creation/116529.cc: New test.
This commit is contained in:
Jonathan Wakely 2024-08-29 13:47:15 +01:00 committed by Jonathan Wakely
parent 8230922230
commit a001d51505
No known key found for this signature in database
2 changed files with 38 additions and 2 deletions

View file

@ -379,8 +379,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
/// Converting constructor from @c auto_ptr
template<typename _Up, typename = _Require<
is_convertible<_Up*, _Tp*>, is_same<_Dp, default_delete<_Tp>>>>
template<typename _Up,
typename = _Require<is_convertible<_Up*, pointer>,
is_same<_Dp, default_delete<_Tp>>>>
unique_ptr(auto_ptr<_Up>&& __u) noexcept;
#pragma GCC diagnostic pop
#endif

View file

@ -0,0 +1,35 @@
// { dg-do run { target c++11 } }
// Bug libstdc++/116529 - Construction of unique_ptr with reference type
// is rejected because of auto_ptr constructor
#include <memory>
#include <testsuite_hooks.h>
int count = 0;
struct X
{
~X() { ++count; }
};
struct deleter : std::default_delete<X>
{
using pointer = X*;
};
void
test01()
{
{
std::unique_ptr<X&, deleter> up(new X);
// { dg-bogus "forming pointer to reference" "" { target *-*-* } 0 }
VERIFY( count == 0 );
}
VERIFY( count == 1 );
}
int main()
{
test01();
}