【问题标题】:C++ vector with fixed capacity after initialization初始化后具有固定容量的 C++ 向量
【发布时间】:2018-12-26 13:18:46
【问题描述】:

我需要一个具有以下要求的 C++ 容器:

  • 容器可以在连续内存中存储不可复制和不可移动的对象。对于std::vector,对象必须是可复制的或可移动的。
  • 容器的capacity 在运行时的构建过程中是已知的,并且在销毁之前是固定的。所有需要的内存空间都是在构建过程中分配的。对于boost::static_vector,容量在编译时是已知的。
  • emplace_back容器中有更多元素时,容器的大小会随着时间的推移而增加,但不应超过capacity
  • 由于对象不可复制或移动,因此不允许重新分配。

似乎 STL 和 BOOST 都没有我需要的容器类型。我也在这方面进行了广泛的搜索,但没有找到答案。所以我实现了一个。

#include <memory>

template<class T>
class FixedCapacityVector {
private:
    using StorageType = std::aligned_storage_t<sizeof(T), alignof(T)>;
    static_assert(sizeof(StorageType) == sizeof(T));
public:
    FixedCapacityVector(FixedCapacityVector const&) = delete;
    FixedCapacityVector& operator=(FixedCapacityVector const&) = delete;
    FixedCapacityVector(size_t capacity = 0):
        capacity_{ capacity },
        data_{ std::make_unique<StorageType[]>(capacity) }
    { }
    ~FixedCapacityVector()
    {
        for (size_t i = 0; i < size_; i++)
            reinterpret_cast<T&>(data_[i]).~T();
    }
    template<class... Args>
    T& emplace_back(Args&&... args) 
    {
        if (size_ == capacity_)
            throw std::bad_alloc{};
        new (&data_[size_]) T{ std::forward<Args>(args)... };
        return reinterpret_cast<T&>(data_[size_++]);
    }
    T& operator[](size_t i) 
    { return reinterpret_cast<T&>(data_[i]); }
    T const& operator[](size_t i) const 
    { return reinterpret_cast<T const&>(data_[i]); }
    size_t size() const { return size_; }
    size_t capacity() const { return capacity_; }
    T* data() { return reinterpret_cast<T*>(data_.get()); }
    T const* data() const { return reinterpret_cast<T const*>(data_.get()); }
private:
    size_t const capacity_;
    std::unique_ptr<StorageType[]> const data_;
    size_t size_{ 0 };
};

我的问题是:

  • 我为什么要手动执行此操作?我找不到标准容器。或者,也许我没有看对地方?还是因为我想做的不是传统的?
  • 手写容器是否正确实现?异常安全、内存安全等怎么样?

【问题讨论】:

  • “对于这种常用的容器类型...”是不正确的。您的不可复制且不可移动的对象可以存储在std::shared_ptr 中,然后将std::shared_ptr 存储在std::vector 中吗?
  • @Eljay 感谢您的评论。然后对象不在连续内存中。实际上这就是程序现在正在做的事情(使用std::vector&lt;std::unique_ptr&gt;)。或许我不应该说“这么常用的容器类型”。
  • 不可复制和不可移动的对象是否需要在连续内存中?
  • 为什么不简单地包装一个std::vector,将其调整为所需的最大值,并跟踪实际使用了多少元素?如果该向量是您的类的私有成员,则无论您的类的用户做什么,您都可以确保它在初始化后永远不会调整大小。毕竟,即使用户向您的类请求emplace_back() 某些东西,您的类(或其成员函数的实现)也不需要将其转换为向量的emplace_back() 的调用。
  • @Peter 实际上这是我想到的第一件事。但是如果T 既不可复制又不可移动,std::vector&lt;T&gt; 将无法编译。

标签: c++ vector


【解决方案1】:

它可能没有完全回答这个问题,但根据以下论文,似乎 fixed_capacity_vector 可能会被添加到未来的 C++ 标准中:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0843r1.html

简介

本文提出了 boost::container::static_vector [1] 的现代化版本。也就是说,具有编译时固定容量和连续嵌入式存储的可动态调整大小的向量,其中元素存储在向量对象本身中。

它的 API 与 std::vector 非常相似。它是一个连续容器,在末尾插入和删除元素 O(1)(非摊销),否则在最坏情况下插入和删除 O(size())。与 std::vector 一样,元素在插入时初始化,在移除时销毁。对于平凡的 value_types,向量在 constexpr 函数中完全可用。

【讨论】:

  • 点评来源: 嗨,虽然链接是分享知识的好方法,但如果它们将来被破坏,它们将不会真正回答问题。将回答问题的链接的基本内容添加到您的答案中。如果内容太复杂或太大而无法在此处放置,请描述所提出解决方案的总体思路。请记住始终保留对原始解决方案网站的链接引用。见:How do I write a good answer?
【解决方案2】:

我为什么要手动执行此操作?我找不到标准容器。或者,也许我没有看对地方?还是因为我想做的不是传统的?

这不是传统的。按照惯例,MoveConstructible 的东西也是 MoveAssignable

手写容器是否正确实现?异常安全、内存安全等怎么样?

data 有问题。调用者可能会假设他们可以增加该指针以获取其他元素,但这是严格未定义的。您实际上没有T 的数组。该标准要求在 std::vector 中实现定义的魔法。

【讨论】:

    【解决方案3】:

    我知道这是一个有点旧的帖子并且没有提出确切的要求,但是我们开源了多年来我公司生产代码中使用的 FixedCapacityVector 的实现,可用 here。容量应该是编译时常量。

    它需要 C++11 编译器,但 API 符合 C++17 的std::vector

    【讨论】:

      猜你喜欢
      • 2012-10-18
      • 1970-01-01
      • 2012-08-29
      • 2018-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多