0

如果之前已经回答过这个问题,我深表歉意,我只是无法在最后找到问题的确切答案。

这是一个简化版本的集合。

template <typename T>
struct my_collection
{
    T* buffer_start = nullptr;
    std::size_t capacity;
    std::size_t size;
    T* buffer_end = nullptr;

    // definition of reference, iterators, value_type etc are ommited.

    my_collection(std::size_t capacity) : capacity{ capacity }, buffer_end{ 0 }
    {
        auto memory = ::operator new(capacity * sizeof(T), std::align_val_t{ alignof(T) });
        // I am not sure I am using std::align_val_t correctly here and feel free to correct me, 
        // but I assume that idea is clear
        buffer_start = static_cast<T*>(memory);
        // I think that memory variable can be eliminated, and everything can be in one line
        buffer_end = buffer_start;
    }

    // assume that insertions and removals occur at the end for simplicity and omit move semantics
    // for simplicity and assume copy construction is legal
    void insert(const T& x)
    {
        // overflow check is omitted - assume that size < capacity
        new (buffer_end) T{ x };
        ++size;
        ++buffer_end;
        // buffer_start = std::launder(buffer_start)
    }

    void remove_last()
    {
        // omit checking for empty etc
        --buffer_end;
        --size;
        // Assumption that destructor exists for simplicity - otherwise use destroy_at or implement
        // custom destruction
        std::launder(buffer_start + size)->~T(); 
        // is this even legal or also leads to UB?
        // will these buffer_start = std::launder(buffer_start);
    }

    // const version is omitted
    T& operator[](std::size_t index)
    {
        return *(std::launder(T + index));
        // if I use buffer_start = std::launder(buffer_start) after every insertion, is std::launder still needed?
    }

    ~my_collection()
    {
        while (size > 0) remove_last();
        ::operator delete(static_cast<void*>(buffer_start), capacity*sizeof(T), std::align_val_t{ alignof(T) });
    // is it safe, or there are more unexpected quirks?
    }
};

这是我的问题。(总结评论中出现的问题)。

  1. 分配内存时,分配它并保留T*指针是否足够buffer_start?还是仍然需要原始内存指针?(我没有看到原因,但直到几天前我才知道reintpret_cast<T*>会导致 UB)。
  2. 如果我想访问集合中的元素,我应该使用std::launder(reinterpret_cast<T*>(buffer_start + i)),因为我已经放置了新分配或std::launder(buffer_start)为我解决了这个问题(它甚至有帮助,或者只是没有效果),如果我记得每次删除一个元素时都执行它收藏?(删除元素时甚至需要它吗?)。
  3. delete在析构函数中使用正确吗?
  4. 使用构造元素是否安全,new(buffer_start+size)或者我实际上需要原始内存指针?
4

0 回答 0