admin管理员组

文章数量:1391987

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?

本文标签: cCan stdallocator be used to handle polymorphic objectsStack Overflow