I am trying to write a general-purpose RAII class - very similar to a smart pointer but it handles creation as well as destruction. This is the gist of it:
template <typename T>
struct dynamic_type_t {};
template <typename T>
inline constexpr dynamic_type_t<T> dynamic_type {};
template <typename T>
class RAII
{
private:
T* m_ptr;
public:
template <typename DynamicType, typename... Args>
RAII(dynamic_type_t<DynamicType>, Args&&... args) {
m_ptr = new DynamicType{std::forward<Args>(args)...};
}
~RAII() { delete m_ptr; }
};
Now, the test code works flawlessly:
#include <iostream>
struct Base
{
Base() = default;
virtual ~Base() { std::cout << "doo dtor" << '\n'; }
};
struct Derived : public Base
{
long n;
Derived(int x) : Base(), n(x) {}
~Derived() override { std::cout << "widget dtor" << '\n'; }
};
int main () {
RAII<Base> raii{dynamic_type<Derived>, 42};
return 0;
}
The mechanism behind delete
that allows this kind of polymorphic deallocation is discussed in this thread.
However, the reason I am not using smart pointers in the first place is that I need custom allocator support. But I am failing miserably to extend RAII
with this functionality. The straightforward approach:
template <typename T, template <typename> class AllocatorType = std::allocator>
class RAII
{
private:
T* m_ptr;
public:
template <typename DynamicType, typename... Args>
RAII(dynamic_type_t<DynamicType>, Args&&... args) {
using Alloc = AllocatorType<DynamicType>;
using AllocT = std::allocator_traits<Alloc>;
Alloc alloc{};
auto* ptr = AllocT::allocate(alloc, 1);
AllocT::construct(alloc, ptr, std::forward<Args>(args)...);
m_ptr = ptr;
}
~RAII() {
using Alloc = AllocatorType<T>;
using AllocT = std::allocator_traits<Alloc>;
Alloc alloc{};
AllocT::destroy(alloc, m_ptr);
AllocT::deallocate(alloc, m_ptr, 1);
}
};
obviously does not work since allocation and deallocation sizes do not match. If used with the test code from above, ASAN's new-delete-type-mismatch is triggered.
So, my question is: Is it even possible to do this in C++ and how?
In my humble opinion, it would be a big language embarrassment if new
and delete
allowed this but std::allocator
didn't.
NOTE: There is a thread with similar question but it is very old and does not provide any satisfying answers. I do not consider it to be a duplicate.
EDIT
I cannot afford any overhead compared to what would simple approach using new
and delete
incur. Is it possible to replace delete
with std::allocator
without any additional penalties?
IMPORTANT EDIT
I have found that the problem arises because std::allocator
always uses sized version of operator delete
. If one uses ::operator delete(m_ptr)
, things work as expected. This would seem to imply that std::allocator
misses an unsized overload. Does anybody know more?
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744706256a4589073.html
评论列表(0条)