libstdc++: Prevent dangling references in std::unique_ptr::operator*
LWG 4148 (approved in Wrocław, November 2024) makes it ill-formed to dereference a std::unique_ptr if that would return a dangling reference. That can happen with a custom pointer type and a const-qualified element_type, such that std::add_lvalue_reference_t<element_type> is a reference-to-const that could bind to a short-lived temporary. In C++26 the compiler diagnoses this as an error anyway: bits/unique_ptr.h:457:16: error: returning reference to temporary [-Wreturn-local-addr] But that can be disabled with -Wno-return-local-addr so the static_assert ensures it is enforced consistently. libstdc++-v3/ChangeLog: * include/bits/unique_ptr.h (unique_ptr::operator*): Add static_assert to check for dangling reference, as per LWG 4148. * testsuite/20_util/unique_ptr/lwg4148.cc: New test. Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
This commit is contained in:
parent
04815ae0a2
commit
8ff7ff1a06
2 changed files with 39 additions and 0 deletions
|
@ -445,6 +445,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|||
typename add_lvalue_reference<element_type>::type
|
||||
operator*() const noexcept(noexcept(*std::declval<pointer>()))
|
||||
{
|
||||
#if _GLIBCXX_USE_BUILTIN_TRAIT(__reference_converts_from_temporary)
|
||||
// _GLIBCXX_RESOLVE_LIB_DEFECTS
|
||||
// 4148. unique_ptr::operator* should not allow dangling references
|
||||
using _ResT = typename add_lvalue_reference<element_type>::type;
|
||||
using _DerefT = decltype(*get());
|
||||
static_assert(!__reference_converts_from_temporary(_ResT, _DerefT),
|
||||
"operator* must not return a dangling reference");
|
||||
#endif
|
||||
__glibcxx_assert(get() != pointer());
|
||||
return *get();
|
||||
}
|
||||
|
|
31
libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc
Normal file
31
libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc
Normal file
|
@ -0,0 +1,31 @@
|
|||
// { dg-do compile { target c++11 } }
|
||||
|
||||
// LWG 4148. unique_ptr::operator* should not allow dangling references
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct pointer
|
||||
{
|
||||
pointer() { }
|
||||
pointer(std::nullptr_t) { }
|
||||
int operator*() const { return 0; }
|
||||
bool operator==(pointer) const { return true; }
|
||||
bool operator==(std::nullptr_t) const { return false; }
|
||||
#ifndef __cpp_lib_three_way_comparison
|
||||
bool operator!=(pointer) const { return false; }
|
||||
bool operator!=(std::nullptr_t) const { return true; }
|
||||
#endif
|
||||
};
|
||||
|
||||
struct Deleter
|
||||
{
|
||||
using pointer = ::pointer;
|
||||
void operator()(pointer) const { }
|
||||
};
|
||||
|
||||
std::unique_ptr<const int, Deleter> up;
|
||||
int i = *up; // { dg-error "here" }
|
||||
// { dg-error "dangling reference" "" { target *-*-* } 0 }
|
||||
|
||||
// { dg-warning "returning reference to temporary" "" { target c++23_down } 0 }
|
||||
// { dg-error "returning reference to temporary" "" { target c++26 } 0 }
|
Loading…
Add table
Reference in a new issue