Date: Thu, 29 Oct 2020 18:12:33 +0000
Hi,
Consider the following code:
#include <memory>
```
struct Base {};
struct Derived : Base {};
void foo() {
std::unique_ptr<Base> ptr = std::make_unique<Derived>();
}
```
Calling `foo` is undefined behavior, since Base doesn't have a virtual
destructor. No compiler seems to warn https://godbolt.org/z/xM53P5.
In fact these kind of misusage of unique_ptr could be detected and
disallowed at the point of conversion, if std::default_delete didn't
have a too generous converting constructor template.
_Proposal_
Limit std::default_delete's converting constructor template to only
participate in overload resolution when it's safe to delete the
resulting pointer.
Possible implementation:
template <class T>
struct default_delete {
constexpr default_delete() noexcept = default;
template <std::convertible_to<T> U>
requires (std::derived_from<U, T>
&& std::has_virtual_destructor_v<T>)
|| std::same_as<std::remove_cv_t<U>, std::remove_cv_t<T>>
default_delete(const default_delete<U>&) noexcept {}
void operator()(T* ptr) const
requires requires{ sizeof(T); }
{
delete ptr;
}
};
_Impact on writing code_
After the removal if users don't use unique_ptr's pointer constructors
or its `reset()` member function they can be sure that the call to
~unique_ptr() will not result in UB.
I plan to write this paper after I get some feedback. I got
positive comments on the cpplang slack.
Regards,
Lénárd Szolnoki
Consider the following code:
#include <memory>
```
struct Base {};
struct Derived : Base {};
void foo() {
std::unique_ptr<Base> ptr = std::make_unique<Derived>();
}
```
Calling `foo` is undefined behavior, since Base doesn't have a virtual
destructor. No compiler seems to warn https://godbolt.org/z/xM53P5.
In fact these kind of misusage of unique_ptr could be detected and
disallowed at the point of conversion, if std::default_delete didn't
have a too generous converting constructor template.
_Proposal_
Limit std::default_delete's converting constructor template to only
participate in overload resolution when it's safe to delete the
resulting pointer.
Possible implementation:
template <class T>
struct default_delete {
constexpr default_delete() noexcept = default;
template <std::convertible_to<T> U>
requires (std::derived_from<U, T>
&& std::has_virtual_destructor_v<T>)
|| std::same_as<std::remove_cv_t<U>, std::remove_cv_t<T>>
default_delete(const default_delete<U>&) noexcept {}
void operator()(T* ptr) const
requires requires{ sizeof(T); }
{
delete ptr;
}
};
_Impact on writing code_
After the removal if users don't use unique_ptr's pointer constructors
or its `reset()` member function they can be sure that the call to
~unique_ptr() will not result in UB.
I plan to write this paper after I get some feedback. I got
positive comments on the cpplang slack.
Regards,
Lénárd Szolnoki
Received on 2020-10-29 13:12:43