Related?: Why is the destructor called for an object that is not deleted?
As a minimal case, I have a templated heap-allocated move-only value class template. What I don't understand is why a function that just uses its noexcept
move-constructor seems to want to use its d'tor:
#include <cassert>
#include <memory>
// Roughly a unique_ptr:
template <typename T>
class move_only_heap_value {
T* ptr;
public:
move_only_heap_value() : ptr(new T()) {}
move_only_heap_value(move_only_heap_value&& x) noexcept : ptr(x.ptr) { x.ptr = nullptr; }
// ...
T& operator*() { assert(ptr); return *ptr; }
const T& operator*() const { assert(ptr); return *ptr; }
// ...
~move_only_heap_value() {
if (ptr) {
delete ptr;
}
}
};
//struct S { S(); ~S(); }; // I don't see ~S() called anywhere.
struct S;
move_only_heap_value<S> foo(move_only_heap_value<S>&& x) {
return std::move(x); // Error here due to missing ~move_only_heap_value<S>()
}
produces
<source>: In instantiation of 'move_only_heap_value<T>::~move_only_heap_value() [with T = S]':
<source>:27:21: required from here
<source>:18:13: error: possible problem detected in invocation of 'operator delete' [-Werror=delete-incomplete]
18 | delete ptr;
| ^~~~~~~~~~
<source>:18:20: error: invalid use of incomplete type 'struct S' [-Werror]
18 | delete ptr;
| ^~~
<source>:24:8: note: forward declaration of 'struct S'
24 | struct S;
| ^
<source>:18:13: note: neither the destructor nor the class-specific 'operator delete' will be called, even if they are declared when the class is defined
18 | delete ptr;
| ^~~~~~~~~~
cc1plus: all warnings being treated as errors
ASM generation compiler returned: 1
<source>: In instantiation of 'move_only_heap_value<T>::~move_only_heap_value() [with T = S]':
<source>:27:21: required from here
<source>:18:13: error: possible problem detected in invocation of 'operator delete' [-Werror=delete-incomplete]
18 | delete ptr;
| ^~~~~~~~~~
<source>:18:20: error: invalid use of incomplete type 'struct S' [-Werror]
18 | delete ptr;
| ^~~
<source>:24:8: note: forward declaration of 'struct S'
24 | struct S;
| ^
<source>:18:13: note: neither the destructor nor the class-specific 'operator delete' will be called, even if they are declared when the class is defined
18 | delete ptr;
| ^~~~~~~~~~
cc1plus: all warnings being treated as errors
Execution build compiler returned: 1
https://godbolt.org/z/KoMrcM1n4
I understand why this translation unit can't call ~move_only_heap_value<S>()
(because S
is incomplete), but what possess it to want to call that? If I instead define S
but leave its c'tor and d'tor undefined, I get a link error as expected but I don't see the d'tor called in the assembly, so why does it think it needs to instantiate it?
A simpler case, but possibly subtly different:
#include <utility>
struct Indestructible {
Indestructible() = default;
Indestructible(Indestructible&& x) noexcept = default;
~Indestructible() = delete;
};
Indestructible foo(Indestructible&& x) {
return std::move(x);
}
produces
<source>: In function 'Indestructible foo(Indestructible&&)':
<source>:10:21: error: use of deleted function 'Indestructible::~Indestructible()'
10 | return std::move(x);
https://godbolt.org/z/MbKhqddxd but I worry that's different since maybe the returned result has automatic storage duration and so has to be destructed at some sequence point in the future but is not destructible, whereas the original version can be destructed, just not in this translation unit.
&& x` by (rvalue) reference and so it's moved-from self lives on past the function call for the caller to deal with destructing?– Ben Apr 07 '22 at 15:01