为什么隐式调用析构函数?
作为最小情况,我有一个模板堆积的仅移动值类模板。我不明白的是,为什么只使用其noexcept
move-constructor的函数似乎想使用其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>()
}
asues
<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
我理解为什么此翻译单元不能调用〜move> 〜move_only_heap_value&lt因为
s
是不完整的),但是有什么要说的是什么?如果我改为定义s
,但请将其c'tor and d'tor nordection保留,我会按预期获得链接错误,但我看不到d't tor在大会中呼叫,所以为什么会这样认为需要实例化吗?
一个更简单但可能略有不同的情况:
#include <utility>
struct Indestructible {
Indestructible() = default;
Indestructible(Indestructible&& x) noexcept = default;
~Indestructible() = delete;
};
Indestructible foo(Indestructible&& x) {
return std::move(x);
}
生产
<source>: In function 'Indestructible foo(Indestructible&&)':
<source>:10:21: error: use of deleted function 'Indestructible::~Indestructible()'
10 | return std::move(x);
https://godbolt.org.org.org.org/z/mbkhqddxd 但是我担心这是不同的,因为也许返回的结果具有自动存储持续时间,因此将来必须在某个序列上破坏,但不可破坏,而原始版本可能会破坏,只是在此翻译单元中。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您可以在此处
删除
指向该类型对象的指针。这将调用析构函数,并且必须在此时定义类型。模板的
~move_only_heap_value
又已被实例化,因为您定义了一个返回该模板实例的对象的函数。您可以删除此析构函数的定义,它将编译。您可以在定义
S
的其他地方定义它。仅仅因为某些东西不会在程序集中结束,并不一定意味着它不需要被明确定义。
PS
if (ptr) delete ptr;
总是可以简化为delete ptr;
条件是多余的。You
delete
a pointer to an object of that type here.That invokes the destructor, and the type must be defined at that point.
~move_only_heap_value
of the template in turn has been instantiated because you have defined a function that returns an object of that template instance.You can remove the definition of this destructor, and it will compile. You can define it elsewhere in a place where
S
is defined.Just because something won't end up in the assembly, doesn't necessarily mean that it won't need to be well-defined.
P.S.
if (ptr) delete ptr;
can always be simplified intodelete ptr;
The condition is redundant.