我写了一个类似于std::unique_ptr
的类,但是对象是用placement new构造的。本质上,这个类的析构函数只调用所包含对象的析构函数,而不释放内存。(我现在知道,这可以用unique_ptr
和一个自定义的删除器来完成,但我仍然想知道为什么我得到奇怪的结果。
当我使用GCC和任何优化(-O 1和更高)编译它时,最后一个Assert失败。如果我将优化设置为-O 0,错误就消失了。
#include <cassert>
#include <memory>
template<typename T>
class unique_placed_ptr {
public:
template<typename... Args>
unique_placed_ptr(void * Address, Args && ... Arguments) {
m_Object = new (Address) T{std::forward<Args>(Arguments)...};
}
~unique_placed_ptr() {
m_Object->~T();
}
private:
T * m_Object;
};
template<typename T, typename... Args>
auto make_unique_placed_ptr(void * Address, Args && ... Arguments)
-> unique_placed_ptr<T> {
return unique_placed_ptr<T>{Address, std::forward<Args>(Arguments)...};
}
int g_Global = 0;
struct TestObject {
public:
TestObject() {
m_Local = 1;
}
~TestObject() {
m_Local = 2;
}
int m_Local;
};
int main() {
assert(g_Global == 0);
{ // frame for deconstruction
auto UniquePlacedPtr = make_unique_placed_ptr<TestObject>(&g_Global);
assert(g_Global == 1);
}
assert(g_Global == 2);
}
显然,这个例子被精简和修改以突出问题。
我在godbolt中对比过:
https://godbolt.org/z/55qn8fbYE
Clang从6.0.0版本开始没有任何问题。GCC能够从6.1版开始编译,但无法Assert(包括trunk)。
GCC似乎正在做一些“析构函数省略”,因为在析构函数中更改局部变量应该是无关紧要的。但如果存在某种形式的内存别名(如placement new),那么它就不是了。
那么,我的程序是否依赖于未定义的行为,或者这是GCC中的一个优化错误,应该报告?
1条答案
按热度按时间ikfrs5lh1#
您的代码通过阅读生存期已结束的
int
来调用未定义的行为。来自cppreference(最后一个项目符号):对象的生存期在以下情况下结束:
你正在阅读一个
int
,它已经不复存在了。